Abrindo um arquivo de 20 GB para análise com pandas

33

No momento, estou tentando abrir um arquivo com pandas e python para fins de aprendizado de máquina. Seria ideal para todos eles em um DataFrame. Agora, o arquivo tem 18 GB de largura e minha RAM tem 32 GB, mas continuo recebendo erros de memória.

De sua experiência é possível? Se você não conhece uma maneira melhor de contornar isso? (tabela do hive? aumenta o tamanho da minha RAM para 64? cria um banco de dados e acessa-o a partir de python)

Hari Prasad
fonte
Eu tive o mesmo problema, sugiro que você aumente a troca, a paginação e o tamanho do seu disco rígido.
Mídia
A regra geral ao carregar dados pandasé que você precisa ter de 5 a 10 vezes mais memória RAM. Eu recomendo fazer inplaceoperações, chamar explicitamente garbage.collectorpara desalocar objetos.
22418 Kiritee Gak
4
Faça essa pergunta melhor indicando seu objetivo final. Você está fazendo análise exploratória de dados, limpeza de dados, treinamento de um modelo ou o quê? Que tipo de dados?
Pete
1
Você já pensou em usar o dask ?
rpanai

Respostas:

32

Se for um arquivo csv e você não precisar acessar todos os dados de uma vez ao treinar seu algoritmo, poderá lê-lo em pedaços. O pandas.read_csvmétodo permite que você leia um arquivo em pedaços como este:

import pandas as pd
for chunk in pd.read_csv(<filepath>, chunksize=<your_chunksize_here>)
    do_processing()
    train_algorithm()

Aqui está a documentação do método

Olel Daniel
fonte
isso se aplica ao arquivo zip também?
James Wierzba
Deve funcionar se o arquivo compactado também for um arquivo csv, você precisará passar o tipo de compactação como argumento para o método
Olel Daniel
22

Existem duas possibilidades: você precisa ter todos os dados na memória para processamento (por exemplo, o algoritmo de aprendizado de máquina gostaria de consumir todos de uma vez) ou pode ficar sem eles (por exemplo, o algoritmo precisa apenas de amostras de linhas ou colunas de uma só vez).

No primeiro caso, você precisará resolver um problema de memória . Aumente o tamanho da sua memória, alugue uma máquina na nuvem com muita memória, use operações no local, forneça informações sobre o tipo de dados em que você está lendo, exclua todas as variáveis ​​não utilizadas e colete lixo, etc.

É muito provável que 32 GB de RAM não sejam suficientes para o Pandas manipular seus dados. Observe que o número inteiro "1" tem apenas um byte quando armazenado como texto, mas 8 bytes quando representado como int64(que é o padrão quando o Pandas lê o texto). Você pode fazer o mesmo exemplo com um número de ponto flutuante "1.0" que se expande de uma sequência de 3 bytes para uma de 8 bytes float64por padrão. Você pode ganhar algum espaço informando ao Pandas com precisão quais tipos usar para cada coluna e forçando as menores representações possíveis, mas nem começamos a falar da sobrecarga da estrutura de dados do Python aqui, o que pode adicionar um ponteiro extra ou dois aqui ou ali facilmente e os ponteiros têm 8 bytes cada, em uma máquina de 64 bits.

Resumindo: não, 32 GB de RAM provavelmente não são suficientes para o Pandas manipular um arquivo de 20 GB.

