การเปรียบเทียบสองดาต้าเฟรมและรับความแตกต่าง


89

ฉันมีสองดาต้าเฟรม ตัวอย่าง:

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

แต่ละดาต้าเฟรมมีวันที่เป็นดัชนี ดาต้าเฟรมทั้งสองมีโครงสร้างเหมือนกัน

สิ่งที่ฉันต้องการทำคือเปรียบเทียบดาต้าเฟรมทั้งสองนี้และค้นหาว่าแถวใดอยู่ใน df2 ที่ไม่ได้อยู่ใน df1 ฉันต้องการเปรียบเทียบวันที่ (ดัชนี) และคอลัมน์แรก (Banana, APple ฯลฯ ) เพื่อดูว่ามีอยู่ใน df2 กับ df1 หรือไม่

ฉันได้ลองทำสิ่งต่อไปนี้แล้ว:

สำหรับวิธีแรกที่ผมได้รับข้อผิดพลาดนี้: "ข้อยกเว้น: วัตถุสามารถเพียงเปรียบเทียบเหมือนป้าย DataFrame" ฉันได้ลองลบวันที่เป็นดัชนีแล้ว แต่ได้รับข้อผิดพลาดเดียวกัน

ในแนวทางที่สามฉันได้รับการยืนยันว่าจะส่งคืน False แต่ไม่สามารถเข้าใจได้ว่าจะดูแถวต่างๆได้อย่างไร

ยินดีต้อนรับคำแนะนำใด ๆ


หากคุณทำเช่นนี้: cookbook-r.com/Manipulating_data/…จะกำจัดข้อยกเว้น "วัตถุ DataFrame ที่มีป้ายกำกับเหมือนกัน" หรือไม่
Anthony Kong

ฉันเปลี่ยนชื่อคอลัมน์หลายครั้งเพื่อพยายามแก้ไขปัญหาโดยไม่มีโชค
Eric

1
FWIW ฉันเปลี่ยนชื่อคอลัมน์เป็น "a, b, c, d" บนดาต้าเฟรมทั้งสองและได้รับข้อความแสดงข้อผิดพลาดเดียวกัน
Eric

คำตอบ:


105

วิธีนี้df1 != df2ใช้ได้เฉพาะกับดาต้าเฟรมที่มีแถวและคอลัมน์เหมือนกัน ในความเป็นจริงแกนดาต้าเฟรมทั้งหมดจะถูกเปรียบเทียบกับ_indexed_sameวิธีการและข้อยกเว้นจะเพิ่มขึ้นหากพบความแตกต่างแม้ในลำดับคอลัมน์ / ดัชนี

ถ้าฉันทำให้คุณถูกต้องคุณไม่ต้องการพบการเปลี่ยนแปลง แต่เป็นความแตกต่างแบบสมมาตร ด้วยเหตุนี้แนวทางหนึ่งอาจเชื่อมต่อกันดาต้าเฟรม:

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

จัดกลุ่มตาม

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

รับดัชนีของระเบียนที่ไม่ซ้ำกัน

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

กรอง

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

นี่คือคำตอบ ฉันลบดัชนี "วันที่" และทำตามวิธีนี้และฉันได้ผลลัพธ์ที่ถูกต้อง
Eric

10
มีวิธีง่ายๆในการเพิ่มแฟล็กเพื่อดูว่าแถวใดถูกลบ / เพิ่ม / เปลี่ยนจาก df1 เป็น df2?
pyCthon

@alko ฉันสงสัยว่านี่pd.concatเป็นการเพิ่มเฉพาะรายการที่หายไปจากdf1? หรือไม่แทนที่df1อย่างสมบูรณ์ด้วยdf2?
jake wong

@jakewong pd.concat- ตามที่ใช้ที่นี่ - ทำการรวมภายนอก กล่าวอีกนัยหนึ่งคือรวมดัชนีทั้งหมดจากทั้ง df และนี่คือพฤติกรรมเริ่มต้นสำหรับpd.concat()นี่คือ docs pandas.pydata.org/pandas-docs/stable/merging.html
Thanos

จำนวนบันทึกสูงสุดที่เราสามารถเปรียบเทียบได้โดยใช้แพนด้าคือเท่าใด
pyd

25

การส่งผ่าน dataframes ไปยัง concat ในพจนานุกรมจะทำให้เกิด dataframe แบบหลายดัชนีซึ่งคุณสามารถลบข้อมูลที่ซ้ำกันได้อย่างง่ายดายซึ่งส่งผลให้ dataframe แบบหลายดัชนีมีความแตกต่างระหว่าง dataframes:

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)

ผลลัพธ์:

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

1
นี่เป็นวิธีที่ง่ายกว่ามากการแก้ไขอีกเพียงครั้งเดียวอาจทำให้ง่ายขึ้น ไม่จำเป็นต้องเรียงต่อกันในพจนานุกรมใช้ df = pd.concat ([df1, df2]) ก็ทำได้เช่นเดียวกัน
ลิง

คุณไม่ควรเขียนทับคำหลักในตัวdict!
denfromufa

