Apertura di un file da 20 GB per l'analisi con i panda


33

Attualmente sto provando ad aprire un file con Panda e Python per scopi di apprendimento automatico, sarebbe l'ideale per me averli tutti in un DataFrame. Ora il file ha una dimensione di 18 GB e la mia RAM è di 32 GB, ma continuo a ricevere errori di memoria.

Dalla tua esperienza è possibile? In caso contrario, conosci un modo migliore per aggirare il problema? (tabella hive? aumentare le dimensioni della mia RAM a 64? creare un database e accedervi da Python)


Ho avuto lo stesso problema, ti suggerisco di aumentare lo scambio, il paging, le dimensioni del tuo disco rigido.
Media,

La regola empirica durante il caricamento dei dati pandasè che è necessario disporre di 5-10 volte più RAM. Consiglio di fare inplaceoperazioni, chiamare esplicitamente garbage.collectorper disallocare gli oggetti.
Kiritee Gak,

4
Rendi migliore questa domanda affermando il tuo obiettivo finale. Stai facendo analisi esplorative dei dati, pulizia dei dati, formazione di un modello o cosa? Che tipo di dati?
Pete,

1
Hai pensato di usare Dask ?
rpanai,

Risposte:


32

Se si tratta di un file CSV e non è necessario accedere a tutti i dati contemporaneamente durante l'allenamento dell'algoritmo, è possibile leggerlo in blocchi. Il pandas.read_csvmetodo consente di leggere un file in blocchi come questo:

import pandas as pd
for chunk in pd.read_csv(<filepath>, chunksize=<your_chunksize_here>)
    do_processing()
    train_algorithm()

Ecco la documentazione del metodo


questo vale anche per il file zip?
James Wierzba,

Dovrebbe funzionare se il file zippato è anche un file CSV, dovrai passare il tipo di compressione come argomento al metodo
Olel Daniel

22

Ci sono due possibilità: o si bisogno di avere tutti i dati in memoria per l'elaborazione (ad esempio l'algoritmo di apprendimento automatico vorrebbe consumare tutto in una sola volta), oppure si può fare a meno (ad esempio, l'algoritmo ha bisogno solo di campioni di righe o colonne contemporaneamente).

Nel primo caso, dovrai risolvere un problema di memoria . Aumenta le dimensioni della tua memoria, noleggia una macchina cloud ad alta memoria, utilizza le operazioni sul posto, fornisci informazioni sul tipo di dati che stai leggendo, assicurati di eliminare tutte le variabili non utilizzate e raccogliere rifiuti, ecc.

È molto probabile che 32 GB di RAM non siano sufficienti per gestire i dati da parte di Panda. Si noti che l'intero "1" è solo un byte se memorizzato come testo ma 8 byte quando rappresentato come int64(che è l'impostazione predefinita quando Pandas lo legge dal testo). Puoi fare lo stesso esempio con un numero in virgola mobile "1.0" che si espande da una stringa di 3 byte a un 8 byte float64per impostazione predefinita. Puoi guadagnare un po 'di spazio facendo sapere ai Panda esattamente quali tipi usare per ogni colonna e forzando le rappresentazioni più piccole possibili, ma non abbiamo nemmeno iniziato a parlare del sovraccarico della struttura dei dati di Python qui, che potrebbe aggiungere un puntatore extra o due qui o là facilmente e i puntatori sono 8 byte ciascuno su una macchina a 64 bit.

Riassumendo: no, probabilmente 32 GB di RAM non sono sufficienti per Pandas per gestire un file da 20 GB.

