İki veri çerçevesini karşılaştırma ve farklılıkları elde etme


89

İki veri çerçevem ​​var. Örnekler:

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

Her veri çerçevesinin bir dizin olarak Tarihi vardır. Her iki veri çerçevesi de aynı yapıya sahiptir.

Yapmak istediğim, bu iki veri çerçevesini karşılaştırmak ve df2'de df1'de olmayan hangi satırların olduğunu bulmak. Df2 ile df1 arasında olup olmadıklarını görmek için tarihi (dizin) ve ilk sütunu (Muz, APple, vb.) Karşılaştırmak istiyorum.

Aşağıdakileri denedim:

İlk yaklaşım için şu hatayı alıyorum: "İstisna: Yalnızca aynı etiketli DataFrame nesneleri karşılaştırabilir" . Tarihi indeks olarak kaldırmayı denedim ama aynı hatayı alıyorum.

On üçüncü yaklaşımda , ben yanlış dönmek için assert olsun ama aslında farklı satırları görmek anlamaya olamaz.

Herhangi bir işaret memnuniyetle karşılanacaktır


Bunu yaparsanız: cookbook-r.com/Manipulating_data/… , 'aynı etiketli DataFrame nesneleri' istisnasından kurtulacak mı?
Anthony Kong

Şanssız bir şekilde sorunun üstesinden gelmek için sütun adlarını birçok kez değiştirdim.
Eric D. Brown

1
FWIW, her iki veri çerçevesinde sütun adlarını "a, b, c, d" olarak değiştirdim ve aynı hata mesajını alıyorum.
Eric D. Brown

Yanıtlar:


105

Bu yaklaşım, df1 != df2yalnızca aynı satır ve sütunlara sahip veri çerçeveleri için çalışır. Aslında, tüm veri çerçevesi eksenleri yöntemle karşılaştırılır _indexed_sameve sütun / dizin sıralamasında bile farklılıklar bulunursa istisna oluşur.

Sizi doğru anladıysam, değişiklikleri değil, simetrik farkı bulmak istersiniz. Bunun için bir yaklaşım, veri çerçevelerini birleştirmek olabilir:

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

göre grupla

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

benzersiz kayıtların dizinini al

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

filtre

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

Cevap buydu. "Tarih" endeksini kaldırdım ve bu yaklaşımı izledim ve doğru çıktı aldım.
Eric D. Brown

10
Hangi satırların df1'den df2'ye kaldırıldığını / eklendiğini / değiştirildiğini görmek için buna bir bayrak eklemenin kolay bir yolu var mı?
pyCthon

@alko Merak ediyordum, bu pd.concatsadece eksik olanları mı ekliyor df1? Yoksa df1tamamen yerine df2mi geçiyor ?
jake wong

@jakewong pd.concat- burada kullanıldığı gibi - bir dış birleştirme yapar. Başka bir deyişle, her iki df'nin tüm endekslerine katılır ve bu aslında varsayılan davranıştır pd.concat(), işte dokümanlar pandas.pydata.org/pandas-docs/stable/merging.html
Thanos

Pandalar kullanarak karşılaştırabileceğimiz maksimum kayıt sayısı nedir?
2018

25

Veri çerçevelerinin bir sözlükteki concat'e aktarılması, yinelenenleri kolayca silebileceğiniz çok dizinli bir veri çerçevesi ile sonuçlanır; bu, veri çerçeveleri arasındaki farkları içeren çok dizinli bir veri çerçevesi ile sonuçlanır:

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)

Sonuç:

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

1
Bu çok daha kolay bir yöntemdir, sadece bir revizyon daha kolay hale getirebilir. Aynı yapacağını bir sözlüğe, kullanım df = pd.concat ([df1, df2]) içinde concat gerek yok
ling

yerleşik anahtar kelimenin üzerine yazmamalısınız dict!
denfromufa

