ฉันมีเฟรมข้อมูลสองเฟรม df1 และ df2 โดยที่ df2 เป็นเซตย่อยของ df1 ฉันจะรับ data frame ใหม่ (df3) ซึ่งแตกต่างระหว่าง data frame ทั้งสองได้อย่างไร
กล่าวอีกนัยหนึ่งคือ data frame ที่มีแถว / คอลัมน์ทั้งหมดใน df1 ที่ไม่อยู่ใน df2?
คำตอบ:
โดยใช้ 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
pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
float
(เพราะ12.00000000001 != 12
) แนวทางปฏิบัติที่ดีกว่าคือการหาจุดตัดของรหัสในกรอบข้อมูลสองเฟรมและรับความแตกต่างตามนั้น
indicator=True
) เป็นเครื่องมือที่หลากหลายและมีประโยชน์มากฉันอยากเห็นมันอยู่ที่ด้านบนของคำตอบนี้ แต่การเข้าร่วม 'ด้านนอก' ไม่ใช่ 'ด้านซ้าย' จะครอบคลุมทั้ง 3 สถานการณ์
สำหรับแถวให้ลองสิ่งนี้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)
merge
ด้วยindicator=True
เป็นโซลูชันคลาสสิกสำหรับการเปรียบเทียบดาต้าเฟรมตามช่องที่กำหนด
คำตอบที่ได้รับการยอมรับวิธีที่ 1 pd.np.nan != pd.np.nan
จะไม่ทำงานสำหรับเฟรมข้อมูลกับแก่นแก้วภายในเป็น ฉันไม่แน่ใจว่านี่เป็นวิธีที่ดีที่สุดหรือไม่ แต่สามารถหลีกเลี่ยงได้
df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
แก้ไข 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)
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
บางทีอาจเป็นซับเดียวที่ง่ายกว่าโดยมีชื่อคอลัมน์เหมือนกันหรือต่างกัน ทำงานได้แม้ว่า df2 ['Name2'] จะมีค่าที่ซ้ำกัน
newDf = df1.set_index('Name1')
.drop(df2['Name2'], errors='ignore')
.reset_index(drop=False)
การเปลี่ยนแปลงเล็กน้อยของโซลูชันของ nice @ liangli ที่ไม่จำเป็นต้องเปลี่ยนดัชนีของดาต้าเฟรมที่มีอยู่:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
การหาความแตกต่างด้วยดัชนี สมมติว่า 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
นอกเหนือจากคำตอบที่ได้รับการยอมรับแล้วฉันต้องการเสนอโซลูชันที่กว้างขึ้นอีกหนึ่งวิธีที่สามารถค้นหาความแตกต่างของชุดข้อมูล 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
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)