Come faccio a leggere un file CSV di grandi dimensioni con Panda?


194

Sto cercando di leggere un file CSV di grandi dimensioni (circa 6 GB) in Panda e ricevo un errore di memoria:

MemoryError                               Traceback (most recent call last)
<ipython-input-58-67a72687871b> in <module>()
----> 1 data=pd.read_csv('aphro.csv',sep=';')

...

MemoryError: 

Qualche aiuto su questo?


3
Curiosamente, una domanda molto simile è stata posta quasi un anno prima di questa ...
DarkCygnus,


Risposte:


263

L'errore mostra che la macchina non ha memoria sufficiente per leggere l'intero CSV in un DataFrame contemporaneamente. Supponendo che non sia necessario l'intero set di dati in memoria contemporaneamente, un modo per evitare il problema sarebbe quello di elaborare il CSV in blocchi (specificando il chunksizeparametro):

chunksize = 10 ** 6
for chunk in pd.read_csv(filename, chunksize=chunksize):
    process(chunk)

Il chunksizeparametro specifica il numero di righe per blocco. (L'ultimo pezzo può contenere meno di chunksizerighe, ovviamente.)


17
in genere hai bisogno di 2 volte la memoria finale per leggere qualcosa (da CSV, anche se altri formati sono migliori ad avere requisiti di memoria inferiori). Cordiali saluti, questo è vero per aver cercato di fare quasi tutto in una volta. Molto meglio tagliarlo (che ha un uso costante della memoria).
Jeff,

24
@altabq: il problema qui è che non abbiamo memoria sufficiente per creare un singolo DataFrame contenente tutti i dati. La soluzione precedente cerca di far fronte a questa situazione riducendo i blocchi (ad esempio aggregando o estraendo solo le informazioni desiderate) un pezzo alla volta - risparmiando così memoria. Qualunque cosa tu faccia, NON chiamare DF.append(chunk)all'interno del loop. Che utilizzerà O(N^2)le operazioni di copia. È meglio aggiungere i dati aggregati a un elenco , quindi creare il DataFrame dall'elenco con una chiamata a pd.DataFrameo pd.concat(a seconda del tipo di dati aggregati).
unutbu,

12
@altabq: la chiamata DF.append(chunk)in un ciclo richiede O(N^2)operazioni di copia in cui Nè la dimensione dei blocchi, perché ogni chiamata DF.appendrestituisce un nuovo DataFrame. Chiamare pd.DataFrameo pd.concat una volta al di fuori del ciclo riduce la quantità di copie su O(N).
unutbu,

5
@Pyderman: Sì, il chunksizeparametro si riferisce al numero di righe per blocco. L'ultimo pezzo può contenere meno di chunksizerighe, ovviamente.
unutbu,

7
@Pyderman: Sì; chiamare pd.concat([list_of_dfs]) una volta dopo il ciclo è molto più veloce di chiamare pd.concato df.appendmolte volte all'interno del ciclo. Ovviamente, avrai bisogno di una notevole quantità di memoria per contenere l'intero csv da 6 GB come un DataFrame.
unutbu,

86

Chunking non dovrebbe essere sempre il primo punto di riferimento per questo problema.

  1. Il file è grande a causa di dati non numerici ripetuti o colonne indesiderate?

    In tal caso, a volte puoi vedere enormi risparmi di memoria leggendo le colonne come categorie e selezionando le colonne richieste tramite il parametro pd.read_csv usecols .

  2. Il tuo flusso di lavoro richiede suddivisione, manipolazione, esportazione?

    In tal caso, è possibile utilizzare dask.dataframe per suddividere , eseguire i calcoli ed esportare iterativamente. Il chunking viene eseguito silenziosamente da dask, che supporta anche un sottoinsieme di API panda.

  3. Se tutto il resto fallisce, leggi riga per riga tramite blocchi.

    Chunk via panda o via libreria CSV come ultima risorsa.


34

Ho proceduto così:

chunks=pd.read_table('aphro.csv',chunksize=1000000,sep=';',\
       names=['lat','long','rf','date','slno'],index_col='slno',\
       header=None,parse_dates=['date'])

df=pd.DataFrame()
%time df=pd.concat(chunk.groupby(['lat','long',chunk['date'].map(lambda x: x.year)])['rf'].agg(['sum']) for chunk in chunks)

22
C'è una ragione si è passati da read_csva read_table?
Pyderman,

33

Per dati di grandi dimensioni ti consiglio di utilizzare la libreria "dask",
ad esempio:

# Dataframes implement the Pandas API
import dask.dataframe as dd
df = dd.read_csv('s3://.../2018-*-*.csv')

Puoi leggere di più dalla documentazione qui .

Un'altra ottima alternativa sarebbe quella di utilizzare modin perché tutte le funzionalità sono identiche a quelle dei panda ma sfruttano le librerie di dataframe distribuite come dask.


11
Eventuali benefici rispetto ai panda, potrebbero apprezzare l'aggiunta di alcuni altri suggerimenti
PirateApp

2
Non ho usato Dask per molto tempo, ma i principali vantaggi nei miei casi d'uso erano che Dask può funzionare in parallelo su più macchine, può anche adattare i dati come sezioni in memoria.
Simbarashe Timothy Motsi,

2
Grazie! è un sostituto per i panda o funziona sopra i panda come uno strato
PirateApp

3
Benvenuto, funziona come un wrapper per Numpy, Panda e Scikit-Learn.
Simbarashe Timothy Motsi,

1
Ho provato ad affrontare diversi problemi con Dask e genera sempre un errore per tutto. Anche con i pezzi genera anche errori di memoria. Vedi stackoverflow.com/questions/59865572/…
Genarito

10

La risposta sopra sta già soddisfacendo l'argomento. Ad ogni modo, se hai bisogno di tutti i dati in memoria, dai un'occhiata a bcolz . Comprime i dati in memoria. Ho avuto un'esperienza davvero positiva. Ma manca molte funzionalità dei panda

Modifica: ho ottenuto tassi di compressione intorno a 1/10 o dimensioni dell'orig, credo, ovviamente a seconda del tipo di dati. Le caratteristiche importanti mancanti erano aggregati.


2
Per favore, migliora questa risposta dicendoci a) quali rapporti di compressione ottieni eb) quali caratteristiche principali dei panda mancano? Può gestire le NA? stringhe? categorici? date?
smci,

