it-swarm.com.de

Zwischen zwei Datenrahmen in Pandas unterscheiden

Ich habe zwei Datenrahmen, die beide das gleiche Grundschema haben. (4 Datumsfelder, ein paar Zeichenkettenfelder und 4-5 Gleitkommafelder). Nennen Sie sie df1 und df2.

Was ich tun möchte, ist im Grunde genommen ein "Diff" der beiden zu erhalten - wobei ich alle Zeilen zurückerhalte, die nicht zwischen den beiden Datenrahmen geteilt werden (nicht in der eingestellten Schnittmenge). Beachten Sie, dass die beiden Datenrahmen nicht dieselbe Länge haben müssen.

Ich habe versucht, pandas.merge(how='outer') zu verwenden, war mir aber nicht sicher, welche Spalte als "Schlüssel" übergeben werden sollte, da es wirklich keine gibt und die verschiedenen Kombinationen, die ich versucht habe, nicht funktionierten. Möglicherweise enthält df1 oder df2 zwei (oder mehr) identische Zeilen.

Was ist ein guter Weg, um dies in Pandas/Python zu tun?

2
mhy

Versuche dies:

diff_df = pd.merge(df1, df2, how='outer', indicator='Exist')

diff_df = diff_df.loc[diff_df['Exist'] != 'both']

Sie erhalten einen Datenrahmen aller Zeilen, die nicht auf df1 und df2 vorhanden sind.

1
niceGuy
  1. df2.columns = df1.columns setzen

  2. Stellen Sie nun jede Spalte als Index ein: df1 = df1.set_index(df1.columns.tolist()) und ähnlich für df2.

  3. Sie können jetzt df1.index.difference(df2.index) und df2.index.difference(df1.index) ausführen, und die beiden Ergebnisse sind Ihre unterschiedlichen Spalten.

1
coldspeed

IIUC:
Sie können pd.Index.symmetric_difference verwenden

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