Hangi veri çerçevesinin benzersiz satırı içerdiğini belirlemenin bir yolu var mı?
jlewkovich

Sözlükteki veri çerçevesinin anahtarını içeren çoklu dizindeki ilk seviyeden anlayabilirsiniz (Çıkışı doğru anahtarlarla güncelledim)
jur

25

Diğerleri bulmak için bir yere daha kolay olacaktır, güncellenmesi ve yerleştirme ling 'üzerine ın yorumunu Jur yukarıda s' tepkisi.

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

Bu DataFrame'lerle test etme:

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

Bunun sonuçları:

# 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'nun benim için neredeyse işe yarayan cevabına dayanarak, filtreleme adımı dışında (aldığım yer:) ValueError: cannot reindex from a duplicate axis, işte kullandığım son çözüm:

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

Cevaba güzel bir ek. Teşekkürler
Eric D. Brown

1
IndexError: index out of bounds'Üçüncü satırı çalıştırmayı denediğimde '' hatasını alıyorum .
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']

bu en iyi cevap
moshevi

3

Daha hızlı ve daha iyi olan daha basit bir çözüm var ve sayılar farklıysa size miktar farklılıkları bile verebilir:

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

Burada df_diff, farklılıkların bir özetidir. Miktarlardaki farklılıkları bulmak için bile kullanabilirsiniz. Örneğinizde:

görüntü açıklamasını buraya girin

Açıklama: İki listeyi karşılaştırmaya benzer şekilde, bunu verimli bir şekilde yapmak için önce sıralayıp sonra karşılaştırmalıyız (listeyi kümelere dönüştürmek de hızlı olacaktır; her ikisi de basit O (N ^ 2) çift karşılaştırma döngüsüne göre inanılmaz bir gelişmedir

Not: Aşağıdaki kod tabloları oluşturur:

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

Kurucu burada basit bir çözüm:

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

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


1
Stack Overflow Tom2shoes'a hoş geldiniz. Lütfen yalnızca bağlantı yanıtları vermeyin, içeriği bağlantıdan çıkarmaya çalışın ve yalnızca referans olarak bırakın (bağlantıdaki içerik silinebileceğinden veya bağlantının kendisi bozulabileceğinden). Daha fazla bilgi için "Nasıl iyi bir cevap yazabilirim?" . Bu sorunun başka bir soruda yanıtlandığını düşünüyorsanız, lütfen tekrar olarak işaretleyin.
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

Yana pandas >= 1.1.0sahip olduğumuz DataFrame.compareve Series.compare.

Not: yöntem yalnızca aynı şekilde etiketlenmiş DataFrame nesnelerini karşılaştırabilir; bu, aynı satır ve sütun etiketlerine sahip DataFrames anlamına gelir.

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

Bu bilgi için teşekkürler. Henüz 1.1'e geçmedim, ancak bunu bilmek güzel.
Eric D. Brown

1

Bu çözümü buldum. Bu sana yardımcı oluyor mu?

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

sonuç

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

Yardım için teşekkürler. Cevabı @ alko ile gördüm ve bu kod iyi çalıştı.
Eric D. Brown

0

Dikkat edilmesi gereken önemli bir ayrıntı, verilerinizin yinelenen dizin değerlerine sahip olmasıdır , bu nedenle herhangi bir basit karşılaştırma yapmak için her şeyi benzersiz hale getirmemiz gerekir df.reset_index()ve bu nedenle koşullara göre seçimler yapabiliriz. Sizin durumunuzda dizin tanımlandığında, tek satırlık bir çözüm olması için dizini tutmak istediğinizi varsayıyorum:

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

Pythonic perspektifinden amaç okunabilirliği artırmak olduğunda, biraz kırabiliriz:

# 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

Umarım bu sizin için yararlı olur. ^ 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

Bu yöntemi denedim ve işe yaradı. Umarım o da yardımcı olabilir:

"""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']]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.