So sánh hai khung dữ liệu và nhận được sự khác biệt


89

Tôi có hai khung dữ liệu. Ví dụ:

df1:
Date       Fruit  Num  Color 
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange  8.6 Orange
2013-11-24 Apple   7.6 Green
2013-11-24 Celery 10.2 Green

df2:
Date       Fruit  Num  Color 
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange  8.6 Orange
2013-11-24 Apple   7.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Apple  22.1 Red
2013-11-25 Orange  8.6 Orange

Mỗi khung dữ liệu có Ngày làm chỉ mục. Cả hai khung dữ liệu đều có cấu trúc giống nhau.

Điều tôi muốn làm là so sánh hai khung dữ liệu này và tìm hàng nào nằm trong df2 mà không nằm trong df1. Tôi muốn so sánh ngày (chỉ mục) và cột đầu tiên (Banana, APple, v.v.) để xem liệu chúng có tồn tại trong df2 vs df1 hay không.

Tôi đã thử những cách sau:

Đối với cách tiếp cận đầu tiên, tôi gặp lỗi này: "Ngoại lệ: Chỉ có thể so sánh các đối tượng DataFrame được gắn nhãn giống hệt nhau" . Tôi đã thử xóa Ngày dưới dạng chỉ mục nhưng gặp lỗi tương tự.

cách tiếp cận thứ ba , tôi nhận được khẳng định trả về False nhưng không thể tìm ra cách thực sự nhìn thấy các hàng khác nhau.

Mọi người sẽ được chào đón


Nếu bạn làm điều này: cookbook-r.com/Manipulation_data/… , nó có loại bỏ được ngoại lệ 'đối tượng DataFrame được gắn nhãn giống hệt nhau' không?
Anthony Kong

Tôi đã thay đổi tên cột nhiều lần để cố gắng giải quyết vấn đề mà không gặp may.
Eric D. Brown

1
FWIW, tôi đã đổi tên cột thành "a, b, c, d" trên cả hai khung dữ liệu và nhận được cùng một thông báo lỗi.
Eric D. Brown

Câu trả lời:


103

Cách tiếp cận này df1 != df2chỉ hoạt động với các khung dữ liệu có các hàng và cột giống hệt nhau. Trên thực tế, tất cả các trục khung dữ liệu đều được so sánh với _indexed_samephương thức và ngoại lệ được đưa ra nếu tìm thấy sự khác biệt, ngay cả trong thứ tự cột / chỉ số.

Nếu tôi hiểu bạn đúng, bạn không muốn tìm thấy những thay đổi, mà là sự khác biệt đối xứng. Đối với điều đó, một cách tiếp cận có thể là các khung dữ liệu ghép nối:

>>> df = pd.concat([df1, df2])
>>> df = df.reset_index(drop=True)

nhóm bởi

>>> df_gpby = df.groupby(list(df.columns))

lấy chỉ mục của các bản ghi duy nhất

>>> idx = [x[0] for x in df_gpby.groups.values() if len(x) == 1]

bộ lọc

>>> df.reindex(idx)
         Date   Fruit   Num   Color
9  2013-11-25  Orange   8.6  Orange
8  2013-11-25   Apple  22.1     Red

Đây là câu trả lời. Tôi đã xóa chỉ mục "Ngày" và làm theo cách tiếp cận này và tôi nhận được kết quả phù hợp.
Eric D. Brown

9
Có cách nào dễ dàng để thêm cờ vào mục này để xem những hàng nào đã bị xóa / thêm / thay đổi từ df1 thành df2 không?
pyCthon

@alko Tôi đã tự hỏi, điều này chỉ pd.concatthêm vào các mục bị thiếu từ df1? Hay nó thay thế df1hoàn toàn bằng df2?
jake wong

@jakewong pd.concat- như được sử dụng ở đây - thực hiện một phép nối bên ngoài. Nói cách khác, nó tham gia vào tất cả các chỉ số của cả hai df và điều này là trong thực tế hành vi mặc định cho pd.concat(), đây là tài liệu pandas.pydata.org/pandas-docs/stable/merging.html
Thanos

số lượng bản ghi tối đa mà chúng ta có thể so sánh bằng cách sử dụng gấu trúc là bao nhiêu?
pyd,

25

Chuyển các khung dữ liệu để ghép trong một từ điển, dẫn đến một khung dữ liệu nhiều chỉ mục mà từ đó bạn có thể dễ dàng xóa các bản sao, dẫn đến một khung dữ liệu nhiều chỉ mục với sự khác biệt giữa các khung dữ liệu:

