Python Pandas - Tìm sự khác biệt giữa hai khung dữ liệu


99

Tôi có hai khung dữ liệu df1 và df2, trong đó df2 là một tập con của df1. Làm cách nào để lấy khung dữ liệu mới (df3), đây là sự khác biệt giữa hai khung dữ liệu?

Nói cách khác, một khung dữ liệu có tất cả các hàng / cột trong df1 mà không phải trong df2?

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


3
Cách dễ nhất để làm điều này sẽ phụ thuộc vào cách cấu trúc khung dữ liệu của bạn (tức là liệu các chỉ mục có thể được sử dụng hay không, v.v.). Đây là một ví dụ điển hình về lý do tại sao bạn nên đưa một ví dụ có thể tái tạo vào các câu hỏi về gấu trúc.
cmaher

Tôi đã thêm hình ảnh mẫu
khung dữ liệu

Câu trả lời:


154

Bằng cách sử dụng drop_duplicates

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

Update :

Above method only working for those dataframes they do not have duplicate itself, For example

df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
df2=pd.DataFrame({'A':[1],'B':[2]})

Nó sẽ xuất như dưới đây, là sai

Đầu ra sai:

pd.concat([df1, df2]).drop_duplicates(keep=False)
Out[655]: 
   A  B
1  2  3

Đầu ra chính xác

Out[656]: 
   A  B
1  2  3
2  3  4
3  3  4

Làm thế nào để đạt được điều đó?

Phương pháp 1: Sử dụng isinvớituple

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
Out[657]: 
   A  B
1  2  3
2  3  4
3  3  4

Phương pháp 2: mergevớiindicator

df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both']
Out[421]: 
   A  B     _merge
1  2  3  left_only
2  3  4  left_only
3  3  4  left_only

3
Bạn cũng có thể xác định các cột được để được xem xét, khi tìm kiếm các bản sao:pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
Szpaqn

@Szpaqn lưu ý rằng phương pháp này sẽ không xử lý trường hợp đặc biệt. :-)
BEN_YO

Lưu ý rằng điều này có thể khiến các hàng không mong muốn vẫn còn trong kết quả nếu một trong các loại dữ liệu của bạn là float(bởi vì 12.00000000001 != 12). Một phương pháp hay hơn là tìm giao điểm đã đặt của các ID trong hai khung dữ liệu và lấy sự khác biệt dựa trên đó.
Jiāgěng

1
@DtechNet bạn cần tạo hai khung dữ liệu có cùng tên
BEN_YO 16/10 '19

2
Phương pháp 2 ( indicator=True) là một công cụ rất linh hoạt và hữu ích, tôi muốn xem nó ở đầu câu trả lời này, nhưng với phép nối 'bên ngoài' chứ không phải 'bên trái' để bao gồm cả 3 tình huống.
mirekphd

32

Đối với các hàng, hãy thử điều này, đâu Namelà cột chỉ mục chung (có thể là danh sách cho nhiều cột chung hoặc chỉ định left_onright_on):

m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)

Các indicator=Truethiết lập là hữu ích vì nó cho biết thêm một cột gọi là _merge, với tất cả những thay đổi giữa df1df2, phân loại thành 3 loại tốt: "left_only", "right_only" hoặc "cả hai".

Đối với các cột, hãy thử điều này:

set(df1.columns).symmetric_difference(df2.columns)

8
Downvoter quan tâm để bình luận? mergewith indicator=Truelà giải pháp cổ điển để so sánh các khung dữ liệu theo các trường nhất định.
jpp

9

Câu trả lời được chấp nhận Phương pháp 1 sẽ không hoạt động đối với các khung dữ liệu có NaN bên trong, như pd.np.nan != pd.np.nan. Tôi không chắc đây có phải là cách tốt nhất hay không, nhưng nó có thể tránh được bằng cách

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]

6

edit2, tôi đã tìm ra giải pháp mới mà không cần thiết lập chỉ mục

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

Được rồi, tôi đã tìm thấy câu trả lời của phiếu bầu cao nhất đã chứa những gì tôi đã tìm ra. Vâng, chúng tôi chỉ có thể sử dụng mã này với điều kiện không có bản sao trong mỗi hai dfs.


Tôi có một phương pháp phức tạp, đầu tiên chúng ta đặt 'Tên' làm chỉ mục của hai khung dữ liệu mà câu hỏi đưa ra. . Đây là mã.

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)

có thể ý bạn là pd.concat ([df1, df2]). drop_duplicates (keep = False)
Manaslu

4
import pandas as pd
# given
df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',],
    'Age':[23,45,12,34,27,44,28,39,40]})
df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',],
    'Age':[23,12,34,44,28,40]})

# find elements in df1 that are not in df2
df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True)

# output:
print('df1\n', df1)
print('df2\n', df2)
print('df_1notin2\n', df_1notin2)