Sie können diese Funktion verwenden. Die Ausgabe ist ein geordnetes Diktat von 6 Datenrahmen, die Sie zur weiteren Analyse in Excel schreiben können.

  • 'df1' und 'df2' beziehen sich auf Ihre Eingabedatenrahmen.
  • 'uid' bezieht sich auf die Spalte oder Kombination von Spalten, die den eindeutigen Schlüssel bilden. (d. h. "Früchte")
  • 'dedupe' (Standard = True) löscht Duplikate in df1 und df2. (siehe Schritt 4 in den Kommentaren)
  • Mit 'labels' (default = ('df1', 'df2') können Sie die Eingabedatenrahmen benennen. Wenn in beiden Datenframes ein eindeutiger Schlüssel vorhanden ist, die Werte jedoch in einer oder mehreren Spalten unterschiedlich sind, ist es normalerweise wichtig, diese Zeilen zu kennen, übereinander zu legen und die Zeile mit dem Namen zu beschriften, damit wir wissen, welcher Datenframe dies tut es gehört zu.
  • 'drop' kann eine Liste von Spalten enthalten, die bei der Berücksichtigung des Unterschieds von der Berücksichtigung ausgeschlossen werden sollen

Hier geht:

df1 = pd.DataFrame([['Apple', '1'], ['banana', 2], ['coconut',3]], columns=['Fruits','Quantity'])
df2 = pd.DataFrame([['Apple', '1'], ['banana', 3], ['durian',4]], columns=['Fruits','Quantity'])
dict1 = diff_func(df1, df2, 'Fruits')

In [10]: dict1['df1_only']:
Out[10]:
    Fruits Quantity
1  coconut        3

In [11]: dict1['df2_only']:
Out[11]:
   Fruits Quantity
3  durian        4

In [12]: dict1['Diff']:
Out[12]:
   Fruits Quantity df1 or df2
0  banana        2        df1
1  banana        3        df2

In [13]: dict1['Merge']:
Out[13]:
  Fruits Quantity
0  Apple        1

Hier ist der Code:

import pandas as pd
from collections import OrderedDict as od

def diff_func(df1, df2, uid, dedupe=True, labels=('df1', 'df2'), drop=[]):
    dict_df = {labels[0]: df1, labels[1]: df2}
    col1 = df1.columns.values.tolist()
    col2 = df2.columns.values.tolist()

    # There could be columns known to be different, hence allow user to pass this as a list to be dropped.
    if drop:
        print ('Ignoring columns {} in comparison.'.format(', '.join(drop)))
        col1 = list(filter(lambda x: x not in drop, col1))
        col2 = list(filter(lambda x: x not in drop, col2))
        df1 = df1[col1]
        df2 = df2[col2]


    # Step 1 - Check if no. of columns are the same:
    len_lr = len(col1), len(col2)
    assert len_lr[0]==len_lr[1], \
    'Cannot compare frames with different number of columns: {}.'.format(len_lr)

    # Step 2a - Check if the set of column headers are the same
    #           (order doesnt matter)
    assert set(col1)==set(col2), \
    'Left column headers are different from right column headers.' \
       +'\n   Left orphans: {}'.format(list(set(col1)-set(col2))) \
       +'\n   Right orphans: {}'.format(list(set(col2)-set(col1)))

    # Step 2b - Check if the column headers are in the same order
    if col1 != col2:
        print ('[Note] Reordering right Dataframe...')
        df2 = df2[col1]

    # Step 3 - Check datatype are the same [Order is important]
    if set((df1.dtypes == df2.dtypes).tolist()) - {True}:
        print ('dtypes are not the same.')
        df_dtypes = pd.DataFrame({labels[0]:df1.dtypes,labels[1]:df2.dtypes,'Diff':(df1.dtypes == df2.dtypes)})
        df_dtypes = df_dtypes[df_dtypes['Diff']==False][[labels[0],labels[1],'Diff']]
        print (df_dtypes)
    else:
        print ('DataType check: Passed')

    # Step 4 - Check for duplicate rows
    if dedupe:
        for key, df in dict_df.items():
            if df.shape[0] != df.drop_duplicates().shape[0]:
                print(key + ': Duplicates exists, they will be dropped.')
                dict_df[key] = df.drop_duplicates()

    # Step 5 - Check for duplicate uids.
    if type(uid)==str or type(uid)==list:
        print ('Uniqueness check: {}'.format(uid))
        for key, df in dict_df.items():
            count_uid = df.shape[0]
            count_uid_unique = df[uid].drop_duplicates().shape[0]
            var = [0,1][count_uid_unique == df.shape[0]] #<-- Round off to the nearest integer if it is 100%
            pct = round(100*count_uid_unique/df.shape[0], var)
            print ('{}: {} out of {} are unique ({}%).'.format(key, count_uid_unique, count_uid, pct))

    # Checks complete, begin merge. '''Remenber to dedupe, provide labels for common_no_match'''
    dict_result = od()
    df_merge = pd.merge(df1, df2, on=col1, how='inner')
    if not df_merge.shape[0]:
        print ('Error: Merged DataFrame is empty.')
    else:
        dict_result[labels[0]] = df1
        dict_result[labels[1]] = df2
        dict_result['Merge'] = df_merge
        if type(uid)==str:
            uid = [uid]

        if type(uid)==list:
            df1_only = df1.append(df_merge).reset_index(drop=True)
            df1_only['Duplicated']=df1_only.duplicated(subset=uid, keep=False)  #keep=False, marks all duplicates as True
            df1_only = df1_only[df1_only['Duplicated']==False]
            df2_only = df2.append(df_merge).reset_index(drop=True)
            df2_only['Duplicated']=df2_only.duplicated(subset=uid, keep=False)
            df2_only = df2_only[df2_only['Duplicated']==False]

            label = labels[0]+' or '+labels[1]
            df_lc = df1_only.copy()
            df_lc[label] = labels[0]
            df_rc = df2_only.copy()
            df_rc[label] = labels[1]
            df_c = df_lc.append(df_rc).reset_index(drop=True)
            df_c['Duplicated'] = df_c.duplicated(subset=uid, keep=False)
            df_c1 = df_c[df_c['Duplicated']==True]
            df_c1 = df_c1.drop('Duplicated', axis=1)
            df_uc = df_c[df_c['Duplicated']==False]

            df_uc_left = df_uc[df_uc[label]==labels[0]]
            df_uc_right = df_uc[df_uc[label]==labels[1]]

            dict_result[labels[0]+'_only'] = df_uc_left.drop(['Duplicated', label], axis=1)
            dict_result[labels[1]+'_only'] = df_uc_right.drop(['Duplicated', label], axis=1)
            dict_result['Diff'] = df_c1.sort_values(uid).reset_index(drop=True)

    return dict_result
0
Ji Wei