Membandingkan dua kerangka data dan mendapatkan perbedaannya

89

Saya memiliki dua dataframe. Contoh:

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

Setiap kerangka data memiliki Tanggal sebagai indeks. Kedua kerangka data memiliki struktur yang sama.

Apa yang ingin saya lakukan adalah membandingkan kedua dataframe ini dan menemukan baris mana yang ada di df2 yang tidak ada di df1. Saya ingin membandingkan tanggal (indeks) dan kolom pertama (Banana, APple, dll) untuk melihat apakah keduanya ada di df2 vs df1.

Saya telah mencoba yang berikut ini:

Untuk pendekatan pertama saya mendapatkan kesalahan ini: "Pengecualian: Hanya dapat membandingkan objek DataFrame berlabel identik" . Saya telah mencoba menghapus Tanggal sebagai indeks tetapi mendapatkan kesalahan yang sama.

Pada pendekatan ketiga , saya mendapatkan pernyataan untuk mengembalikan False tetapi tidak dapat menemukan cara untuk benar-benar melihat baris yang berbeda.

Setiap petunjuk akan diterima

Eric D. Brown
sumber
Jika Anda melakukan ini: cookbook-r.com/Manipulating_data/… , apakah itu akan menghilangkan pengecualian 'objek DataFrame berlabel identik'?
Anthony Kong
Saya telah mengubah nama kolom berkali-kali untuk mencoba mengatasi masalah ini tanpa hasil.
Eric D. Brown
1
FWIW, saya mengubah nama kolom menjadi "a, b, c, d" pada kedua dataframe dan menerima pesan kesalahan yang sama.
Eric D. Brown

Jawaban:

105

Pendekatan ini,, df1 != df2hanya berfungsi untuk kerangka data dengan baris dan kolom yang identik. Faktanya, semua sumbu kerangka data dibandingkan dengan _indexed_samemetode, dan pengecualian dimunculkan jika ditemukan perbedaan, bahkan dalam urutan kolom / indeks.

Jika saya membuat Anda benar, Anda tidak ingin menemukan perubahan, tetapi perbedaan simetris. Untuk itu, satu pendekatan mungkin menggabungkan kerangka data:

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

dikelompokkan menurut

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

dapatkan indeks catatan unik

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

Saring

>>> df.reindex(idx)
         Date   Fruit   Num   Color
9  2013-11-25  Orange   8.6  Orange
8  2013-11-25   Apple  22.1     Red
alko
sumber
Inilah jawabannya. Saya menghapus indeks "Tanggal" dan mengikuti pendekatan ini dan saya mendapatkan hasil yang benar.
Eric D. Brown
10
Apakah ada cara mudah untuk menambahkan flag ke ini untuk melihat baris mana yang dihapus / ditambahkan / diubah dari df1 ke df2?
pyCthon
@alko Saya bertanya-tanya, apakah ini pd.concatmenambahkan hanya item yang hilang dari df1? Atau apakah itu df1sepenuhnya diganti dengan df2?
jake wong
@jakewong pd.concat- seperti yang digunakan di sini - melakukan gabungan luar. Dengan kata lain, ini menggabungkan semua indeks dari kedua df dan ini sebenarnya adalah perilaku default untuk pd.concat(), inilah docs pandas.pydata.org/pandas-docs/stable/merging.html
Thanos
berapa jumlah rekaman maksimum yang dapat kita bandingkan menggunakan panda?
pyd
25

Meneruskan kerangka data untuk digabungkan dalam kamus, menghasilkan kerangka data multi-indeks yang darinya Anda dapat dengan mudah menghapus duplikat, yang menghasilkan kerangka data multi-indeks dengan perbedaan antara kerangka data:

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)

Hasil:

             Date   Fruit   Num   Color
DF2 4  2013-11-25   Apple  22.1     Red
    5  2013-11-25  Orange   8.6  Orange