Eh? Può gestire le NA? stringhe? categorici? date? Queste sono le cose che rendono la lettura dei panda csv lenta e flaccida. NA e oggetti come stringhe (anche brevi) sono un killer. A proposito, il .ipynb a cui fa riferimento il tuo blog è inattivo.
smci,

1
@smci ti stavo leggendo nota. ma ti suggerisco di dare un'occhiata ai documenti. avrei bisogno di leggerli da solo.
PlagTag

2
Ok, quindi non può gestire NA, stringhe o date. Dubito che possa gestire anche i galleggianti.
smci,

1
Suppongo che tu possa preelaborare i panda usando il chunksmetodo menzionato, quindi usare bcolz se hai bisogno di tutti i dati in memoria per fare un'analisi. Solo un pensiero.
JakeCowton,

6

Puoi leggere i dati come blocchi e salvare ogni blocco come pickle.

import pandas as pd 
import pickle

in_path = "" #Path where the large file is
out_path = "" #Path to save the pickle files to
chunk_size = 400000 #size of chunks relies on your available memory
separator = "~"

reader = pd.read_csv(in_path,sep=separator,chunksize=chunk_size, 
                    low_memory=False)    


for i, chunk in enumerate(reader):
    out_file = out_path + "/data_{}.pkl".format(i+1)
    with open(out_file, "wb") as f:
        pickle.dump(chunk,f,pickle.HIGHEST_PROTOCOL)

Nel passaggio successivo leggi i sottaceti e aggiungi ogni sottaceto al frame di dati desiderato.

import glob
pickle_path = "" #Same Path as out_path i.e. where the pickle files are

data_p_files=[]
for name in glob.glob(pickle_path + "/data_*.pkl"):
   data_p_files.append(name)


df = pd.DataFrame([])
for i in range(len(data_p_files)):
    df = df.append(pd.read_pickle(data_p_files[i]),ignore_index=True)

3
Se il tuo finale si dfadatta interamente alla memoria (come implicito) e contiene la stessa quantità di dati del tuo input, sicuramente non hai bisogno di chunk affatto?
jpp

In questo caso dovresti tagliarlo se, ad esempio, il tuo file è molto largo (come più di 100 colonne con molte colonne stringa). Ciò aumenta la memoria necessaria per contenere il df in memoria. Anche un file da 4 GB come questo potrebbe finire per usare tra 20 e 30 GB di RAM su una scatola con 64 GB di RAM.
cdabel,

4

La funzione read_csv e read_table è quasi la stessa. Ma devi assegnare il delimitatore “,” quando usi la funzione read_table nel tuo programma.

