8

Ho una funzione che elabora un DataFrame, in gran parte per elaborare i dati in bucket, creare una matrice binaria di caratteristiche in una particolare colonna usando pd.get_dummies(df[col]).Perché la concatenazione di DataFrames aumenta esponenzialmente più lentamente?

Per evitare l'elaborazione di tutti i miei dati utilizzando questa funzione in una sola volta (che finisce la memoria e causa ipython il crash), ho rotto il grande dataframe in blocchi utilizzando:

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

pd.get_dummies(df) creerà automaticamente nuove colonne basate sul contenuto di df[col] e queste potrebbero differire per ogni df in df_list.

Dopo l'elaborazione, sto concatenando il DataFrames di nuovo insieme con:

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

Il tempo di elaborazione del primo blocco è perfettamente accettabile, tuttavia, cresce ogni pezzo! Questo non ha a che fare con lo preprocess_data(df_chunk) in quanto non c'è motivo di aumentare. Questo aumento di tempo si verifica a seguito della chiamata a pd.concat()?

Vedere il registro di seguito:

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 

Esiste una soluzione per accelerare questo? Ho 2900 pezzi da elaborare, quindi ogni aiuto è apprezzato!

Aperto a qualsiasi altro suggerimento in Python!

risposta

8

Mai chiamare DataFrame.append o pd.concat all'interno di un ciclo for. Porta alla copia quadratica.

pd.concat restituisce un nuovo DataFrame. Lo spazio deve essere assegnato per il nuovo DataFrame e i dati dai vecchi DataFrames devono essere copiati nel nuovo DataFrame . Considerare la quantità di copie richiesto da questa linea all'interno del for-loop (assumendo ogni x ha dimensione 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. Quindi ci sono O(N**2) copie richieste per completare il ciclo.

Ora consideriamo

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) 

Aggiunta a un elenco è un'operazione O(1) e non richiede la copia. Ora c'è una singola chiamata a pd.concat dopo che il ciclo è terminato. Questa chiamata a pd.concat richiede N copie da effettuare, dal momento che contiene super_xN DataFrames di dimensioni 1. Così, quando costruito in questo modo, super_x richiede O(N) copie.

+0

Ciao @unutbu, grazie per la spiegazione dettagliata, questo ha davvero spiegato la teoria in dettaglio! – jfive

+0

È possibile concatenare 2900 blocchi di questa forma, in questo modo (43717, 3261)? Il passaggio di elaborazione richiede solo 10 secondi. – jfive

4

Ogni volta che si concatena, si restituisce una copia dei dati.

Si desidera mantenere un elenco dei blocchi e quindi concatenare tutto come passaggio finale.

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

Grazie mille, questo ha risolto il problema! – jfive

Problemi correlati