No segundo caso (que é mais realista e provavelmente se aplica a você), você precisa resolver um problema de gerenciamento de dados . De fato, ter que carregar todos os dados quando você realmente precisa apenas de partes deles para processamento, pode ser um sinal de mau gerenciamento de dados. Existem várias opções aqui:

  1. Use um banco de dados SQL. Se você puder, é quase sempre a primeira escolha e uma solução decentemente confortável. 20 GB parece o tamanho que a maioria dos bancos de dados SQL suportaria bem, sem a necessidade de distribuição, mesmo em um laptop (de ponta). Você poderá indexar colunas, fazer agregações básicas via SQL e obter as subamostras necessárias no Pandas para um processamento mais complexo usando um simples pd.read_sql. Mover os dados para um banco de dados também fornecerá a você a oportunidade de pensar nos tipos e tamanhos de dados reais de suas colunas.

  2. Se seus dados forem principalmente numéricos (ou seja, matrizes ou tensores), considere mantê-los em um formato HDF5 (consulte PyTables ), que permite ler convenientemente apenas as fatias necessárias de enormes matrizes do disco. O básico numpy.save e numpy.load também obtêm o mesmo efeito através do mapeamento de memória das matrizes no disco. Para GIS e dados de varredura relacionados, existem bancos de dados dedicados , que podem não se conectar aos pandas tão diretamente quanto o SQL, mas também devem permitir fatias e consultas de forma conveniente.

  3. O Pandas não suporta esse mapeamento de memória "parcial" de HDF5 ou matrizes numpy, tanto quanto eu sei. Se você ainda deseja um tipo de solução para "pandas-puros", pode tentar contornar o "sharding": armazenando as colunas da sua enorme mesa separadamente (por exemplo, em arquivos separados ou em "tabelas" separadas de um único HDF5) arquivo) e apenas carregando os necessários sob demanda ou armazenando os blocos de linhas separadamente. No entanto, você precisará implementar a lógica para carregar os trechos necessários, reinventando as bicicletas já implementadas na maioria dos bancos de dados SQL; portanto, talvez a opção 1 ainda seja mais fácil aqui. Se seus dados vierem em um CSV, você poderá processá-los em partes, especificando o chunksizeparâmetro para pd.read_csv.

KT.
fonte
5
Algo que deve ser mencionado no "primeiro caso" é que, se o OP tiver muitas entradas com o mesmo valor nos dados (como zeros), os dados serão considerados esparsos e uma matriz esparsa e esparsa poderá ser usada em vez de uma pandas dataframe - dados esparsos requerem muito menos memória.
Ricardo Cruz
9

Acabei de ter esse problema há alguns dias! Não tenho certeza se isso ajuda no seu caso específico, pois você não está fornecendo tantos detalhes, mas minha situação era trabalhar offline em um conjunto de dados 'grande'. Os dados foram obtidos como arquivos CSV compactados em gb de 20 GB a partir de medidores de energia, dados de séries temporais em intervalos de alguns segundos.

Arquivo E / S:

data_root = r"/media/usr/USB STICK"
fname = r"meters001-050-timestamps.csv.gz"
this_file = os.path.join(data_root,fname)
assert os.path.exists(this_file), this_file
this_file

Crie um iterador de bloco diretamente sobre o arquivo gzip (não descompacte!)

cols_to_keep = [0,1,2,3,7]
column_names = ['METERID','TSTAMP','ENERGY','POWER_ALL','ENERGY_OUT',]
parse_dates = ['TSTAMP']
dtype={'METERID': np.int32, 
       'ENERGY': np.int32,
       'POWER_ALL': np.int32,
       'ENERGY_OUT': np.int32,
      }
df_iterator = pd.read_csv(this_file, 
                        skiprows=0, 
                        compression='gzip',
                        chunksize=1000000, 
                        usecols=cols_to_keep,
                        delimiter=";",
                        header=None,
                        names = column_names,
                      dtype=dtype,
                     parse_dates=parse_dates,
                     index_col=1,
                     )

Iterar sobre os pedaços

new_df = pd.DataFrame()
count = 0
for df in df_iterator:
    chunk_df_15min = df.resample('15T').first()
    #chunk_df_30min = df.resample('30T').first()
    #chunk_df_hourly = df.resample('H').first()
    this_df = chunk_df_15min
    this_df = this_df.pipe(lambda x: x[x.METERID == 1])
    #print("chunk",i)
    new_df = pd.concat([new_df,chunk_df_15min])
    print("chunk",count, len(chunk_df_15min), 'rows added')
    #print("chunk",i, len(temp_df),'rows added')
    #break
    count += 1

Dentro do loop do bloco, estou fazendo algumas filtragem e re-amostragem no prazo. Com isso, reduzi o tamanho de 20 GB para algumas centenas de MB HDF5 para mais exploração de dados offline.

Marcus Jones
fonte
5

Na minha experiência, a inicialização read_csv()com o parâmetro low_memory=Falsetende a ajudar na leitura de arquivos grandes. Não acho que você tenha mencionado o tipo de arquivo que está lendo, portanto, não tenho certeza de como isso se aplica à sua situação.

chainD
fonte
1

Se o seu arquivo é um CSV, você pode simplesmente fazê-lo no Chunk by Chunk. Você pode simplesmente fazer:

import pandas as pd
for chunk in pd.read_csv(FileName, chunksize=ChunkSizeHere)
(Do your processing and training here)
Abdul
fonte