Nel secondo caso (che è più realistico e probabilmente si applica a te), devi risolvere un problema di gestione dei dati . In effetti, dover caricare tutti i dati quando in realtà sono necessari solo parti di essi per l'elaborazione, può essere un segno di cattiva gestione dei dati. Ci sono più opzioni qui:

  1. Utilizzare un database SQL. Se puoi, è quasi sempre la prima scelta e una soluzione abbastanza confortevole. 20 GB sembrano le dimensioni che la maggior parte dei database SQL gestirà bene senza la necessità di essere distribuiti anche su un laptop (di fascia alta). Sarai in grado di indicizzare le colonne, fare aggregazioni di base tramite SQL e ottenere i sottocampioni necessari in Panda per un'elaborazione più complessa usando un semplice pd.read_sql. Lo spostamento dei dati in un database ti darà anche l'opportunità di pensare ai tipi di dati e alle dimensioni effettivi delle tue colonne.

  2. Se i tuoi dati sono per lo più numerici (ad esempio array o tensori), puoi prendere in considerazione la possibilità di tenerli in un formato HDF5 (vedi Tabelle Py ), che ti consente di leggere comodamente solo le sezioni necessarie di enormi array dal disco. Base numpy.save e numpy.load ottenere lo stesso effetto con la memoria a mappare gli array sul disco pure. Per GIS e relativi dati raster ci sono database dedicati , che potrebbero non connettersi ai panda direttamente come SQL, ma dovrebbero anche consentire di eseguire sezioni e query ragionevolmente comodamente.

  3. Pandas non supporta tale mappatura "parziale" della memoria di HDF5 o array intorpiditi, per quanto ne so. Se vuoi ancora una sorta di soluzione "pure-panda", puoi provare a aggirare "frammentando": sia archiviando le colonne della tua enorme tabella separatamente (ad esempio in file separati o in "tabelle" separate di un singolo HDF5 file) e caricando solo quelli necessari su richiesta o memorizzando i blocchi di righe separatamente. Tuttavia, dovresti quindi implementare la logica per caricare i blocchi necessari, reinventando così le biciclette già implementate nella maggior parte dei database SQL, quindi forse l'opzione 1 sarebbe ancora più semplice qui. Se i tuoi dati arrivano in un CSV, tuttavia, puoi elaborarli in blocchi specificando il chunksizeparametro su pd.read_csv.


5
Qualcosa che dovrebbe essere menzionato nel "primo caso" è che se l'OP ha molte voci con lo stesso valore nei dati (come zeri), si dice che i dati sono radi e si potrebbe usare una matrice scipy piuttosto che un Panda Dataframe: i dati sparsi richiedono molta meno memoria.
Ricardo Cruz,

9

Ho appena avuto questo problema qualche giorno fa! Non sono sicuro se questo aiuta nel tuo caso specifico poiché non stai fornendo così tanti dettagli, ma la mia situazione era di lavorare offline su un set di dati "di grandi dimensioni". I dati sono stati ottenuti come file CSV con gzip da 20 GB da contatori di energia, dati di serie temporali a intervalli di diversi secondi.

File IO:

data_root = r"/media/usr/USB STICK"
fname = r"meters001-050-timestamps.csv.gz"
this_file = os.path.join(data_root,fname)
assert os.path.exists(this_file), this_file
this_file

Creare un iteratore di blocchi direttamente sul file gzip (non decomprimere!)

cols_to_keep = [0,1,2,3,7]
column_names = ['METERID','TSTAMP','ENERGY','POWER_ALL','ENERGY_OUT',]
parse_dates = ['TSTAMP']
dtype={'METERID': np.int32, 
       'ENERGY': np.int32,
       'POWER_ALL': np.int32,
       'ENERGY_OUT': np.int32,
      }
df_iterator = pd.read_csv(this_file, 
                        skiprows=0, 
                        compression='gzip',
                        chunksize=1000000, 
                        usecols=cols_to_keep,
                        delimiter=";",
                        header=None,
                        names = column_names,
                      dtype=dtype,
                     parse_dates=parse_dates,
                     index_col=1,
                     )

Scorri i pezzi

new_df = pd.DataFrame()
count = 0
for df in df_iterator:
    chunk_df_15min = df.resample('15T').first()
    #chunk_df_30min = df.resample('30T').first()
    #chunk_df_hourly = df.resample('H').first()
    this_df = chunk_df_15min
    this_df = this_df.pipe(lambda x: x[x.METERID == 1])
    #print("chunk",i)
    new_df = pd.concat([new_df,chunk_df_15min])
    print("chunk",count, len(chunk_df_15min), 'rows added')
    #print("chunk",i, len(temp_df),'rows added')
    #break
    count += 1

All'interno del chunk loop, sto facendo un po 'di filtraggio e ricampionamento in tempo. In questo modo ho ridotto le dimensioni da 20 GB a poche centinaia di MB HDF5 per un'ulteriore esplorazione dei dati offline.


5

Nella mia esperienza, l'inizializzazione read_csv()con parametro low_memory=Falsetende ad aiutare durante la lettura di file di grandi dimensioni. Non penso che tu abbia menzionato il tipo di file in cui stai leggendo, quindi non sono sicuro di quanto sia applicabile alla tua situazione.


1

Se il tuo file è un CSV, puoi semplicemente farlo in Chunk by Chunk. Puoi semplicemente fare:

import pandas as pd
for chunk in pd.read_csv(FileName, chunksize=ChunkSizeHere)
(Do your processing and training here)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.