DataFrame grande e persistente em pandas


91

Estou explorando a mudança para python e pandas como um usuário SAS de longa data.

No entanto, ao executar alguns testes hoje, fiquei surpreso que python ficou sem memória ao tentar pandas.read_csv()um arquivo csv de 128 MB. Ele tinha cerca de 200.000 linhas e 200 colunas de dados principalmente numéricos.

Com o SAS, posso importar um arquivo csv para um conjunto de dados SAS e pode ser tão grande quanto meu disco rígido.

Existe algo análogo em pandas?

Trabalho regularmente com arquivos grandes e não tenho acesso a uma rede de computação distribuída.


Não estou familiarizado com os pandas, mas talvez você queira dar uma olhada no arquivo iterado. pandas.pydata.org/pandas-docs/stable/…
monkut

Respostas:


79

Em princípio, ele não deveria ficar sem memória, mas atualmente existem problemas de memória read_csvem arquivos grandes causados ​​por alguns problemas internos complexos do Python (isso é vago, mas é conhecido há muito tempo: http://github.com/pydata / pandas / questões / 407 ).

No momento não há uma solução perfeita (aqui está uma tediosa: você pode transcrever o arquivo linha por linha em um array NumPy pré-alocado ou arquivo mapeado em memória-- np.mmap), mas é uma que estarei trabalhando em um futuro próximo. Outra solução é ler o arquivo em partes menores (usar iterator=True, chunksize=1000) e depois concatenar com pd.concat. O problema surge quando você puxa o arquivo de texto inteiro para a memória de uma só vez.


1
Digamos que eu possa ler o arquivo e concatá-los todos em um DataFrame. O DataFrame precisa residir na memória? Com o SAS, posso trabalhar com conjuntos de dados de qualquer tamanho, desde que tenha espaço no disco rígido. É o mesmo com DataFrames? Tenho a impressão de que eles são limitados pela RAM e não pelo espaço no disco rígido. Desculpe pela pergunta noob e obrigado pela ajuda. Estou gostando do seu livro.
Zelazny7

3
Certo, você está limitado pela RAM. O SAS de fato tem um suporte muito melhor para processamento de big data "fora do núcleo".
Wes McKinney

5
@WesMcKinney Essas soluções alternativas não devem ser mais necessárias, por causa do novo carregador csv que você instalou no 0.10, certo?
Gabriel Grant

79

Wes está certo, claro! Estou apenas entrando na conversa para fornecer um código de exemplo um pouco mais completo. Tive o mesmo problema com um arquivo de 129 Mb, que foi resolvido por:

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
Eu acho que você pode apenas fazer df = concate(tp, ignore_index=True)?
Andy Hayden

@smci Tentei isso rapidamente com os mesmos dados repetidos x4 (550 Mb) ou x8 (1,1 Gb). Curiosamente, com ou sem [x para x em tp], o x4 funcionou bem e o x8 travou em um MemoryError.
fickludd

3
Eu recebo este erro ao usá-lo: AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". Alguma ideia do que está acontecendo aqui?
Príncipe Kumar

3
Este bug será corrigido em 0.14 (lançamento em breve), github.com/pydata/pandas/pull/6941 ; solução alternativa para <0.14.0 é fazerpd.concat(list(tp), ignore_index=True)
Jeff

1
e se os valores forem strings ou categóricos - estou recebendo o erro: categorias incompatíveis em concat categórica
As3adTintin

40

Este é um segmento mais antigo, mas eu só queria despejar minha solução alternativa aqui. Eu tentei inicialmente o chunksizeparâmetro (mesmo com valores bem pequenos como 10000), mas não ajudou muito; ainda tinha problemas técnicos com o tamanho da memória (meu CSV era de ~ 7,5 Gb).

No momento, acabei de ler pedaços dos arquivos CSV em uma abordagem de loop for e adicioná-los, por exemplo, a um banco de dados SQLite passo a 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
Muito útil para ver um caso de uso realista para o recurso de leitura em partes. Obrigado.
Alex Kestner

5
Apenas um pequeno comentário sobre este tópico antigo: pandas.read_csvretorna diretamente (pelo menos na versão que estou usando atualmente) um iterador se você simplesmente fornecer iterator=Truee chunksize=chunksize. Portanto, você apenas faria um forloop sobre a pd.read_csvchamada, em vez de instanciar novamente todas as vezes. No entanto, isso custa apenas a sobrecarga da chamada, talvez não haja um impacto significativo.
Joël

1
Oi, Joel. Obrigado pela nota! Os parâmetros iterator=Truee chunksizejá existiam naquela época, se bem me lembro. Talvez houvesse um bug em uma versão mais antiga que causou o estouro da memória - vou tentar novamente na próxima vez que ler um grande DataFrame no Pandas (estou usando o Blaze agora para essas tarefas)

6

Abaixo está o meu fluxo de trabalho.

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')

Com base no tamanho do arquivo, é melhor otimizar o tamanho dos pedaços.

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

Depois de ter todos os dados no banco de dados, você pode consultar aqueles que precisa do banco de dados.


3

Se você deseja carregar arquivos csv enormes, o dask pode ser uma boa opção. Ele imita a api dos pandas, por isso é bastante semelhante aos pandas

link para dask no github


Obrigado, desde que postei isso estou usando o dask e o formato parquet.
Zelazny7

1

Você pode usar o Pytable em vez do pandas df. Ele é projetado para grandes conjuntos de dados e o formato de arquivo é hdf5. Portanto, o tempo de processamento é relativamente rápido.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.