2013-03-14 12 views
13

Per python/panda, trovo che df.to_csv (fname) funzioni a una velocità di ~ 1 mln di righe al minuto. A volte riesco a migliorare le prestazioni di un fattore 7 in questo modo:Qual è il modo più veloce per generare un DataFrame di grandi dimensioni in un file CSV?

def df2csv(df,fname,myformats=[],sep=','): 
    """ 
    # function is faster than to_csv 
    # 7 times faster for numbers if formats are specified, 
    # 2 times faster for strings. 
    # Note - be careful. It doesn't add quotes and doesn't check 
    # for quotes or separators inside elements 
    # We've seen output time going down from 45 min to 6 min 
    # on a simple numeric 4-col dataframe with 45 million rows. 
    """ 
    if len(df.columns) <= 0: 
    return 
    Nd = len(df.columns) 
    Nd_1 = Nd - 1 
    formats = myformats[:] # take a copy to modify it 
    Nf = len(formats) 
    # make sure we have formats for all columns 
    if Nf < Nd: 
    for ii in range(Nf,Nd): 
     coltype = df[df.columns[ii]].dtype 
     ff = '%s' 
     if coltype == np.int64: 
     ff = '%d' 
     elif coltype == np.float64: 
     ff = '%f' 
     formats.append(ff) 
    fh=open(fname,'w') 
    fh.write(','.join(df.columns) + '\n') 
    for row in df.itertuples(index=False): 
    ss = '' 
    for ii in xrange(Nd): 
     ss += formats[ii] % row[ii] 
     if ii < Nd_1: 
     ss += sep 
    fh.write(ss+'\n') 
    fh.close() 

aa=DataFrame({'A':range(1000000)}) 
aa['B'] = aa.A + 1.0 
aa['C'] = aa.A + 2.0 
aa['D'] = aa.A + 3.0 

timeit -r1 -n1 aa.to_csv('junk1') # 52.9 sec 
timeit -r1 -n1 df2csv(aa,'junk3',myformats=['%d','%.1f','%.1f','%.1f']) # 7.5 sec 

Nota: l'aumento delle prestazioni dipende dtypes. Ma è sempre vero (almeno nei miei test) che to_csv() esegue molto più lentamente di Python non ottimizzato.

Se ho un file CSV 45 milioni di righe, quindi:

aa = read_csv(infile) # 1.5 min 
aa.to_csv(outfile)  # 45 min 
df2csv(aa,...)   # ~6 min 

Domande:

What are the ways to make the output even faster? 
What's wrong with to_csv() ? Why is it soooo slow ? 

Nota: i miei test sono stati fatti utilizzando panda 0.9.1 su un disco locale su un Linux server.

+0

Recentemente ho utilizzato i metodi built-in to_excel e poi to_csv DataFrame per esportare rapporti di circa 1.7 K in un batch da un gruppo DataFrame, e una parte dei report (in particolare, i file più grandi) è stata danneggiata. Ora sono piuttosto diffidente nei confronti di queste procedure integrate e ho in programma di preparare le mie proprie funzioni di esportazione per il mio flusso di lavoro. –

+2

@DavidMarx potresti pubblicare un esempio di file corrotto, DataFrame e il tuo codice? Ci aiuterebbe immensamente a risolvere il problema. Grazie. –

+0

È uno strumento da lavoro che ho costruito per il mio ufficio. Vedrò se non riesco a duplicare il problema con dati che non sono sensibili al posto di lavoro. –

risposta

1

La tua funzione df_to_csv è molto bella, tranne che fa un sacco di ipotesi e non funziona nel caso generale.

Se funziona per te, va bene, ma tieni presente che non è una soluzione generale. CSV può contenere virgole, quindi cosa succede se c'è questa tupla da scrivere? ('a,b','c')

Il modulo python csv dovrebbe citare quel valore in modo che non sorga alcuna confusione e eviterebbe le virgolette se le virgolette sono presenti in uno qualsiasi dei valori. Naturalmente generare qualcosa che funzioni in tutti i casi è molto più lento. Ma suppongo tu abbia solo un sacco di numeri.

Si potrebbe provare questo e vedere se è più veloce:

#data is a tuple containing tuples 

for row in data: 
    for col in xrange(len(row)): 
     f.write('%d' % row[col]) 
     if col < len(row)-1: 
      f.write(',') 
    f.write('\n') 

Non so se questo sarebbe più veloce. In caso contrario, sono state eseguite troppe chiamate di sistema, quindi è possibile utilizzare StringIO anziché l'output diretto e quindi eseguire il dump su un file reale ogni tanto.

10

Lev. Pandas ha riscritto lo to_csv per ottenere un notevole miglioramento della velocità nativa. Il processo ora è legato a I/O, rappresenta molti problemi di dtype e casi di citazione. Ecco i risultati delle nostre prestazioni rispetto alla versione 0.10.1 (nella prossima versione 0.11). Questi sono in ms, il rapporto inferiore è migliore.

Results: 
              t_head t_baseline  ratio 
name                  
frame_to_csv2 (100k) rows     190.5260 2244.4260  0.0849 
write_csv_standard (10k rows)    38.1940 234.2570  0.1630 
frame_to_csv_mixed (10k rows, mixed)  369.0670 1123.0412  0.3286 
frame_to_csv (3k rows, wide)    112.2720 226.7549  0.4951 

Così Throughput per un singolo DTYPE (per esempio carri), non troppo larga è di circa 20M righe/min, qui è il vostro esempio dall'alto.

In [12]: df = pd.DataFrame({'A' : np.array(np.arange(45000000),dtype='float64')}) 
In [13]: df['B'] = df['A'] + 1.0 
In [14]: df['C'] = df['A'] + 2.0 
In [15]: df['D'] = df['A'] + 2.0 
In [16]: %timeit -n 1 -r 1 df.to_csv('test.csv') 
1 loops, best of 1: 119 s per loop 
+0

Questo è un miglioramento eccellente.Grazie. –

+2

FWIW per questo tipo di set di dati che spesso si paga per archiviare in HDF5, specialmente se avete bisogno di interrogare, vedere http://pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables, e http://pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore – Jeff

+0

Sto provando a generare un dataframe da 100.000 * 100.000 e sta prendendo faticosamente lungo ... = ( – alvas

2

utilizzare chunksize. Ho scoperto che fa molta differenza. Se hai memoria in mano usa un buon chunksize (no di righe) per entrare in memoria e poi scrivi una volta.

+0

È il predefinito chunksize 1 o qualcosa del genere Che tipo di chunksize usi? Non sembrava fare la differenza per me, anche impostando chunksize = 500000 – wordsforthewise

+1

Trovato, potrebbe essere 1 ... https://github.com/pandas-dev/pandas/blob /v0.20.3/pandas/io/formats.py#L1555 – wordsforthewise

+0

@wordsforthewise chunksize è solo 1 se il numero di colonne> 100000, altrimenti è 100000 // numero di colonne – Matt

Problemi correlati