jur
sumber
1
Ini adalah metode yang jauh lebih mudah, hanya satu revisi lagi akan membuatnya lebih mudah. Tidak perlu menggabungkan dalam kamus, gunakan df = pd.concat ([df1, df2]) akan melakukan hal yang sama
ling
Anda tidak boleh menimpa kata kunci bawaan dict!
denfromufa
Apakah ada cara untuk menambahkan ini untuk menentukan bingkai data mana yang berisi baris unik?
jlewkovich
Anda dapat mengetahui dengan tingkat pertama dalam multiindex yang berisi kunci dataframe di kamus (saya memperbarui output dengan kunci yang benar)
jur
25

Memperbarui dan menempatkan, di suatu tempat akan lebih mudah ditemukan oleh orang lain, ling komentar atas tanggapan juri di atas.

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

Menguji dengan DataFrames ini:

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

Hasil ini:

# 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
leerssej
sumber
5

Berdasarkan jawaban alko yang hampir berhasil untuk saya, kecuali untuk langkah pemfilteran (di mana saya mendapatkan :) ValueError: cannot reindex from a duplicate axis, berikut adalah solusi terakhir yang saya gunakan:

# 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]
fnl
sumber
Tambahan yang bagus untuk jawabannya. Terima kasih
Eric D. Brown
1
Saya mendapatkan kesalahan, ' IndexError: index out of bounds', ketika saya mencoba menjalankan baris ketiga.
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']
Ivan Moran
sumber
ini adalah jawaban terbaik
moshevi
3

Ada solusi yang lebih sederhana yang lebih cepat dan lebih baik, dan jika jumlahnya berbeda bahkan dapat memberi Anda perbedaan kuantitas:

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

Berikut df_diff adalah sinopsis dari perbedaannya. Anda bahkan dapat menggunakannya untuk mencari perbedaan jumlah. Dalam contoh Anda:

masukkan deskripsi gambar di sini

Penjelasan: Sama halnya dengan membandingkan dua daftar, untuk melakukannya secara efisien kita harus terlebih dahulu memesannya kemudian membandingkannya (mengonversi daftar menjadi set / hashing juga akan cepat; keduanya merupakan peningkatan yang luar biasa pada loop perbandingan ganda O (N ^ 2) sederhana

Catatan: kode berikut menghasilkan tabel:

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

Pendiri solusi sederhana di sini:

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

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

Tom2shoes
sumber
1
Selamat datang di Stack Overflow Tom2shoes. Harap jangan memberikan jawaban hanya tautan, coba ekstrak konten dari tautan dan biarkan hanya sebagai referensi (karena konten dalam tautan dapat dihapus atau tautan itu sendiri dapat rusak). Untuk informasi lebih lanjut, lihat "Bagaimana cara menulis jawaban yang baik?" . Jika Anda yakin pertanyaan ini sudah terjawab di pertanyaan lain, tandai sebagai duplikat.
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
SpeedCoder5
sumber
2

Sejak pandas >= 1.1.0kami memiliki DataFrame.comparedan Series.compare.

Catatan: metode hanya dapat membandingkan objek DataFrame berlabel identik, ini berarti DataFrames dengan label baris dan kolom yang identik.

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
Erfan
sumber
Terimakasih atas informasinya. Saya belum pindah ke 1.1, tapi ini bagus untuk diketahui.
Eric D. Brown
1

Saya mendapat solusi ini. Apakah ini membantu Anda?

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

hasil

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
eyquem
sumber
Terima kasih untuk bantuannya. Saya melihat jawabannya dari @alko dan kode itu bekerja dengan baik.
Eric D. Brown
0

Satu detail penting untuk diperhatikan adalah bahwa data Anda memiliki nilai indeks duplikat , jadi untuk melakukan perbandingan langsung apa pun, kami perlu mengubah semuanya menjadi unik df.reset_index()dan karena itu kami dapat melakukan pemilihan berdasarkan kondisi. Setelah dalam kasus Anda indeks ditentukan, saya berasumsi bahwa Anda ingin menyimpan de index sehingga ada solusi satu baris:

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

Setelah tujuan dari sudut pandang pythonic adalah untuk meningkatkan keterbacaan, kita dapat sedikit merusak:

# 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)
Gandreoti
sumber
0

Semoga bermanfaat bagi Anda. ^ 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
"""
Carson
sumber
0

Saya mencoba metode ini, dan berhasil. Saya harap ini bisa membantu juga:

"""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']]
debugging XD
sumber