2017-08-23 1 views
0

Ich habe ein Wörterbuch mit einer unbekannten Anzahl von Pandas Datenrahmen. Jeder Datenrahmen enthält eine Reihe von Spalten, die immer vorhanden sind (user_id) und eine Reihe von Spalten, die möglicherweise vorhanden sind oder nicht. Alle Datenrahmen haben die gleiche Anzahl und Reihenfolge der Zeilen. Der Inhalt jeder Zelle ist eine Liste (für die Spalten, die mich interessieren).Mehrere Pandas-Datenrahmen zu einem einzigen Datenrahmen verschmelzen mit Inhalt als Liste verkettet

Ein vereinfachtes Beispiel:

df['first']  = pd.DataFrame( {'user_ID': [1, 2, 3], 
         'col1': [[1], [2,3], [3]], 
         'col2': [[3], [3], [3,1]], 
         'col3': [[], [1,2,3], [3,1]]}) 

df['second'] = pd.DataFrame( {'user_ID': [1, 2, 3], 
         'col1': [[1, 2], [3], [3]], 
         'col3': [[1], [2,3], [3]], 
         'col4': [[3], [3], [3,1]] }) 

df['last']  = pd.DataFrame( {'user_ID': [1, 2, 3], 
         'col1': [[1], [2,3], [3]], 
         'col2': [[3], [3], [3,1]], 
         'col5': [[], [1,2,3], [3,1]]}) 

Sie sehen aus wie:

 col1 col2  col3 user_ID 
0  [1]  [3]   []  1 
1 [2, 3]  [3] [1, 2, 3]  2 
2  [3] [3, 1]  [3, 1]  3 

    col1 col3 col4 user_ID 
0 [1, 2]  [1]  [3]  1 
1  [3] [2, 3]  [3]  2 
2  [3]  [3] [3, 1]  3 

    col1 col2  col5 user_ID 
0  [1]  [3]   []  1 
1 [2, 3]  [3] [1, 2, 3]  2 
2  [3] [3, 1]  [3, 1]  3 

Wie kann ich alle verschmelzen diese Datenrahmen in einem einzigen Datenrahmen, in der alle Spalten, die nicht benutzer_ID zusammengeführt werden, so der Inhalt an die Liste angehängt?

Ergebnis aussehen soll (die Reihenfolge der Elemente in jeder Liste ist irrelevant):

   col1   col2    col3 col4  col5 user_ID 
0  [1, 1, 2, 1]  [3, 3]    [1]  [3]   []   1 
1 [2, 3, 3, 2, 3]  [3, 3] [1, 2, 3, 2, 3]  [2] [1, 2, 3]   2 
2  [3, 3, 3] [3, 1, 3, 1]  [3, 1, 3] [3, 1]  [3, 1]   3 

ich den Datenrahmen verketten geschaffen, aber ich muß noch die resultierenden Spalten verschmelzen.

for dfName in ['first', 'second', 'last']: 
    df[dfName] = df[dfName].drop(['user_ID'], axis=1) 

merged = pd.concat(df, axis=1, keys=['first', 'second', 'last']) 
print(merged) 

Ausgänge:

first      second     last   \ 
    col1 col2  col3 col1 col3 col4 col1 col2 
0  [1]  [3]   [] [1, 2]  [1]  [3]  [1]  [3] 
1 [2, 3]  [3] [1, 2, 3]  [3] [2, 3]  [3] [2, 3]  [3] 
2  [3] [3, 1]  [3, 1]  [3]  [3] [3, 1]  [3] [3, 1] 


     col5 
0   [] 
1 [1, 2, 3] 
2  [3, 1] 

Irgendwelche Ideen?

Antwort

2

Es ' s ein wenig beteiligt, aber Sie brauchen df.groupby. Verwenden Sie zuerst pd.concat und verbinden Sie sie. Dann ersetzen NaN s mit df.applymap, und schließlich die groupby und Summe.

In [673]: pd.concat([df1, df2, df3], 0)\ 
      .applymap(lambda x: [] if x != x else x)\ 
      .groupby('user_ID', as_index=False).sum() 
Out[673]: 
    user_ID    col1   col2    col3 col4  col5 
0  1  [1, 1, 2, 1]  [3, 3]    [1]  [3]   [] 
1  2 [2, 3, 3, 2, 3]  [3, 3] [1, 2, 3, 2, 3]  [3] [1, 2, 3] 
2  3  [3, 3, 3] [3, 1, 3, 1]  [3, 1, 3] [3, 1]  [3, 1] 

Leicht verbesserte Effizienz dank Maarten Fabré.


Wenn Sie eine unbekannte Menge an Datenrahmen haben, können Sie sie einem in list setzen oder dict, und übergeben das zu pd.concat:

merged = pd.concat(df_list, 0). ... 
+1

netter Trick Vergleich 'NaN' zu' NaN'. Ich habe das vergessen. Ich denke, Sie müssen nicht nach Achse 1 concat und dann transponieren. Sie können einfach concat und dann groupby 'user_ID' –

+0

@ MaartenFabré Works gut, danke für die Eingabe! –

+0

Ich habe eine kleine Änderung vorgenommen, um mit der Tatsache umzugehen, dass ich nicht weiß, wie viele Datenfelder ich im Dict habe: 'merged = pd.concat (df, Achse = 0, keys = list (df.keys())) .applymap (Lambda x: [] wenn x! = x sonst x) .groupby ('user_ID', as_index = Falsch) .sum() ' Und voila! – fuyas

1

könnten Sie df.groupby('user_ID').sum() verwenden, wenn es nicht für die nan Werte wäre, die alle Spalten auseinander col1 fallen lassen.

Um dies umgehen Sie diese eher hässlich Methode

pd.concat((df0, df1, df2)).fillna(-1).applymap(lambda x: x if x != -1 else []).groupby('user_ID').sum() 

ich zum fillna(-1).applymap(...) musste zurückgreifen nutzen könnten, weil Sie nicht [] direkt zu einem Element zuordnen können zu sein scheinen. Wenn jemand einen besseren Vorschlag, dies zu tun hat, lassen Sie mich wissen

bearbeiten

mit @ COLDSPEED Trick von NaN zu NaN

pd.concat((df0, df1, df2)).applymap(lambda x: x if x == x else []).groupby('user_ID').sum() 

Vergleich funktioniert einfacher

Wenn Sie die user_ID wollen als eine Spalte, n anstelle eines Index, einfach hinzufügen .reset_index()

Verwandte Themen