Como leio um arquivo csv grande com pandas?

194

Estou tentando ler um arquivo csv grande (aprox. 6 GB) no pandas e estou recebendo um erro de memória:

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

...

MemoryError: 

Alguma ajuda nisso?

Rajkumar Kumawat
fonte
3
Curiosamente, um muito semelhante pergunta foi perguntado quase um ano antes de este ...
DarkCygnus

Respostas:

261

O erro mostra que a máquina não possui memória suficiente para ler o CSV inteiro em um DataFrame de uma só vez. Supondo que você não precise do conjunto de dados inteiro na memória de uma só vez, uma maneira de evitar o problema seria processar o CSV em partes (especificando o chunksizeparâmetro):

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

O chunksizeparâmetro especifica o número de linhas por bloco. (O último pedaço pode conter menos de chunksizelinhas, é claro.)

unutbu
fonte
17
você geralmente precisa 2X da memória final para ler algo (do csv, embora outros formatos sejam melhores em ter requisitos de memória mais baixos). Para sua informação, isso é verdade para tentar fazer quase tudo ao mesmo tempo. Muito melhor dividi-lo (que tem um uso constante de memória).
Jeff
24
@altabq: O problema aqui é que não temos memória suficiente para construir um único DataFrame contendo todos os dados. A solução acima tenta lidar com essa situação reduzindo os pedaços (por exemplo, agregando ou extraindo apenas as informações desejadas) um pedaço de cada vez - economizando memória. Faça o que fizer, NÃO chame DF.append(chunk)dentro do loop. Isso usará O(N^2)operações de cópia. É melhor acrescentar os dados agregados a uma lista e criar o DataFrame a partir da lista com uma chamada para pd.DataFrameou pd.concat(dependendo do tipo de dados agregados).
21416 unutbu
12
@altabq: A chamada DF.append(chunk)em um loop requer O(N^2)operações de cópia onde Né o tamanho dos pedaços, porque cada chamada DF.appendretorna um novo DataFrame. Ligar pd.DataFrameou pd.concat uma vez fora do loop reduz a quantidade de cópias para O(N).
23416 unutbu
5
@ Pyderman: Sim, o chunksizeparâmetro se refere ao número de linhas por bloco. O último pedaço pode conter menos de chunksizelinhas, é claro.
Unutbu
7
@ Pyderman: Sim; chamar pd.concat([list_of_dfs]) uma vez após o loop é muito mais rápido do que chamar pd.concatou df.appendmuitas vezes dentro do loop. Obviamente, você precisará de uma quantidade considerável de memória para armazenar o csv inteiro de 6 GB como um DataFrame.
Unutbu
85

Chunking nem sempre deve ser a primeira porta de escala para esse problema.

  1. O arquivo é grande devido a dados não numéricos repetidos ou a colunas indesejadas?

    Nesse caso, às vezes você pode obter uma economia de memória massiva lendo as colunas como categorias e selecionando as colunas necessárias por meio do usecols parâmetro pd.read_csv .

  2. Seu fluxo de trabalho exige fatiar, manipular e exportar?

    Nesse caso, você pode usar o dask.dataframe para fatiar, executar seus cálculos e exportar iterativamente. O chunking é executado silenciosamente pelo dask, que também suporta um subconjunto da API do pandas.

  3. Se tudo mais falhar, leia linha por linha através de blocos.

    Pedaço via pandas ou via biblioteca csv como último recurso.

jpp
fonte
3
Eu não estava ciente de Dask. +100 por isso!
noamtm
34

Eu procedi assim:

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)
Rajkumar Kumawat
fonte
22
Existe uma razão pela qual você mudou read_csvpara read_table?
Pyderman
33

Para dados grandes, recomendo que você use a biblioteca "dask",
por exemplo:

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

Você pode ler mais na documentação aqui .

Outra ótima alternativa seria usar o modin, porque toda a funcionalidade é idêntica à dos pandas, mas aproveita as bibliotecas de quadros de dados distribuídos, como o dask.