มีวิธีเพิ่มเข้าไปเพื่อกำหนดว่า data frame ใดมีแถวที่ไม่ซ้ำกัน
jlewkovich

คุณสามารถบอกได้โดยระดับแรกใน multiindex ที่มีสำคัญของ dataframe ในพจนานุกรมที่ (ฉันปรับปรุงการส่งออกด้วยปุ่มที่ถูกต้อง)
Jur

25

การอัปเดตและวางไว้ที่ไหนสักแห่งจะง่ายขึ้นสำหรับผู้อื่นในการค้นหาความคิดเห็นของลิงตามคำตอบของจูริข้างบน

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

การทดสอบกับ DataFrames เหล่านี้:

# with import pandas as pd

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'],
    })

ผลลัพธ์ในสิ่งนี้:

# for df1

         Date   Fruit   Num   Color
0  2013-11-24  Banana  22.1  Yellow
1  2013-11-24  Orange   8.6  Orange
2  2013-11-24   Apple   7.6   Green
3  2013-11-24  Celery  10.2   Green


# for df2

         Date   Fruit   Num   Color
0  2013-11-24  Banana  22.1  Yellow
1  2013-11-24  Orange   8.6  Orange
2  2013-11-24   Apple   7.6   Green
3  2013-11-24  Celery  10.2   Green
4  2013-11-25   Apple  22.1     Red
5  2013-11-25  Orange   8.6  Orange


# for df_diff

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

5

จากคำตอบของ alko ที่เกือบจะได้ผลสำหรับฉันยกเว้นขั้นตอนการกรอง (ที่ฉันได้รับValueError: cannot reindex from a duplicate axis:) นี่คือทางออกสุดท้ายที่ฉันใช้:

# 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]

นอกจากคำตอบที่ดี ขอบคุณ
Eric

1
ฉันได้รับข้อผิดพลาด ' IndexError: index out of bounds'เมื่อฉันพยายามเรียกใช้บรรทัดที่สาม
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']

นี่คือคำตอบที่ดีที่สุด
moshevi

3

มีวิธีแก้ปัญหาที่ง่ายกว่าเร็วกว่าและดีกว่าและหากตัวเลขแตกต่างกันอาจทำให้คุณมีความแตกต่างด้านปริมาณ:

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_'])

df_diff นี่คือบทสรุปของความแตกต่าง คุณยังสามารถใช้เพื่อค้นหาความแตกต่างของปริมาณ ในตัวอย่างของคุณ:

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

คำอธิบาย: ในทำนองเดียวกันกับการเปรียบเทียบสองรายการเพื่อให้ได้ผลอย่างมีประสิทธิภาพเราควรสั่งซื้อก่อนจากนั้นเปรียบเทียบ (การแปลงรายการเป็นชุด / การแฮชก็จะรวดเร็วเช่นกันทั้งสองเป็นการปรับปรุงอย่างไม่น่าเชื่อสำหรับลูปการเปรียบเทียบคู่ O (N ^ 2) แบบง่าย

หมายเหตุ: รหัสต่อไปนี้สร้างตาราง:

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

ผู้ก่อตั้งโซลูชันง่ายๆที่นี่:

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

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


1
ยินดีต้อนรับสู่ Stack Overflow Tom2shoes โปรดอย่าให้คำตอบแบบลิงก์อย่างเดียวพยายามดึงเนื้อหาจากลิงก์และปล่อยให้เป็นข้อมูลอ้างอิงเท่านั้น (เนื่องจากเนื้อหาในลิงก์สามารถลบได้หรือลิงก์อาจเสียหายได้) สำหรับข้อมูลเพิ่มเติมโปรดดูที่"ฉันจะเขียนคำตอบที่ดีได้อย่างไร" . หากคุณเชื่อว่าคำถามนี้ได้รับคำตอบในคำถามอื่นแล้วโปรดทำเครื่องหมายว่าซ้ำกัน
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

2

เนื่องจากpandas >= 1.1.0เรามีDataFrame.compareและSeries.compare.

หมายเหตุ: วิธีนี้สามารถเปรียบเทียบวัตถุ DataFrame ที่มีป้ายกำกับเหมือนกันเท่านั้นซึ่งหมายถึง DataFrames ที่มีป้ายชื่อแถวและคอลัมน์เหมือนกัน

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

ขอบคุณสำหรับข้อมูล. ฉันยังไม่ได้ย้ายไปที่ 1.1 แต่นี่เป็นสิ่งที่ควรรู้
Eric

1

ฉันได้วิธีนี้ สิ่งนี้ช่วยคุณได้หรือไม่?

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))

ผลลัพธ์

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

ขอบคุณสำหรับความช่วยเหลือ ฉันเห็นคำตอบโดย @alko และรหัสนั้นใช้ได้ดี
Eric

0

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

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

เมื่อวัตถุประสงค์จากมุมมองของ pythonic คือการปรับปรุงความสามารถในการอ่านเราสามารถแบ่งออกเล็กน้อย:

# 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

หวังว่านี่จะเป็นประโยชน์กับคุณ ^ 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

ฉันลองวิธีนี้และได้ผล ฉันหวังว่ามันจะช่วยได้เช่นกัน:

"""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']]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.