Python Pandas - ค้นหาความแตกต่างระหว่างสองเฟรมข้อมูล


98

ฉันมีเฟรมข้อมูลสองเฟรม df1 และ df2 โดยที่ df2 เป็นเซตย่อยของ df1 ฉันจะรับ data frame ใหม่ (df3) ซึ่งแตกต่างระหว่าง data frame ทั้งสองได้อย่างไร

กล่าวอีกนัยหนึ่งคือ data frame ที่มีแถว / คอลัมน์ทั้งหมดใน df1 ที่ไม่อยู่ใน df2?

ป้อนคำอธิบายภาพที่นี่


3
วิธีที่ง่ายที่สุดในการดำเนินการนี้จะขึ้นอยู่กับว่าดาต้าเฟรมของคุณมีโครงสร้างอย่างไร (เช่นสามารถใช้ดัชนีได้หรือไม่เป็นต้น) นี่เป็นตัวอย่างที่ดีว่าทำไมคุณควรใส่ตัวอย่างที่ทำซ้ำได้ในคำถามแพนด้า
cmaher

ฉันได้เพิ่มรูปภาพตัวอย่าง
ดาต้าเฟรมแล้ว

คล้ายกับstackoverflow.com/q/20225110
SpeedCoder5

คำตอบ:


152

โดยใช้ 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]})

มันจะออกมาเหมือนด้านล่างซึ่งผิด

ผลลัพธ์ไม่ถูกต้อง:

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

ผลลัพธ์ที่ถูกต้อง

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

จะบรรลุได้อย่างไร?

วิธีที่ 1: ใช้isinกับtuple

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

วิธีที่ 2: mergeด้วยindicator

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
คุณยังสามารถกำหนดได้ว่าจะพิจารณาคอลัมน์ใดเมื่อต้องการค้นหารายการที่ซ้ำกัน:pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
Szpaqn

@Szpaqn สังเกตว่าวิธีนี้จะไม่รองรับกรณีพิเศษ :-)
BEN_YO

โปรดทราบว่าสิ่งนี้อาจทำให้แถวที่ไม่คาดคิดยังคงอยู่ในผลลัพธ์หากประเภทข้อมูลของคุณเป็นfloat(เพราะ12.00000000001 != 12) แนวทางปฏิบัติที่ดีกว่าคือการหาจุดตัดของรหัสในกรอบข้อมูลสองเฟรมและรับความแตกต่างตามนั้น
Jiāgěng

1
@DtechNet คุณต้องทำให้ data frame สองอันมีชื่อเดียวกัน
BEN_YO

2
วิธีที่ 2 ( indicator=True) เป็นเครื่องมือที่หลากหลายและมีประโยชน์มากฉันอยากเห็นมันอยู่ที่ด้านบนของคำตอบนี้ แต่การเข้าร่วม 'ด้านนอก' ไม่ใช่ 'ด้านซ้าย' จะครอบคลุมทั้ง 3 สถานการณ์
mirekphd

32

สำหรับแถวให้ลองสิ่งนี้Nameคอลัมน์ดัชนีร่วมอยู่ที่ไหน(อาจเป็นรายการสำหรับคอลัมน์ทั่วไปหลายคอลัมน์หรือระบุleft_onและright_on):

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

การindicator=Trueตั้งค่านี้มีประโยชน์เมื่อเพิ่มคอลัมน์ที่เรียกว่า_mergeการเปลี่ยนแปลงทั้งหมดระหว่างdf1และdf2แบ่งออกเป็น 3 ประเภทที่เป็นไปได้: "left_only", "right_only" หรือ "both"

สำหรับคอลัมน์ให้ลองทำดังนี้

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

8
Downvoter สนใจที่จะแสดงความคิดเห็น? mergeด้วยindicator=Trueเป็นโซลูชันคลาสสิกสำหรับการเปรียบเทียบดาต้าเฟรมตามช่องที่กำหนด
jpp

9

