2016-11-12 4 views
3

Ich fand, dass geradlinige Kettenverschmelzung mit Pandas-Bibliothek ziemlich ineffizient ist, wenn Sie viele Datensätze mit einer großen Anzahl von Spalten durch die gleiche Spalte zusammenführen.Effiziente Kettenzusammenführung in Pandas

Die Wurzel des Problems ist das gleiche wie wenn wir eine Menge str der stummen Weise verbinden: verbunden = reduzieren (Lambda a + b, str_list) Statt: verbunden = '' .join (str_list)

Doing Kette merge Kopieren wir oft Dataset (in meinem Fall fast 100-mal), anstatt nur Füllen von Spalten aus mehreren Datensätzen auf einmal oder um

gibt es eine effiziente Möglichkeit ist (= mit linearer Komplexität durch die Anzahl von Sets), um eine Menge von Datensätzen durch die gleiche Spalte zu verschmelzen?

Antwort

3

Wenn Sie eine Liste Ihrer Datenrahmen dfs:

dfs = [df1, df2, df3, ... , dfn] 

Sie sie beitreten können mit concat Funktion des Panda, die soweit ich sagen kann, ist schneller als merge verketten. concat verbindet nur Datenrahmen basierend auf einem Index (keine Spalte), aber mit einer kleinen Vorverarbeitung können Sie eine merge Operation simulieren.

Ersetzen Sie zuerst den Index jedes Ihrer Datenrahmen in dfs mit der Spalte, die Sie zusammenführen möchten. Sagen wir Sie auf Spalte "A" zusammenführen möchten:

dfs = [df.set_index("A", drop=True) for df in dfs] 

Beachten Sie, dass diese wird die bisherigen Indizes überschreiben (fusionieren würde diese ohnehin tun), so dass Sie könnte diese Indizes irgendwo gespeichert werden soll (wenn Sie gehen zu müssen, sie später aus irgendeinem Grund).

Jetzt können wir concat verwenden, die auf dem Index im Wesentlichen fusionieren (die tatsächlich Ihre Spalte !!)

merged = pd.concat(dfs, axis=1, keys=range(len(dfs)), join='outer', copy=False) 

Das join= Argument kann entweder 'inner' oder 'outer' (default) sein. Das Argument copy= hält concat davon ab, unnötige Kopien Ihrer Datenrahmen zu erstellen.

Sie können dann entweder verlassen "A" als Index oder Sie können es in eine Spalte, indem Sie machen zurück:

merged.reset_index(drop=False, inplace=True) 

Das keys= Argument ist optional und weist einen Schlüsselwert zu jedem Datenrahmen (in diesem Fall I gab es eine Reihe von ganzen Zahlen, aber Sie könnten ihnen andere Etiketten geben, wenn Sie wollen). Dadurch können Sie auf Spalten aus den ursprünglichen Datenrahmen zugreifen. Also, wenn Sie die Spalten, die bis zum 20. Datenrahmen entsprechen in dfs bekommen wollten rufen Sie an:

merged[20] 

Ohne das keys= Argument kann es verwirrend, welche Zeilen, aus denen Datenrahmen, vor allem, wenn sie die gleichen Spaltennamen haben .

Ich bin noch nicht ganz sicher, ob concat läuft in linearer Zeit, aber es ist auf jeden Fall schneller als Verkettungs merge:

ipython des% timeit auf Listen von zufällig generierten Datenrahmen unter Verwendung von (Listen von 10, 100 und 1000 Datenrahmen) :

def merge_with_concat(dfs, col):            
    dfs = [df.set_index(col, drop=True) for df in dfs] 
    merged = pd.concat(dfs, axis=1, keys=range(len(dfs)), join='outer', copy=False) 
    return merged 

dfs10 = [pd.util.testing.makeDataFrame() for i in range(10)] 
dfs100 = [pd.util.testing.makeDataFrame() for i in range(100)] 
dfs1000 = [pd.util.testing.makeDataFrame() for i in range(1000)] 

%timeit reduce(lambda df1, df2: df1.merge(df2, on="A", how='outer'), dfs10) 
10 loops, best of 3: 45.8 ms per loop 
%timeit merge_with_concat(dfs10,"A") 
100 loops, best of 3: 11.7 ms per loop 

%timeit merge_with_concat(dfs100,"A") 
10 loops, best of 3: 139 ms per loop 
%timeit reduce(lambda df1, df2: df1.merge(df2, on="A", how='outer'), dfs100) 
1 loop, best of 3: 1.55 s per loop 

%timeit merge_with_concat(dfs1000,"A") 
1 loop, best of 3: 9.67 s per loop 
%timeit reduce(lambda df1, df2: df1.merge(df2, on="A", how='outer'), dfs1000) 
# I killed it after about 5 minutes so the other one is definitely faster 
+0

Danke, das ist, was für – morph

+0

würde ich war auf der Suche – muon

+1

nicht 'dfs = (df.set_index (col, fallen = True) für df in dfs)' mehr Speicher effizient, da es ein Generator ist @ Myon ja, toller Fang! Ich habe ein paar Benchmarks mit dem 'dfs1000'-Set durchgeführt, nachdem ich es in einen Generator umgewandelt hatte, und es scheint auf meinem System etwa 2,5 Sekunden schneller zu laufen (7,1 Sekunden von 9,6). Wenn Sie diese Änderung vornehmen möchten, sollten Sie den Generator anders als "dfs" aufrufen, da Sie immer noch die Länge von 'dfs' (Anzahl der zu verschmelzenden Datenrahmen) für das' keys' Argument in 'pd.concat benötigen 'und Generatoren haben keine Länge. – bunji