def get_from_action_data(fname, chunk_size=100000):
    reader = pd.read_csv(fname, header=0, iterator=True)
    chunks = []
    loop = True
    while loop:
        try:
            chunk = reader.get_chunk(chunk_size)[["user_id", "type"]]
            chunks.append(chunk)
        except StopIteration:
            loop = False
            print("Iteration is stopped")

    df_ac = pd.concat(chunks, ignore_index=True)

Sarebbe d'aiuto se affermassi qual è la tua domanda in questo post. Come "Qual è la differenza tra read_csv e read_table?" o "Perché la tabella di lettura necessita di un delimitatore?"
nate_weldon,

1
Dipende dall'aspetto del tuo file. Alcuni file hanno delimitatori comuni come "," o "|" o "\ t" ma potresti vedere altri file con delimitatori come 0x01, 0x02 (inventando questo) ecc. Quindi read_table è più adatto a delimitatori non comuni ma read_csv può fare lo stesso lavoro altrettanto bene.
Naufal,

3

Soluzione 1:

Utilizzo di Panda con dati di grandi dimensioni

Soluzione 2:

TextFileReader = pd.read_csv(path, chunksize=1000)  # the number of rows per chunk

dfList = []
for df in TextFileReader:
    dfList.append(df)

df = pd.concat(dfList,sort=False)

3
Anche in questo caso stiamo caricando il file da 6 GB totalmente in memoria, c'è qualche opzione, possiamo elaborare il blocco corrente e quindi leggere il blocco successivo
debaonline4u

6
semplicemente non farlo dfList.append, basta elaborare ogni blocco ( df) separatamente
gokul_uf

3

Ecco un esempio:

chunkTemp = []
queryTemp = []
query = pd.DataFrame()

for chunk in pd.read_csv(file, header=0, chunksize=<your_chunksize>, iterator=True, low_memory=False):

    #REPLACING BLANK SPACES AT COLUMNS' NAMES FOR SQL OPTIMIZATION
    chunk = chunk.rename(columns = {c: c.replace(' ', '') for c in chunk.columns})

    #YOU CAN EITHER: 
    #1)BUFFER THE CHUNKS IN ORDER TO LOAD YOUR WHOLE DATASET 
    chunkTemp.append(chunk)

    #2)DO YOUR PROCESSING OVER A CHUNK AND STORE THE RESULT OF IT
    query = chunk[chunk[<column_name>].str.startswith(<some_pattern>)]   
    #BUFFERING PROCESSED DATA
    queryTemp.append(query)

#!  NEVER DO pd.concat OR pd.DataFrame() INSIDE A LOOP
print("Database: CONCATENATING CHUNKS INTO A SINGLE DATAFRAME")
chunk = pd.concat(chunkTemp)
print("Database: LOADED")

#CONCATENATING PROCESSED DATA
query = pd.concat(queryTemp)
print(query)


2

Se usi i panda leggi file di grandi dimensioni in blocchi e poi produci riga per riga, ecco cosa ho fatto

import pandas as pd

def chunck_generator(filename, header=False,chunk_size = 10 ** 5):
   for chunk in pd.read_csv(filename,delimiter=',', iterator=True, chunksize=chunk_size, parse_dates=[1] ): 
        yield (chunk)

def _generator( filename, header=False,chunk_size = 10 ** 5):
    chunk = chunck_generator(filename, header=False,chunk_size = 10 ** 5)
    for row in chunk:
        yield row

if __name__ == "__main__":
filename = r'file.csv'
        generator = generator(filename=filename)
        while True:
           print(next(generator))

1

Voglio fare una risposta più completa basata sulla maggior parte delle potenziali soluzioni che sono già fornite. Voglio anche sottolineare un ulteriore potenziale aiuto che può aiutare il processo di lettura.

Opzione 1: tipi

"dtypes" è un parametro abbastanza potente che puoi usare per ridurre la pressione di memoria dei readmetodi. Vedi questa e questa risposta. I panda, per impostazione predefinita, provano a dedurre i tipi di dati.

Facendo riferimento alle strutture di dati, ad ogni dato archiviato, avviene un'allocazione di memoria. A livello base fare riferimento ai valori seguenti (La tabella seguente illustra i valori per il linguaggio di programmazione C):

The maximum value of UNSIGNED CHAR = 255                                    
The minimum value of SHORT INT = -32768                                     
The maximum value of SHORT INT = 32767                                      
The minimum value of INT = -2147483648                                      
The maximum value of INT = 2147483647                                       
The minimum value of CHAR = -128                                            
The maximum value of CHAR = 127                                             
The minimum value of LONG = -9223372036854775808                            
The maximum value of LONG = 9223372036854775807

Fare riferimento a questa pagina per vedere la corrispondenza tra i tipi NumPy e C.

