Como armazenar um quadro de dados usando o Pandas

317

No momento, estou importando um quadro de dados bastante grande CSVtoda vez que executo o script. Existe uma boa solução para manter esse dataframe constantemente disponível entre as execuções, para que eu não precise gastar todo esse tempo esperando a execução do script?

jeffstern
fonte
2
Sim, esta é uma das minhas principais reclamações usando Python - não há uma maneira simples de salvar e recuperar quadros de dados. R e SAS são muito mais amigáveis ​​a esse respeito.
RobertF 15/01/19

Respostas:

481

A maneira mais fácil é buscá- lo usando to_pickle:

df.to_pickle(file_name)  # where to save it, usually as a .pkl

Em seguida, você pode carregá-lo novamente usando:

df = pd.read_pickle(file_name)

Nota: antes de 0.11.1 savee loadera a única maneira de fazer isso (agora eles foram descontinuados em favor to_picklee read_picklerespectivamente).


Outra opção popular é usar o HDF5 ( pytables ), que oferece tempos de acesso muito rápidos para grandes conjuntos de dados:

store = HDFStore('store.h5')

store['df'] = df  # save it
store['df']  # load it

Estratégias mais avançadas são discutidas no livro de receitas .


Desde a versão 0.13, há também o msgpack, que pode ser melhor para interoperabilidade, como uma alternativa mais rápida ao JSON, ou se você tiver dados de objetos / textos pesados ​​em python (consulte esta pergunta ).

Andy Hayden
fonte
8
O @geekazoid save foi descontinuado para to_pickle (que cria um pickle em vez de um csv, que é um objeto muito mais rápido / diferente).
Andy Hayden
9
@geekazoid Caso os dados precisem ser transformados após o carregamento (ou seja, string / objeto em datetime64), isso precisará ser feito novamente após o carregamento de um CSV salvo, resultando em perda de desempenho. pickle salva o quadro de dados em seu estado atual, assim os dados e seu formato são preservados. Isso pode levar a grandes aumentos de desempenho.
harbun
4
O pickle e o HDFStore não podem salvar o dataframe superior a 8 GB. Existem alternativas?
user1700890
1
@ user1700890 tente gerar dados aleatórios (texto e matrizes) e poste uma nova pergunta. Eu não acho que isso possa estar certo / suspeito que estamos perdendo alguma coisa. Nova pergunta vai ter mais olhos, mas tente incluir / gerar uma trama de dados que reproduz :)
Andy Hayden
1
@YixingLiu você pode alterar o modo após o fato stackoverflow.com/a/16249655/1240268
Andy Hayden
100

Embora já existam algumas respostas, encontrei uma boa comparação na qual eles tentaram várias maneiras de serializar os DataFrames do Pandas: Armazene eficientemente os DataFrames do Pandas .

Eles comparam:

  • pickle: formato de dados ASCII original
  • cPickle, uma biblioteca C
  • pickle-p2: usa o formato binário mais recente
  • json: biblioteca json standardlib
  • json-no-index: como json, mas sem index
  • msgpack: alternativa JSON binária
  • CSV
  • hdfstore: formato de armazenamento HDF5

Em seu experimento, eles serializam um DataFrame de 1.000.000 de linhas com as duas colunas testadas separadamente: uma com dados de texto e outra com números. O aviso deles diz:

Você não deve confiar que o que se segue generalize para seus dados. Você deve examinar seus próprios dados e executar benchmarks você mesmo

O código fonte do teste ao qual eles se referem está disponível online . Como esse código não funcionou diretamente, fiz algumas pequenas alterações, que você pode obter aqui: serialize.py , obtive os seguintes resultados:

resultados de comparação de tempo

Eles também mencionam que, com a conversão de dados de texto em dados categóricos, a serialização é muito mais rápida. No teste, cerca de 10 vezes mais rápido (veja também o código do teste).

Editar : os tempos mais altos para picles que CSV podem ser explicados pelo formato de dados usado. Por padrão, pickleusa uma representação ASCII imprimível, que gera conjuntos de dados maiores. No entanto, como pode ser visto no gráfico, o pickle usando o formato de dados binários mais recente (versão 2 pickle-p2) tem tempos de carregamento muito menores.

Algumas outras referências:

agold
fonte
1
Atualizei minha resposta para explicar sua pergunta. Para resumir: por padrão, pickle armazena dados em um formato ASCII.
agold
1
Ah, obrigado por essa explicação! Como uma nota, pandas trama de dados .to_pickle parece estar usando o pkl.HIGHEST_PROTOCOL (deve ser 2)
NTG
2
Parece que o blog vinculado acima ( Eficientemente Store Pandas DataFrames foi excluído. Fiz minhas próprias comparações com .to_pickle()(que usa armazenamento binário) contra .to_hdf()(sem compactação). O objetivo era a velocidade, o tamanho do arquivo do HDF era 11x Pickle e o tempo para carregar foi 5x Pickle Meus dados foi ~ 5k arquivos de ~ 7k linhas x 6 cols cada um, na sua maioria numérica..
hamx0r
1
A página ainda existir, você só precisa remover a barra final: eficientemente loja Pandas DataFrames
IanSR
2
@ Mike Williamson, no meu teste, o pickle era 5x mais rápido do que o HDF e também ocupava 1/11 do espaço em disco (ou seja, o hdf era 11x maior no disco e levava 5x o tempo necessário para carregar do disco como o pickle). isso foi tudo no python 3 com pandas 0.22.0.
hamx0r
35

Se bem entendi, você já está usando, pandas.read_csv()mas gostaria de acelerar o processo de desenvolvimento para não precisar carregar o arquivo toda vez que editar seu script, certo? Tenho algumas recomendações:

  1. você pode carregar apenas parte do arquivo CSV usando pandas.read_csv(..., nrows=1000)apenas para carregar o bit superior da tabela enquanto estiver desenvolvendo

  2. use o ipython para uma sessão interativa, de forma que você mantenha a tabela pandas na memória enquanto edita e recarrega seu script.

  3. converter o csv em uma tabela HDF5

  4. atualizados uso DataFrame.to_feather()e pd.read_feather()para armazenar dados no R-compatível pena formato binário que é super rápido (em minhas mãos, ligeiramente mais rápido do que pandas.to_pickle()em dados numéricos e muito mais rápido em dados string).

Você também pode estar interessado nesta resposta no stackoverflow.

Noé
fonte
Você sabe por to_featherque funcionaria bem em dados de string? Eu aferido to_picklee to_featureno meu trama de dados numéricos e picles é de cerca de 3x mais rápido.
21418 zyxue
pergunta @zyxue bom, eu honestamente não tenho jogado muito com as coisas pena, então eu não tenho uma resposta
Noah
20

Pickle funciona bem!

import pandas as pd
df.to_pickle('123.pkl')    #to save the dataframe, df to 123.pkl
df1 = pd.read_pickle('123.pkl') #to load 123.pkl back to the dataframe df
Anbarasu Ramachandran
fonte
8
Observe que os arquivos gerados não são arquivos CSV, talvez seja melhor usar a extensão .pklconforme sugerido na resposta do @Andy Haydens.
agold
5

Você pode usar o arquivo de formato de penas. É extremamente rápido.

df.to_feather('filename.ft')
Huanyu Liao
fonte
E os dados podem ser usados ​​diretamente Rusando a featherbiblioteca.
James Hirschorn 28/01
4

Os DataFrames do Pandas têm a to_picklefunção que é útil para salvar um DataFrame:

import pandas as pd

a = pd.DataFrame({'A':[0,1,0,1,0],'B':[True, True, False, False, False]})
print a
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False

a.to_pickle('my_file.pkl')

b = pd.read_pickle('my_file.pkl')
print b
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False
mgoldwasser
fonte
4

Como já mencionado, existem diferentes opções e formatos de arquivo ( HDF5 , JSON , CSV , parquet , SQL ) para armazenar um quadro de dados. No entanto, picklenão é um cidadão de primeira classe (dependendo da sua configuração), porque:

  1. pickleé um risco potencial à segurança. Forme a documentação do Python para pickle :

Aviso O picklemódulo não é seguro contra dados incorretos ou mal-intencionados. Nunca remova os dados recebidos de uma fonte não confiável ou não autenticada.

  1. pickleé lento. Encontre aqui e aqui referências.

Dependendo da sua configuração / uso, ambas as limitações não se aplicam, mas eu não recomendaria picklecomo a persistência padrão para os quadros de dados do pandas.

Michael Dorner
fonte
1

Formatos de arquivo numpy são bastante rápidos para dados numéricos

Prefiro usar arquivos numpy, pois eles são rápidos e fáceis de trabalhar. Aqui está uma referência simples para salvar e carregar um quadro de dados com 1 coluna de 1 milhão de pontos.

import numpy as np
import pandas as pd

num_dict = {'voltage': np.random.rand(1000000)}
num_df = pd.DataFrame(num_dict)

usando a %%timeitfunção mágica do ipython

%%timeit
with open('num.npy', 'wb') as np_file:
    np.save(np_file, num_df)

a saída é

100 loops, best of 3: 5.97 ms per loop

carregar os dados novamente em um dataframe

%%timeit
with open('num.npy', 'rb') as np_file:
    data = np.load(np_file)

data_df = pd.DataFrame(data)

a saída é

100 loops, best of 3: 5.12 ms per loop

NÃO É RUIM!

CONS

Existe um problema se você salvar o arquivo numpy usando o python 2 e tentar abrir usando o python 3 (ou vice-versa).

Mark Jay
fonte
6
Observe que esta solução excluirá todos os nomes de colunas e alterará todos os dados inteiros para flutuar :(
Joseph Garvin
0

https://docs.python.org/3/library/pickle.html

Os formatos do protocolo pickle:

A versão 0 do protocolo é o protocolo “legível por humanos” original e é compatível com versões anteriores das versões anteriores do Python.

A versão 1 do protocolo é um formato binário antigo que também é compatível com versões anteriores do Python.

O protocolo versão 2 foi introduzido no Python 2.3. Ele fornece decapagem muito mais eficiente de novas classes de estilo. Consulte o PEP 307 para obter informações sobre melhorias trazidas pelo protocolo 2.

O protocolo versão 3 foi adicionado no Python 3.0. Ele possui suporte explícito para objetos de bytes e não pode ser desassociado pelo Python 2.x. Este é o protocolo padrão e o protocolo recomendado quando a compatibilidade com outras versões do Python 3 é necessária.

O protocolo versão 4 foi adicionado no Python 3.4. Ele adiciona suporte a objetos muito grandes, separando mais tipos de objetos e algumas otimizações de formato de dados. Consulte o PEP 3154 para obter informações sobre melhorias trazidas pelo protocolo 4.

Gilco
fonte
0

compatibilidade pyarrow entre versões

O movimento geral foi o pyarrow / feather (avisos de reprovação de pandas / msgpack). No entanto, tenho um desafio com pyarrow com transiente na especificação. Dados serializados com pyarrow 0.15.1 não podem ser desserializados com 0.16.0 ARROW-7961 . Estou usando serialização para usar redis, então tenho que usar uma codificação binária.

Testei novamente várias opções (usando o notebook jupyter)

import sys, pickle, zlib, warnings, io
class foocls:
    def pyarrow(out): return pa.serialize(out).to_buffer().to_pybytes()
    def msgpack(out): return out.to_msgpack()
    def pickle(out): return pickle.dumps(out)
    def feather(out): return out.to_feather(io.BytesIO())
    def parquet(out): return out.to_parquet(io.BytesIO())

warnings.filterwarnings("ignore")
for c in foocls.__dict__.values():
    sbreak = True
    try:
        c(out)
        print(c.__name__, "before serialization", sys.getsizeof(out))
        print(c.__name__, sys.getsizeof(c(out)))
        %timeit -n 50 c(out)
        print(c.__name__, "zlib", sys.getsizeof(zlib.compress(c(out))))
        %timeit -n 50 zlib.compress(c(out))
    except TypeError as e:
        if "not callable" in str(e): sbreak = False
        else: raise
    except (ValueError) as e: print(c.__name__, "ERROR", e)
    finally: 
        if sbreak: print("=+=" * 30)        
warnings.filterwarnings("default")

Com os seguintes resultados para meu quadro de dados (na outvariável jupyter)

pyarrow before serialization 533366
pyarrow 120805
1.03 ms ± 43.9 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pyarrow zlib 20517
2.78 ms ± 81.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
msgpack before serialization 533366
msgpack 109039
1.74 ms ± 72.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
msgpack zlib 16639
3.05 ms ± 71.7 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
pickle before serialization 533366
pickle 142121
733 µs ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pickle zlib 29477
3.81 ms ± 60.4 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
feather ERROR feather does not support serializing a non-default index for the index; you can .reset_index() to make the index into column(s)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
parquet ERROR Nested column branch had multiple children: struct<x: double, y: double>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

plumas e parquet não funcionam para o meu quadro de dados. Vou continuar usando pyarrow. No entanto, vou suplementar com picles (sem compressão). Ao escrever no cache, armazene os formulários serializados de pyarrow e pickle. Ao ler do fallback do cache para pickle se a desserialização do pyarrow falhar.

Rob Raymond
fonte
Isso não responde à pergunta
Jason S
0

O formato depende do seu caso de uso

  • Salve o DataFrame entre as sessões do notebook - pena , se você estiver acostumado a conservar - também ok.
  • Salve o DataFrame no menor tamanho de arquivo possível - parquet ou pickle.gz (verifique o que é melhor para seus dados)
  • Salve um DataFrame muito grande (mais de 10 milhões de linhas) - hdf
  • Consiga ler os dados em outra plataforma (não Python) que não suporta outros formatos - csv , csv.gz , verifique se o parquet é suportado
  • Seja capaz de revisar com seus olhos / usando o Excel / Google Sheets / Git diff - csv
  • Salve um DataFrame que consome quase toda a RAM - csv

Comparação dos formatos de arquivo pandas estão neste vídeo .

Artoby
fonte