# df1
#     Age   Name
# 0   23   John
# 1   45   Mike
# 2   12  Smith
# 3   34   Wale
# 4   27  Marry
# 5   44    Tom
# 6   28  Menda
# 7   39   Bolt
# 8   40  Yuswa
# df2
#     Age   Name
# 0   23   John
# 1   12  Smith
# 2   34   Wale
# 3   44    Tom
# 4   28  Menda
# 5   40  Yuswa
# df_1notin2
#     Age   Name
# 0   45   Mike
# 1   27  Marry
# 2   39   Bolt

'~' Nghĩa là gì?
Piotrek Leśniak

'~' không dành cho lập chỉ mục boolean. Xem: pandas.pydata.org/pandas-docs/stable/user_guide/…
SpeedCoder5

3

Có lẽ là một lớp lót đơn giản hơn, với các tên cột giống nhau hoặc khác nhau. Hoạt động ngay cả khi df2 ['Name2'] chứa các giá trị trùng lặp.

newDf = df1.set_index('Name1')
           .drop(df2['Name2'], errors='ignore')
           .reset_index(drop=False)

2
đơn giản và hiệu quả. Đã thêm lỗi = 'bỏ qua' để giải quyết vấn đề trong trường hợp các giá trị đích không có trong nguồn (tức là giao điểm) và việc đặt lại chỉ mục cuối cùng mang lại df tương tự như ban đầu.
MrE

0

Một biến thể nhỏ của giải pháp tốt đẹp @ liangli không yêu cầu thay đổi chỉ mục của các khung dữ liệu hiện có:

newdf = df1.drop(df1.join(df2.set_index('Name').index))

0

Tìm sự khác biệt theo chỉ số. Giả sử df1 là một tập con của df2 và các chỉ mục được chuyển tiếp khi tập hợp con

df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

# Example

df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5])

df2 =  df1.loc[[1,3,5]]

df1

 gender subject
1      f     bio
2      m    chem
3      f     phy
4      m     bio
5      f     bio

df2

  gender subject
1      f     bio
3      f     phy
5      f     bio

df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

df3

  gender subject
2      m    chem
4      m     bio


0

Ngoài câu trả lời được chấp nhận, tôi muốn đề xuất một giải pháp rộng hơn có thể tìm thấy sự khác biệt tập hợp 2D của hai khung dữ liệu với bất kỳ index/ columns(chúng có thể không trùng với cả hai khung dữ liệu). Ngoài ra, phương pháp cho phép thiết lập dung sai cho floatcác phần tử để so sánh khung dữ liệu (nó sử dụng np.isclose)


import numpy as np
import pandas as pd

def get_dataframe_setdiff2d(df_new: pd.DataFrame, 
                            df_old: pd.DataFrame, 
                            rtol=1e-03, atol=1e-05) -> pd.DataFrame:
    """Returns set difference of two pandas DataFrames"""

    union_index = np.union1d(df_new.index, df_old.index)
    union_columns = np.union1d(df_new.columns, df_old.columns)

    new = df_new.reindex(index=union_index, columns=union_columns)
    old = df_old.reindex(index=union_index, columns=union_columns)

    mask_diff = ~np.isclose(new, old, rtol, atol)

    df_bool = pd.DataFrame(mask_diff, union_index, union_columns)

    df_diff = pd.concat([new[df_bool].stack(),
                         old[df_bool].stack()], axis=1)

    df_diff.columns = ["New", "Old"]

    return df_diff

Thí dụ:

In [1]

df1 = pd.DataFrame({'A':[2,1,2],'C':[2,1,2]})
df2 = pd.DataFrame({'A':[1,1],'B':[1,1]})

print("df1:\n", df1, "\n")

print("df2:\n", df2, "\n")

diff = get_dataframe_setdiff2d(df1, df2)

print("diff:\n", diff, "\n")
Out [1]

df1:
   A  C
0  2  2
1  1  1
2  2  2 

df2:
   A  B
0  1  1
1  1  1 

diff:
     New  Old
0 A  2.0  1.0
  B  NaN  1.0
  C  2.0  NaN
1 B  NaN  1.0
  C  1.0  NaN
2 A  2.0  NaN
  C  2.0  NaN 

0

Như đã đề cập ở đây rằng

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]

là giải pháp đúng nhưng nó sẽ tạo ra sai đầu ra nếu

df1=pd.DataFrame({'A':[1],'B':[2]})
df2=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})

Trong trường hợp đó, giải pháp ở trên sẽ cung cấp Empty DataFrame , thay vào đó, bạn nên sử dụng concatphương pháp sau khi loại bỏ các bản sao khỏi mỗi khung dữ liệu.

Sử dụng concate with drop_duplicates

df1=df1.drop_duplicates(keep="first") 
df2=df2.drop_duplicates(keep="first") 
pd.concat([df1,df2]).drop_duplicates(keep=False)
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.