2013-11-26 26 views
24

Mam dwie ramki danych. Przykłady:Porównywanie dwóch ramek danych i uzyskiwanie różnic

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 

Każda ramka danych ma datę jako indeks. Obie ramki danych mają taką samą strukturę.

Co chcę zrobić, to porównać te dwie ramki danych i znaleźć, które wiersze są w df2, które nie są w df1. Chcę porównać datę (indeks) i pierwszą kolumnę (Banana, APple, itp.), Aby sprawdzić, czy istnieją w df2 vs df1.

Próbowałem następujące:

Na pierwszym podejściu otrzymuję ten błąd: "wyjątek mogę porównać tylko identycznie oznakowane obiekty DataFrame". Próbowałem usunąć Date jako indeks, ale otrzymałem ten sam błąd.

Na third approach otrzymuję potwierdzenie, że zwraca Fałsz, ale nie może dowiedzieć się, jak wyświetlić różne wiersze.

Wszelkie wskazówki byłyby mile widziane.

+0

Jeśli to zrobić: http://www.cookbook-r.com/Manipulating_data/Renaming_columns_in_a_data_frame/ będzie pozbyć wyjątku „identycznie znakowanych OBIEKTÓW DataFrame”? –

+0

Zmieniłem wiele nazw kolumn, aby spróbować obejść problem bez powodzenia. –

+1

FWIW, zmieniłem nazwy kolumn na "a, b, c, d" na obu ramkach danych i otrzymam ten sam komunikat o błędzie. –

Odpowiedz

32

Takie podejście, df1 != df2, działa tylko dla dataframes o identycznych wierszy i kolumn. W rzeczywistości wszystkie osie siatki danych są porównywane z metodą _indexed_same, a wyjątek jest wywoływany, jeśli znaleziono różnice, nawet w kolejności kolumn/indeksów.

Jeśli masz rację, nie chcesz znaleźć zmian, ale symetryczną różnicę.Za to jedno podejście może być oddzielając dataframes:

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

grupa autorstwa

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

Indeks get unikalnych zapisów

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

filtr

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

To była odpowiedź. Usunąłem wskaźnik "Date" i zastosowałem to podejście, a otrzymałem prawidłowy wynik. –

+2

Czy istnieje prosty sposób dodania do tego flagi, aby zobaczyć, które wiersze zostały usunięte/dodane/zmienione z df1 do df2? – pyCthon

+0

@alko Zastanawiam się, czy to 'pd.concat' dodaje tylko brakujące elementy z' df1'? Czy też zastępuje 'df1' całkowicie' df2'? –

1

Mam to rozwiązanie. Czy to ci pomoże?

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

wynik

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 
+0

Dzięki za pomoc. Widziałam odpowiedź @alko i ten kod działa dobrze. –

2

Opierając się na odpowiedź Alko za to prawie działało dla mnie, z wyjątkiem etapu filtrowania (gdzie uzyskać: ValueError: cannot reindex from a duplicate axis), tutaj jest ostateczne rozwiązanie użyłem:

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

Niezły dodatek do odpowiedzi. Dzięki –

+0

Otrzymuję błąd "IndexError: index out of bounds'', gdy próbuję uruchomić trzecią linię. – Moondra

10

Mijając dataframes do Concat w słowniku, skutkuje dataframe multi-index, z którego można łatwo usunąć duplikaty, co skutkuje dataframe multi-index z różnic między 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) 

Wynik:

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

Jest to znacznie łatwiejsza metoda, tylko jedna kolejna wersja może ułatwić to. Nie musisz konkatować się w słowniku, użyj df = pd.concat ([df1, df2]) zrobiłby to samo – ling

+0

nie powinieneś nadpisywać wbudowanego słowa kluczowego 'dict'! – denfromufa

0

jest SIMP Rozwiązanie ler to szybciej i lepiej, a jeśli numery są różne może nawet dać różnice ilości:

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

Tutaj df_diff jest streszczeniem różnic. Możesz go nawet użyć, aby znaleźć różnice w ilościach. W przykładzie:

enter image description here

Objaśnienie: Podobnie porównując dwie listy, aby zrobić to skutecznie trzeba najpierw zamówić je następnie porównać je (konwersja listy do zestawów/mieszania byłoby również szybko; oba są niesamowita poprawa do prostego o (n^2) porównanie podwójna pętla

UWAGA: Poniższy kod tworzy tabele:

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

jeden ważny szczegół, aby zauważyć, jest to, że dane zostały zduplikowane wartości indeksu, więc aby wykonać proste porównanie, musimy zmienić wszystko tak, aby było unikalne z df.reset_index() i dlatego możemy dokonywać selekcji na podstawie warunków. Raz w przypadku gdy indeks jest zdefiniowany, zakładam, że chcesz zachować de indeksu więc istnieje rozwiązanie jedna linia:

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

raz cel z pythonic perspektywy jest poprawa czytelności możemy złamać trochę:

# 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) 
Powiązane problemy