import sys
if sys.version_info[0] < 3:
    from StringIO import StringIO
else:
    from io import StringIO
import pandas as pd

DF1 = StringIO("""Date       Fruit  Num  Color 
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange  8.6 Orange
2013-11-24 Apple   7.6 Green
2013-11-24 Celery 10.2 Green
""")
DF2 = StringIO("""Date       Fruit  Num  Color 
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange  8.6 Orange
2013-11-24 Apple   7.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Apple  22.1 Red
2013-11-25 Orange  8.6 Orange""")


df1 = pd.read_table(DF1, sep='\s+')
df2 = pd.read_table(DF2, sep='\s+')
#%%
dfs_dictionary = {'DF1':df1,'DF2':df2}
df=pd.concat(dfs_dictionary)
df.drop_duplicates(keep=False)

Kết quả:

             Date   Fruit   Num   Color
DF2 4  2013-11-25   Apple  22.1     Red
    5  2013-11-25  Orange   8.6  Orange

1
Đây là một phương pháp dễ dàng hơn nhiều, chỉ cần một lần sửa đổi nữa có thể làm cho nó dễ dàng hơn. Không cần phải concat trong một cuốn từ điển, sử dụng df = pd.concat ([df1, df2]) sẽ làm như vậy
ling

bạn không nên ghi đè từ khóa có sẵn dict!
denfromufa 23/07/17

Có cách nào để thêm vào điều này để xác định khung dữ liệu nào chứa hàng duy nhất không?
jlewkovich

Bạn có thể biết được mức độ đầu tiên trong multiindex trong đó có chìa khóa của dataframe trong từ điển (Tôi cập nhật sản lượng với các phím đúng)
jur

24

Cập nhật và đặt, đâu đó nó sẽ được dễ dàng hơn cho những người khác để tìm, ling 'bình luận s khi jur ' s phản ứng trên.

df_diff = pd.concat([df1,df2]).drop_duplicates(keep=False)

Thử nghiệm với các khung dữ liệu này:

df1=pd.DataFrame({
    'Date':['2013-11-24','2013-11-24','2013-11-24','2013-11-24'],
    'Fruit':['Banana','Orange','Apple','Celery'],
    'Num':[22.1,8.6,7.6,10.2],
    'Color':['Yellow','Orange','Green','Green'],
})

df2=pd.DataFrame({
    'Date':['2013-11-24','2013-11-24','2013-11-24','2013-11-24','2013-11-25','2013-11-25'],
    'Fruit':['Banana','Orange','Apple','Celery','Apple','Orange'],
    'Num':[22.1,8.6,7.6,10.2,22.1,8.6],
    'Color':['Yellow','Orange','Green','Green','Red','Orange'],
})

Kết quả là: nhập mô tả hình ảnh ở đây


5

Dựa trên câu trả lời của alko gần như hiệu quả với tôi, ngoại trừ bước lọc (nơi tôi nhận được ValueError: cannot reindex from a duplicate axis:), đây là giải pháp cuối cùng tôi đã sử dụng:

# join the dataframes
united_data = pd.concat([data1, data2, data3, ...])
# group the data by the whole row to find duplicates
united_data_grouped = united_data.groupby(list(united_data.columns))
# detect the row indices of unique rows
uniq_data_idx = [x[0] for x in united_data_grouped.indices.values() if len(x) == 1]
# extract those unique values
uniq_data = united_data.iloc[uniq_data_idx]

Tốt đẹp bổ sung cho câu trả lời. Cảm ơn
Eric D. Brown

1
Tôi đang gặp lỗi, ' IndexError: index out of bounds', khi tôi cố gắng chạy dòng thứ ba.
Moondra

5
# THIS WORK FOR ME

# Get all diferent values
df3 = pd.merge(df1, df2, how='outer', indicator='Exist')
df3 = df3.loc[df3['Exist'] != 'both']


# If you like to filter by a common ID
df3  = pd.merge(df1, df2, on="Fruit", how='outer', indicator='Exist')
df3  = df3.loc[df3['Exist'] != 'both']

đây là câu trả lời hay nhất
moshevi

3

Có một giải pháp đơn giản hơn, nhanh hơn và tốt hơn, và nếu các con số khác nhau thậm chí có thể cung cấp cho bạn sự khác biệt về số lượng:

df1_i = df1.set_index(['Date','Fruit','Color'])
df2_i = df2.set_index(['Date','Fruit','Color'])
df_diff = df1_i.join(df2_i,how='outer',rsuffix='_').fillna(0)
df_diff = (df_diff['Num'] - df_diff['Num_'])

