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.