DataFrame grande e persistente nei panda


91

Sto esplorando il passaggio a python e panda come utente SAS di lunga data.

Tuttavia, durante l'esecuzione di alcuni test oggi, sono rimasto sorpreso dal fatto che python abbia esaurito la memoria durante il tentativo di pandas.read_csv()un file CSV da 128 MB. Aveva circa 200.000 righe e 200 colonne di dati per lo più numerici.

Con SAS, posso importare un file CSV in un set di dati SAS e può essere grande quanto il mio disco rigido.

C'è qualcosa di analogo in pandas?

Lavoro regolarmente con file di grandi dimensioni e non ho accesso a una rete informatica distribuita.


Non ho familiarità con i panda, ma potresti voler esaminare l'iterazione del file. pandas.pydata.org/pandas-docs/stable/…
monkut

Risposte:


79

In linea di principio non dovrebbe esaurire la memoria, ma attualmente ci sono problemi di memoria con read_csvfile di grandi dimensioni causati da alcuni complessi problemi interni di Python (questo è vago ma è noto da molto tempo: http://github.com/pydata / pandas / issues / 407 ).

Al momento non esiste una soluzione perfetta (eccone una noiosa: potresti trascrivere il file riga per riga in un array NumPy pre-allocato o in un file mappato in memoria-- np.mmap), ma è quello su cui lavorerò nel prossimo futuro. Un'altra soluzione è leggere il file in parti più piccole (usa iterator=True, chunksize=1000) quindi concatenarlo con pd.concat. Il problema si presenta quando si estrae l'intero file di testo in memoria in un unico grande slurp.


1
Diciamo che posso leggere il file e concatenarli tutti insieme in un unico DataFrame. Il DataFrame deve risiedere in memoria? Con SAS, posso lavorare con set di dati di qualsiasi dimensione purché disponga dello spazio sul disco rigido. È lo stesso con DataFrames? Ho l'impressione che siano limitati dalla RAM e non dallo spazio sul disco rigido. Ci scusiamo per la domanda noob e grazie per il tuo aiuto. Mi sto godendo il tuo libro.
Zelazny7

3
Bene, sei vincolato dalla RAM. SAS ha effettivamente un supporto molto migliore per l'elaborazione di big data "out-of-core".
Wes McKinney

5
@WesMcKinney Queste soluzioni alternative non dovrebbero più essere necessarie, a causa del nuovo caricatore CSV che hai ottenuto con la versione 0.10, giusto?
Gabriel Grant

79

Wes ha ovviamente ragione! Sto solo intervenendo per fornire un codice di esempio un po 'più completo. Ho avuto lo stesso problema con un file da 129 Mb, che è stato risolto da:

import pandas as pd

tp = pd.read_csv('large_dataset.csv', iterator=True, chunksize=1000)  # gives TextFileReader, which is iterable with chunks of 1000 rows.
df = pd.concat(tp, ignore_index=True)  # df is DataFrame. If errors, do `list(tp)` instead of `tp`

6
Penso che puoi solo fare df = concate(tp, ignore_index=True)?
Andy Hayden

@smci L'ho provato rapidamente con gli stessi dati ripetuti x4 (550 Mb) o x8 (1,1 Gb). È interessante notare che, con o senza [x for x in tp], x4 ha funzionato bene e x8 si è bloccato in un MemoryError.
fickludd

3
Ottengo questo errore durante l'utilizzo: AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". Qualche idea di cosa sta succedendo qui?
Prince Kumar

3
Questo bug verrà corretto nella 0.14 (rilascio a breve), github.com/pydata/pandas/pull/6941 ; soluzione per <0.14.0 è da farepd.concat(list(tp), ignore_index=True)
Jeff

1
cosa succede se i valori sono stringhe o categoriali
ricevo

41

Questo è un thread più vecchio, ma volevo solo scaricare la mia soluzione alternativa qui. Inizialmente ho provato il filechunksize parametro (anche con valori piuttosto piccoli come 10000), ma non è stato di grande aiuto; aveva ancora problemi tecnici con la dimensione della memoria (il mio CSV era ~ 7,5 Gb).

In questo momento, ho appena letto blocchi di file CSV in un approccio for-loop e li ho aggiunti, ad esempio, a un database SQLite passo dopo passo:

import pandas as pd
import sqlite3
from pandas.io import sql
import subprocess

# In and output file paths
in_csv = '../data/my_large.csv'
out_sqlite = '../data/my.sqlite'

table_name = 'my_table' # name for the SQLite database table
chunksize = 100000 # number of lines to process at each iteration

# columns that should be read from the CSV file
columns = ['molecule_id','charge','db','drugsnow','hba','hbd','loc','nrb','smiles']

# Get number of lines in the CSV file
nlines = subprocess.check_output('wc -l %s' % in_csv, shell=True)
nlines = int(nlines.split()[0]) 

# connect to database
cnx = sqlite3.connect(out_sqlite)

# Iteratively read CSV and dump lines into the SQLite table
for i in range(0, nlines, chunksize):

    df = pd.read_csv(in_csv,  
            header=None,  # no header, define column header manually later
            nrows=chunksize, # number of rows to read at each iteration
            skiprows=i)   # skip rows that were already read

    # columns to read        
    df.columns = columns

    sql.to_sql(df, 
                name=table_name, 
                con=cnx, 
                index=False, # don't use CSV file index
                index_label='molecule_id', # use a unique column from DataFrame as index
                if_exists='append') 
cnx.close()    

4
Super utile per vedere un caso d'uso realistico per la funzione di lettura a blocchi. Grazie.
Alex Kestner

5
Solo una piccola osservazione, a questo vecchio argomento: pandas.read_csvrestituisce direttamente (almeno sulla versione che sto utilizzando attualmente) un iteratore se fornisci semplicemente iterator=Truee chunksize=chunksize. Quindi, faresti semplicemente un forciclo sulla pd.read_csvchiamata, invece di reistanziarla ogni volta. Tuttavia, questo costa solo il sovraccarico della chiamata, forse non c'è un impatto significativo.
Joël

1
Ciao, Joel. Grazie per la nota! I parametri iterator=Truee chunksizeesistevano già allora, se ricordo bene. Forse c'era un bug in una versione precedente che ha causato l'esplosione della memoria - Ci proverò la prossima volta che leggo un DataFrame di grandi dimensioni in Pandas (sto principalmente usando Blaze ora per tali attività)

6

Di seguito è riportato il mio flusso di lavoro.

import sqlalchemy as sa
import pandas as pd
import psycopg2

count = 0
con = sa.create_engine('postgresql://postgres:pwd@localhost:00001/r')
#con = sa.create_engine('sqlite:///XXXXX.db') SQLite
chunks = pd.read_csv('..file', chunksize=10000, encoding="ISO-8859-1",
                     sep=',', error_bad_lines=False, index_col=False, dtype='unicode')

Basandoti sulla dimensione del tuo file, faresti meglio a ottimizzare il chunksize.

 for chunk in chunks:
        chunk.to_sql(name='Table', if_exists='append', con=con)
        count += 1
        print(count)

Dopo avere tutti i dati nel database, puoi interrogare quelli che ti servono dal database.



1

Puoi usare Pytable piuttosto che pandas df. È progettato per set di dati di grandi dimensioni e il formato del file è in hdf5. Quindi il tempo di elaborazione è relativamente veloce.

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.