Simbarashe Timothy Motsi
fonte
11
Quaisquer vantagens sobre pandas, podia apreciar a adição de mais alguns ponteiros
PirateApp
2
Não uso o Dask há muito tempo, mas as principais vantagens em meus casos de uso eram que o Dask pode ser executado paralelamente em várias máquinas, mas também pode ajustar dados como fatias na memória.
Simbarashe Timothy Motsi
2
obrigado! é dask um substituto para pandas ou funciona em cima de pandas como uma camada
PirateApp
3
Bem-vindo, ele funciona como um invólucro para Numpy, Pandas e Scikit-Learn.
Simbarashe Timothy Motsi
1
Eu tentei enfrentar vários problemas com o Dask e sempre gera um erro para tudo. Mesmo com pedaços Ele também gera erros de memória. Veja stackoverflow.com/questions/59865572/…
Genarito
10

A resposta acima já está satisfazendo o tópico. De qualquer forma, se você precisar de todos os dados da memória - dê uma olhada no bcolz . Está comprimindo os dados na memória. Eu tive uma experiência muito boa com isso. Mas faltam muitos recursos de pandas

Edit: Eu tenho taxas de compressão em torno de 1/10 ou tamanho orig, eu acho, é claro, dependendo do tipo de dados. Recursos importantes ausentes foram agregados.

PlagTag
fonte
2
Por favor, melhore esta resposta dizendo-nos a) quais taxas de compactação você obtém eb) quais principais características dos pandas estão faltando? Ele consegue lidar com NAs? cordas? categóricos? datas?
SMCI
Hã? Ele consegue lidar com NAs? cordas? categóricos? datas? Essas são as coisas que tornam a leitura de pandas csv lenta e flácida. NAs e objetos como cordas (mesmo os curtos) são um assassino. Mas o .ipynb mencionado no seu blog está inativo.
SMCI
1
@smci eu estava lendo sua nota. mas eu sugiro que você dê uma olhada nos documentos. eu precisaria lê-los eu mesmo.
PlagTag
2
Ok, então ele não pode lidar com NAs, strings ou datas. Duvido que ele possa lidar com carros alegóricos também.
SMCI
1
Suponho que você possa pré-processar com os pandas usando o chunksmétodo mencionado e usar o bcolz se precisar de todos os dados na memória para fazer uma análise. Apenas um pensamento.
JakeCowton
6

Você pode ler os dados como pedaços e salvar cada pedaço como picles.

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)

Na próxima etapa, você lerá os picles e anexará cada picles ao quadro de dados desejado.

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)
Lukas Humpe
fonte
3
Se a sua final dfse encaixa inteiramente na memória (como implícita) e contém a mesma quantidade de dados que a sua entrada, certamente você não precisa dividir nada?
jpp 19/02/19
Você precisaria dividir neste caso se, por exemplo, seu arquivo for muito amplo (como mais de 100 colunas com muitas colunas de string). Isso aumenta a memória necessária para manter o df na memória. Mesmo um arquivo de 4 GB como esse pode acabar usando entre 20 e 30 GB de RAM em uma caixa com 64 GB de RAM.
cdabel
4

As funções read_csv e read_table são quase as mesmas. Mas você deve atribuir o delimitador “,” ao usar a função read_table no seu programa.

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)
Tyrion W
fonte
Ajudaria se declarasse qual é a sua pergunta neste post. Como "Qual é a diferença entre read_csv e read_table?" ou "Por que a tabela de leitura precisa de um delimitador?"
N
1
Depende da aparência do seu arquivo. Alguns arquivos possuem delimitadores comuns, como "," ou "|" ou "\ t", mas você pode ver outros arquivos com delimitadores como 0x01, 0x02 (criando este) etc. Portanto, read_table é mais adequado para delimitadores incomuns, mas read_csv pode fazer o mesmo trabalho da mesma maneira.
Naufal
3

Solução 1:

Usando pandas com grandes dados

Solução 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)
ovelha negra
fonte
3
Aqui, novamente, estamos carregando o arquivo 6 GB totalmente à memória, há alguma opções, podemos processar o bloco atual e, em seguida, ler o próximo pedaço
debaonline4u
6
apenas não faça dfList.append, apenas processar cada pedaço ( df) separadamente
gokul_uf
3

Segue um exemplo:

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)
Jonathask
fonte
2

Você pode tentar o sframe, que tem a mesma sintaxe que os pandas, mas permite manipular arquivos maiores que a sua RAM.

nunodsousa
fonte
"Os dados no SFrame são armazenados em colunas no lado do servidor GraphLab" é um serviço ou um pacote?
Danny Wang
2