Supponiamo che tu abbia una matrice di numeri interi di cifre . È possibile assegnare sia teoricamente che praticamente, diciamo array di tipo intero a 16 bit, ma allocare più memoria di quella necessaria per archiviare tale array. Per evitare ciò, è possibile attivare l' dtypeopzione read_csv. Non si desidera archiviare gli elementi dell'array come numeri interi lunghi in cui effettivamente è possibile adattarli con numeri interi a 8 bit ( np.int8o np.uint8).

Osservare la seguente mappa del tipo.

Fonte: https://pbpython.com/pandas_dtypes.html

Puoi passare un dtypeparametro come parametro sui metodi Panda come dict su readcome {colonna: tipo}.

import numpy as np
import pandas as pd

df_dtype = {
        "column_1": int,
        "column_2": str,
        "column_3": np.int16,
        "column_4": np.uint8,
        ...
        "column_n": np.float32
}

df = pd.read_csv('path/to/file', dtype=df_dtype)

Opzione 2: lettura di blocchi

La lettura dei dati in blocchi consente di accedere a una parte dei dati in memoria e di applicare la preelaborazione sui dati e preservare i dati elaborati anziché i dati non elaborati. Sarebbe molto meglio se combinassi questa opzione con il primo, i tipi .

Voglio sottolineare le sezioni del libro di cucina di Panda per quel processo, dove puoi trovarlo qui . Nota quelle due sezioni lì;

Opzione 3: Dask

Dask è un framework definito nel sito Web di Dask come:

Dask offre un parallelismo avanzato per l'analisi, consentendo prestazioni su vasta scala per gli strumenti che ami

È nato per coprire le parti necessarie dove i panda non possono raggiungere. Dask è un potente framework che ti consente di accedere molto più ai dati elaborandoli in modo distribuito.

Puoi usare dask per preelaborare i tuoi dati nel loro insieme, Dask si occupa della parte di chunking, quindi a differenza dei panda puoi semplicemente definire le tue fasi di elaborazione e lasciare che Dask faccia il lavoro. Dask non applica i calcoli prima di essere esplicitamente spinto da computee / o persist(vedere la risposta qui per la differenza).

Altri aiuti (idee)

  • Flusso ETL progettato per i dati. Mantenere solo ciò che è necessario dai dati grezzi.
    • Innanzitutto, applicare ETL a interi dati con framework come Dask o PySpark ed esportare i dati elaborati.
    • Quindi verifica se i dati elaborati possono essere inseriti nella memoria nel suo insieme.
  • Valuta di aumentare la tua RAM.
  • Prendi in considerazione l'idea di lavorare con quei dati su una piattaforma cloud.

0

Oltre alle risposte di cui sopra, per coloro che vogliono elaborare CSV e quindi esportare in CSV, parquet o SQL, d6tstack è un'altra buona opzione. È possibile caricare più file e si occupa delle modifiche allo schema dei dati (colonne aggiunte / rimosse). Chunked fuori dal supporto core è già integrato.

def apply(dfg):
    # do stuff
    return dfg

c = d6tstack.combine_csv.CombinerCSV([bigfile.csv], apply_after_read=apply, sep=',', chunksize=1e6)

# or
c = d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), apply_after_read=apply, chunksize=1e6)

# output to various formats, automatically chunked to reduce memory consumption
c.to_csv_combine(filename='out.csv')
c.to_parquet_combine(filename='out.pq')
c.to_psql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # fast for postgres
c.to_mysql_combine('mysql+mysqlconnector://usr:pwd@localhost/db', 'tablename') # fast for mysql
c.to_sql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # slow but flexible

0

Nel caso in cui qualcuno stia ancora cercando qualcosa di simile, ho scoperto che questa nuova libreria chiamata modin può aiutare. Utilizza il calcolo distribuito che può aiutare con la lettura. Ecco un bell'articolo che confronta la sua funzionalità con i panda. Utilizza essenzialmente le stesse funzioni dei panda.

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)

Puoi commentare come questo nuovo modulo si modinconfronta con il consolidato dask.dataframe? Ad esempio, vedi passare da Panda a Dask per utilizzare tutti i core CPU locali .
jpp,

0

Prima di usare l'opzione chunksize se vuoi essere sicuro della funzione di processo che vuoi scrivere all'interno del chunking for-loop come menzionato da @unutbu puoi semplicemente usare l'opzione nrows.

small_df = pd.read_csv(filename, nrows=100)

Una volta che si è sicuri che il blocco di processo sia pronto, è possibile inserirlo nel loop di chunking per l'intero frame di dati.

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.