2013-11-26 17 views
24

Ho due frame di dati. Esempi:Confronto di due frame di dati e acquisizione delle differenze

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 

Ogni dataframe ha la data come un indice. Entrambi i dataframe hanno la stessa struttura.

Quello che voglio fare è confrontare questi due dataframes e trovare quali file sono in df2 che non sono in df1. Voglio confrontare la data (indice) e la prima colonna (Banana, APple, ecc.) Per vedere se esistono in df2 vs df1.

Ho provato quanto segue:

Per il primo approccio ottengo questo errore: "Eccezione: può solo confrontare gli oggetti dataframe identicamente-etichettati". Ho provato a rimuovere la data come indice ma ottengo lo stesso errore.

Sul numero third approach, ho ottenuto l'asserzione per restituire False ma non riesco a capire come effettivamente vedere le diverse righe.

Qualsiasi puntatori sarebbe il benvenuto

+0

Se si esegue questa operazione: http://www.cookbook-r.com/Manipulating_data/Renaming_columns_in_a_data_frame/, sarà sbarazzarsi di eccezione gli 'oggetti dataframe identico-etichettati'? –

+0

Ho cambiato i nomi delle colonne molte volte per cercare di aggirare il problema senza fortuna. –

+1

FWIW, ho modificato i nomi delle colonne come "a, b, c, d" su entrambi i frame di dati e ricevere lo stesso messaggio di errore. –

risposta

32

Questo approccio, df1 != df2, funziona solo per dataframes con righe e colonne identiche. Infatti, tutti gli assi dei dataframes vengono confrontati con il metodo _indexed_same e l'eccezione viene sollevata se vengono rilevate differenze, anche nell'ordine di colonne/indici.

Se ti ho capito bene, non vuoi trovare le modifiche, ma la differenza simmetrica.Per questo, un approccio potrebbe essere dataframes concatenare:

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

gruppo di

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

indice get di record univoci

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

filtro

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

Questa era la risposta. Ho rimosso l'indice "Date" e ho seguito questo approccio e ho ottenuto il risultato giusto. –

+2

C'è un modo semplice per aggiungere un flag a questo per vedere quali righe sono state rimosse/aggiunte/modificate da df1 a df2? – pyCthon

+0

@alko Mi stavo chiedendo, questo 'pd.concat' aggiunge solo gli elementi mancanti da' df1'? O sostituisce 'df1' completamente con' df2'? –

1

ho ottenuto questa soluzione. Questo ti aiuta?

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

risultato

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

Grazie per l'aiuto. Ho visto la risposta di @alko e quel codice ha funzionato bene. –

2

Sulla risposta di alko quello ha funzionato quasi per me, tranne che per la fase di filtraggio (dove ottengo: ValueError: cannot reindex from a duplicate axis), ecco la soluzione definitiva che ho usato:

# 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

Bella aggiunta alla risposta. Grazie –

+0

Ricevo l'errore "IndexError: index out of bounds", quando cerco di eseguire la terza riga. – Moondra

10

Passando le dataframes per concat in un dizionario, si traduce in un dataframe multi-indice da cui è può facilmente eliminare i duplicati, che si traduce in un dataframe multi-indice con le differenze tra i 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) 

Risultato:

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

Questo è un metodo molto più semplice, solo una revisione in più può renderlo più facile. Non c'è bisogno di concat in un dizionario, usare df = pd.concat ([df1, df2]) farebbe lo stesso – ling

+0

non si dovrebbe sovrascrivere la parola chiave built-in 'dict'! – denfromufa

0

V'è un semp soluzione ler che è più veloce e migliore, e se i numeri sono diversi possono anche darvi differenze quantità:

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

Ecco df_diff è una sintesi delle differenze. Puoi persino usarlo per trovare le differenze nelle quantità. Nel tuo esempio:

enter image description here

Spiegazione: Allo stesso modo per confrontare due liste, per farlo in modo efficiente dobbiamo prima di loro per poi confrontarli (convertendo l'elenco in insiemi/hashing sarebbe anche veloce, entrambi sono un incredibili miglioramenti alla semplice O (N^2) doppio anello confronto

Nota: il seguente codice produce le tabelle:

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

Un dettaglio importante da notare è che i dati sono valori di indice duplicati, quindi per eseguire un confronto diretto è necessario trasformare tutto come univoco con df.reset_index() e pertanto è possibile eseguire selezioni in base alle condizioni. Una volta che nel tuo caso è definito l'indice, suppongo che si desidera mantenere de indice quindi ci sono una soluzione one-line:

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

Una volta che l'obiettivo dal punto di vista divinatorio è quello di migliorare la leggibilità, siamo in grado di rompere un po ':

# 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) 
Problemi correlati