คำตอบที่ได้รับการยอมรับวิธีที่ 1 pd.np.nan != pd.np.nanจะไม่ทำงานสำหรับเฟรมข้อมูลกับแก่นแก้วภายในเป็น ฉันไม่แน่ใจว่านี่เป็นวิธีที่ดีที่สุดหรือไม่ แต่สามารถหลีกเลี่ยงได้

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

6

แก้ไข 2 ฉันพบโซลูชันใหม่โดยไม่ต้องใช้ดัชนีการตั้งค่า

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

โอเคฉันพบว่าคำตอบของการโหวตที่สูงที่สุดมีสิ่งที่ฉันคิดไว้แล้วใช่เราสามารถใช้รหัสนี้โดยมีเงื่อนไขว่าไม่มีข้อมูลซ้ำกันในแต่ละ df สองรายการ


ฉันมีวิธีที่ยุ่งยากก่อนอื่นเราตั้งค่า 'ชื่อ' เป็นดัชนีของสองดาต้าเฟรมที่กำหนดโดยคำถามเนื่องจากเรามี 'ชื่อ' เหมือนกันในสอง dfs เราจึงสามารถวางดัชนี df ที่ 'เล็กลง' จาก df 'ใหญ่กว่า' ได้ . นี่คือรหัส

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

คุณอาจหมายถึง 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

'~' หมายถึงอะไร?
Piotrek Leśniak

'~' ไม่ใช่สำหรับการทำดัชนีบูลีน ดู: pandas.pydata.org/pandas-docs/stable/user_guide/…
SpeedCoder5

3

บางทีอาจเป็นซับเดียวที่ง่ายกว่าโดยมีชื่อคอลัมน์เหมือนกันหรือต่างกัน ทำงานได้แม้ว่า df2 ['Name2'] จะมีค่าที่ซ้ำกัน

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

2
ง่ายและมีประสิทธิภาพ เพิ่มข้อผิดพลาด = 'ละเว้น' เพื่อแก้ไขปัญหาสำหรับกรณีที่ค่าปลายทางไม่อยู่ในต้นทาง (เช่นจุดตัด) และการรีเซ็ตดัชนีในท้ายที่สุดจะทำให้ได้ df ที่ใกล้เคียงกับต้นฉบับ
MrE

0

การเปลี่ยนแปลงเล็กน้อยของโซลูชันของ nice @ liangli ที่ไม่จำเป็นต้องเปลี่ยนดัชนีของดาต้าเฟรมที่มีอยู่:

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

0

การหาความแตกต่างด้วยดัชนี สมมติว่า df1 เป็นเซ็ตย่อยของ df2 และดัชนีจะถูกยกไปข้างหน้าเมื่อเซตย่อย

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

นอกเหนือจากคำตอบที่ได้รับการยอมรับแล้วฉันต้องการเสนอโซลูชันที่กว้างขึ้นอีกหนึ่งวิธีที่สามารถค้นหาความแตกต่างของชุดข้อมูล 2Dของสองดาต้าเฟรมใด ๆ ที่มีindex/ columns(อาจไม่ตรงกันสำหรับทั้งสองดาต้าเฟรม) นอกจากนี้วิธีการยังอนุญาตให้ตั้งค่าความอดทนสำหรับfloatองค์ประกอบสำหรับการเปรียบเทียบ dataframe (ใช้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

ตัวอย่าง:

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

ดังที่กล่าวมานี้ ว่า

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

เป็นวิธีการแก้ปัญหาที่ถูกต้อง แต่จะให้ผลลัพธ์ที่ไม่ถูกต้องหาก

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

ในกรณีข้างต้นโซลูชันจะให้ DataFrame ว่างเปล่าคุณควรใช้concatวิธีการหลังจากลบรายการที่ซ้ำกันออกจากแต่ละ datframe

ใช้ concate with drop_duplicates

df1=df1.drop_duplicates(keep="first") 
df2=df2.drop_duplicates(keep="first") 
pd.concat([df1,df2]).drop_duplicates(keep=False)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.