8

Ich habe eine Funktion, die einen DataFrame verarbeitet, vor allem Daten in Buckets zu verarbeiten, erstellen Sie eine binäre Matrix von Features in einer bestimmten Spalte mit pd.get_dummies(df[col]).Warum wird die Verkettung von DataFrames exponentiell langsamer?

Verarbeitung all meiner Daten Um zu vermeiden, sofort mit dieser Funktion (die aus dem Speicher geht und bewirkt, dass ipython zum Absturz), ich habe den großen Datenrahmen in Stücke gebrochen werden:

chunks = (len(df)/10000) + 1 
df_list = np.array_split(df, chunks) 

pd.get_dummies(df) automatisch erstellen neue Spalten basierend auf dem Inhalt von df[col] und diese werden sich wahrscheinlich für jede df in df_list unterscheiden.

Nach der Verarbeitung Ich bin Verketten der Datenrahmen mit wieder zusammen:

for i, df_chunk in enumerate(df_list): 
    print "chunk", i 
    [x, y] = preprocess_data(df_chunk) 
    super_x = pd.concat([super_x, x], axis=0) 
    super_y = pd.concat([super_y, y], axis=0) 
    print datetime.datetime.utcnow() 

Die Bearbeitungszeit des ersten Brocken durchaus akzeptabel ist, wächst pro Chunk jedoch! Dies ist nicht mit der preprocess_data(df_chunk) zu tun, da es keinen Grund für die Erhöhung gibt. Tritt dieser Zeitanstieg als Folge des Anrufs auf pd.concat() auf?

siehe unten log:

chunks 6 
chunk 0 
2016-04-08 00:22:17.728849 
chunk 1 
2016-04-08 00:22:42.387693 
chunk 2 
2016-04-08 00:23:43.124381 
chunk 3 
2016-04-08 00:25:30.249369 
chunk 4 
2016-04-08 00:28:11.922305 
chunk 5 
2016-04-08 00:32:00.357365 

Gibt es eine Abhilfe dies zu beschleunigen? Ich habe 2900 Brocken zu verarbeiten, so dass jede Hilfe geschätzt wird!

Offen für alle anderen Vorschläge in Python!

Antwort

8

nennen niemals DataFrame.append oder pd.concat innerhalb einer for-Schleife. Dies führt zu quadratischem Kopieren.

pd.concat gibt einen neuen DataFrame zurück. Für den neuen DataFrame muss Speicherplatz reserviert werden, und Daten aus den alten DataFrames müssen in den neuen DataFrame kopiert werden. Betrachten Sie die Menge des Kopierens von dieser Linie innerhalb der for-loop erforderlich (vorausgesetzt, jede x Größe hat 1):

super_x = pd.concat([super_x, x], axis=0) 

| iteration | size of old super_x | size of x | copying required | 
|   0 |     0 |   1 |    1 | 
|   1 |     1 |   1 |    2 | 
|   2 |     2 |   1 |    3 | 
|  ... |      |   |     | 
|  N-1 |     N-1 |   1 |    N | 

1 + 2 + 3 + ... + N = N(N-1)/2. So gibt es O(N**2) Kopien erforderlich, um die Schleife abzuschließen.

Betrachten wir nun

super_x = [] 
for i, df_chunk in enumerate(df_list): 
    [x, y] = preprocess_data(df_chunk) 
    super_x.append(x) 
super_x = pd.concat(super_x, axis=0) 

auf eine Liste Anfügen ist ein O(1) Betrieb und nicht das Kopieren erforderlich. Jetzt gibt es einen einzigen Aufruf an pd.concat, nachdem die Schleife fertig ist. Dieser Aufruf von pd.concat erfordert Kopien N gemacht werden, da super_x enthält N Datenrahmen der Größe 1. So auf diese Weise, wenn gebaut, super_xO(N) Kopien erfordert.

+0

Hallo @unutbu, danke für die ausführliche Erklärung, das hat die Theorie wirklich im Detail erklärt! – jfive

+0

Ist es möglich, 2900 Blöcke dieser Form auf diese Weise zu verketten (43717, 3261)? Der Bearbeitungsschritt dauert jetzt nur noch 10 Sekunden. – jfive

4

Jedes Mal, wenn Sie verketten, geben Sie eine Kopie der Daten zurück.

Sie möchten eine Liste Ihrer Stücke behalten und dann alles als letzten Schritt verketten.

df_x = [] 
df_y = [] 
for i, df_chunk in enumerate(df_list): 
    print "chunk", i 
    [x, y] = preprocess_data(df_chunk) 
    df_x.append(x) 
    df_y.append(y) 

super_x = pd.concat(df_x, axis=0) 
del df_x # Free-up memory. 
super_y = pd.concat(df_y, axis=0) 
del df_y # Free-up memory. 
+0

Vielen Dank, das Problem wurde behoben! – jfive