Ở đây df_diff là bản tóm tắt về sự khác biệt. Bạn thậm chí có thể sử dụng nó để tìm sự khác biệt về số lượng. Trong ví dụ của bạn:

nhập mô tả hình ảnh ở đây

Giải thích: Tương tự như so sánh hai danh sách, để làm điều đó hiệu quả, trước tiên chúng ta nên sắp xếp thứ tự chúng sau đó so sánh chúng (chuyển đổi danh sách thành bộ / băm cũng sẽ nhanh chóng; cả hai đều là một cải tiến đáng kinh ngạc đối với vòng lặp so sánh kép O (N ^ 2) đơn giản

Lưu ý: đoạn mã sau tạo ra các bảng:

df1=pd.DataFrame({
    'Date':['2013-11-24','2013-11-24','2013-11-24','2013-11-24'],
    'Fruit':['Banana','Orange','Apple','Celery'],
    'Num':[22.1,8.6,7.6,10.2],
    'Color':['Yellow','Orange','Green','Green'],
})
df2=pd.DataFrame({
    'Date':['2013-11-24','2013-11-24','2013-11-24','2013-11-24','2013-11-25','2013-11-25'],
    'Fruit':['Banana','Orange','Apple','Celery','Apple','Orange'],
    'Num':[22.1,8.6,7.6,10.2,22.1,8.6],
    'Color':['Yellow','Orange','Green','Green','Red','Orange'],
})

3

Người sáng lập một giải pháp đơn giản ở đây:

https://stackoverflow.com/a/47132808/9656339

pd.concat([df1, df2]).loc[df1.index.symmetric_difference(df2.index)]


1
Chào mừng bạn đến với Stack Overflow Tom2shoes. Vui lòng không cung cấp câu trả lời chỉ có liên kết, hãy cố gắng trích xuất nội dung từ liên kết và chỉ để nó làm tài liệu tham khảo (vì nội dung trong liên kết có thể bị xóa hoặc bản thân liên kết có thể bị hỏng). Để biết thêm thông tin, hãy tham khảo "Làm thế nào để viết một câu trả lời tốt?" . Nếu bạn tin rằng câu hỏi này đã được trả lời trong một câu hỏi khác, vui lòng đánh dấu nó là một bản sao.
GGG

2
# given
df1=pd.DataFrame({'Date':['2013-11-24','2013-11-24','2013-11-24','2013-11-24'],
    'Fruit':['Banana','Orange','Apple','Celery'],
    'Num':[22.1,8.6,7.6,10.2],
    'Color':['Yellow','Orange','Green','Green']})
df2=pd.DataFrame({'Date':['2013-11-24','2013-11-24','2013-11-24','2013-11-24','2013-11-25','2013-11-25'],
    'Fruit':['Banana','Orange','Apple','Celery','Apple','Orange'],
    'Num':[22.1,8.6,7.6,1000,22.1,8.6],
    'Color':['Yellow','Orange','Green','Green','Red','Orange']})

# find which rows are in df2 that aren't in df1 by Date and Fruit
df_2notin1 = df2[~(df2['Date'].isin(df1['Date']) & df2['Fruit'].isin(df1['Fruit']) )].dropna().reset_index(drop=True)

# output
print('df_2notin1\n', df_2notin1)
#      Color        Date   Fruit   Num
# 0     Red  2013-11-25   Apple  22.1
# 1  Orange  2013-11-25  Orange   8.6

1

Tôi có giải pháp này. Điều này có giúp bạn?

text = """df1:
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green

df2:
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange



argetz45
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 118.6 Orange
2013-11-24 Apple 74.6 Green
2013-11-24 Celery 10.2 Green
2013-11-25     Nuts    45.8 Brown
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange
2013-11-26   Pear 102.54    Pale"""

.

from collections import OrderedDict
import re

r = re.compile('([a-zA-Z\d]+).*\n'
               '(20\d\d-[01]\d-[0123]\d.+\n?'
               '(.+\n?)*)'
               '(?=[ \n]*\Z'
                  '|'
                  '\n+[a-zA-Z\d]+.*\n'
                  '20\d\d-[01]\d-[0123]\d)')

r2 = re.compile('((20\d\d-[01]\d-[0123]\d) +([^\d.]+)(?<! )[^\n]+)')

d = OrderedDict()
bef = []

for m in r.finditer(text):
    li = []
    for x in r2.findall(m.group(2)):
        if not any(x[1:3]==elbef for elbef in bef):
            bef.append(x[1:3])
            li.append(x[0])
    d[m.group(1)] = li


for name,lu in d.iteritems():
    print '%s\n%s\n' % (name,'\n'.join(lu))

kết quả

df1
2013-11-24 Banana 22.1 Yellow
2013-11-24 Orange 8.6 Orange
2013-11-24 Apple 7.6 Green
2013-11-24 Celery 10.2 Green

df2
2013-11-25 Apple 22.1 Red
2013-11-25 Orange 8.6 Orange

argetz45
2013-11-25     Nuts    45.8 Brown
2013-11-26   Pear 102.54    Pale

Cảm ơn đã giúp đỡ. Tôi đã thấy câu trả lời của @alko và mã đó hoạt động tốt.
Eric D. Brown

1

Kể từ khi pandas >= 1.1.0chúng tôi có DataFrame.compareSeries.compare.

Lưu ý: phương pháp chỉ có thể so sánh các đối tượng DataFrame được gắn nhãn giống hệt nhau, điều này có nghĩa là DataFrame có nhãn hàng và cột giống hệt nhau.

df1 = pd.DataFrame({'A': [1, 2, 3],
                    'B': [4, 5, 6],
                    'C': [7, np.NaN, 9]})

df2 = pd.DataFrame({'A': [1, 99, 3],
                    'B': [4, 5, 81],
                    'C': [7, 8, 9]})

   A  B    C
0  1  4  7.0
1  2  5  NaN
2  3  6  9.0 

    A   B  C
0   1   4  7
1  99   5  8
2   3  81  9
df1.compare(df2)

     A          B          C      
  self other self other self other
1  2.0  99.0  NaN   NaN  NaN   8.0
2  NaN   NaN  6.0  81.0  NaN   NaN

Cảm ơn bạn cho thông tin này. Tôi vẫn chưa chuyển sang 1.1, nhưng điều này là tốt để biết.
Eric D. Brown,

0

Một chi tiết quan trọng cần lưu ý là dữ liệu của bạn có các giá trị chỉ mục trùng lặp , vì vậy để thực hiện bất kỳ so sánh đơn giản nào, chúng tôi cần biến mọi thứ thành duy nhất df.reset_index()và do đó chúng tôi có thể thực hiện các lựa chọn dựa trên các điều kiện. Khi trong trường hợp của bạn chỉ mục được xác định, tôi giả sử rằng bạn muốn giữ lại chỉ mục để có giải pháp một dòng:

[~df2.reset_index().isin(df1.reset_index())].dropna().set_index('Date')

Một khi mục tiêu từ góc độ pythonic là cải thiện khả năng đọc, chúng ta có thể phá vỡ một chút:

# keep the index name, if it does not have a name it uses the default name
index_name = df.index.name if df.index.name else 'index' 

# setting the index to become unique
df1 = df1.reset_index()
df2 = df2.reset_index()

# getting the differences to a Dataframe
df_diff = df2[~df2.isin(df1)].dropna().set_index(index_name)

0

Hy vọng điều này sẽ hữu ích cho bạn. ^ o ^

df1 = pd.DataFrame({'date': ['0207', '0207'], 'col1': [1, 2]})
df2 = pd.DataFrame({'date': ['0207', '0207', '0208', '0208'], 'col1': [1, 2, 3, 4]})
print(f"df1(Before):\n{df1}\ndf2:\n{df2}")
"""
df1(Before):
   date  col1
0  0207     1
1  0207     2

df2:
   date  col1
0  0207     1
1  0207     2
2  0208     3
3  0208     4
"""

old_set = set(df1.index.values)
new_set = set(df2.index.values)
new_data_index = new_set - old_set
new_data_list = []
for idx in new_data_index:
    new_data_list.append(df2.loc[idx])

if len(new_data_list) > 0:
    df1 = df1.append(new_data_list)
print(f"df1(After):\n{df1}")
"""
df1(After):
   date  col1
0  0207     1
1  0207     2
2  0208     3
3  0208     4
"""

0

Tôi đã thử phương pháp này, và nó đã hoạt động. Tôi hy vọng nó cũng có thể giúp:

"""Identify differences between two pandas DataFrames"""
df1.sort_index(inplace=True)
df2.sort_index(inplace=True)
df_all = pd.concat([df1, df12], axis='columns', keys=['First', 'Second'])
df_final = df_all.swaplevel(axis='columns')[df1.columns[1:]]
df_final[df_final['change this to one of the columns'] != df_final['change this to one of the columns']]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.