Se você usa pandas, lê grandes arquivos em partes e depois produz linha por linha, eis o que eu fiz

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))
paulg
fonte
1

Desejo fazer uma resposta mais abrangente com base na maioria das soluções possíveis que já são fornecidas. Também quero apontar mais uma ajuda potencial que pode ajudar no processo de leitura.

Opção 1: tipos

"dtypes" é um parâmetro bastante poderoso que você pode usar para reduzir a pressão de memória dos readmétodos. Veja esta e esta resposta. Os pandas, por padrão, tentam inferir tipos de dados.

Referindo-se a estruturas de dados, todos os dados armazenados, uma alocação de memória ocorre. Em um nível básico, consulte os valores abaixo (A tabela abaixo ilustra os valores da linguagem de programação 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

Consulte esta página para ver a correspondência entre os tipos NumPy e C.

Vamos dizer que você tem um array de inteiros de dígitos . Você pode atribuir teoricamente e praticamente, por exemplo, matriz do tipo inteiro de 16 bits, mas alocaria mais memória do que realmente precisa para armazenar essa matriz. Para evitar isso, você pode ativar a dtypeopção read_csv. Você não deseja armazenar os itens da matriz como um número inteiro longo onde, na verdade, você pode ajustá-los com um número inteiro de 8 bits ( np.int8ou np.uint8).

Observe o seguinte mapa de tipo.

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

Você pode passar o dtypeparâmetro como um parâmetro nos métodos de pandas como ditado readcomo {column: type}.

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)

Opção 2: Leia por Chunks

A leitura dos dados em partes permite acessar uma parte dos dados na memória e você pode aplicar o pré-processamento aos dados e preservar os dados processados ​​em vez dos dados brutos. Seria muito melhor se você combinar essa opção com o primeiro, dtypes .

Quero destacar as seções do livro de receitas dos pandas para esse processo, onde você pode encontrá-lo aqui . Observe essas duas seções lá;

Opção 3: Dask

O Dask é uma estrutura definida no site da Dask como:

O Dask fornece paralelismo avançado para análises, permitindo desempenho em escala para as ferramentas que você ama

Nasceu para cobrir as partes necessárias onde os pandas não podem alcançar. O Dask é uma estrutura poderosa que permite muito mais acesso a dados, processando-o de maneira distribuída.

Você pode usar o dask para pré-processar seus dados como um todo. O Dask cuida da parte do chunking. Assim, ao contrário dos pandas, você pode apenas definir suas etapas de processamento e deixar o Dask fazer o trabalho. O Dask não aplica os cálculos antes de ser explicitamente enviado por computee / ou persist(veja a resposta aqui para a diferença).

Outras ajudas (ideias)

  • Fluxo ETL projetado para os dados. Mantendo apenas o que é necessário a partir dos dados brutos.
    • Primeiro, aplique ETL a dados inteiros com estruturas como Dask ou PySpark e exporte os dados processados.
    • Então veja se os dados processados ​​podem caber na memória como um todo.
  • Considere aumentar sua RAM.
  • Considere trabalhar com esses dados em uma plataforma em nuvem.
nulo
fonte
0

Além das respostas acima, para quem deseja processar CSV e depois exportar para csv, parquet ou SQL, o d6tstack é outra boa opção. Você pode carregar vários arquivos e ele lida com alterações no esquema de dados (colunas adicionadas / removidas). O suporte fragmentado do núcleo já está incorporado.

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
citynorman
fonte
0

Caso alguém ainda esteja procurando algo assim, descobri que essa nova biblioteca chamada modin pode ajudar. Ele usa computação distribuída que pode ajudar na leitura. Aqui está um bom artigo comparando sua funcionalidade com os pandas. Essencialmente, ele usa as mesmas funções que os pandas.

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)
Jaskaran
fonte
Você pode comentar como esse novo módulo se modincompara ao bem estabelecido dask.dataframe? Por exemplo, consulte mover de pandas para dask para utilizar todos os núcleos de CPU locais .
jpp
0

Antes de usar a opção chunksize, se você quiser ter certeza sobre a função do processo que deseja escrever dentro do loop for de chunking, conforme mencionado por @unutbu, basta usar a opção nrows.

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

Depois de ter certeza de que o bloco do processo está pronto, você pode colocá-lo no loop chunking for para todo o quadro de dados.

sam
fonte