Comment stocker une trame de données à l'aide de Pandas

317

À l'heure actuelle, CSVj'importe un fichier assez volumineux à chaque fois que j'exécute le script. Existe-t-il une bonne solution pour garder cette trame de données disponible en permanence entre les exécutions, donc je n'ai pas à passer tout ce temps à attendre l'exécution du script?

Jeffstern
la source
2
Oui, c'est l'une de mes principales plaintes en utilisant Python - il n'y a pas de moyen simple pour enregistrer et récupérer des trames de données. R et SAS sont beaucoup plus conviviaux à cet égard.
RobertF

Réponses:

481

Le moyen le plus simple est de le décaper en utilisant to_pickle:

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

Ensuite, vous pouvez le recharger en utilisant:

df = pd.read_pickle(file_name)

Remarque: avant 0.11.1 saveet loadétaient le seul moyen de le faire (ils sont désormais obsolètes en faveur de to_pickleet read_picklerespectivement).


Un autre choix populaire consiste à utiliser HDF5 ( pytables ) qui offre des temps d'accès très rapides pour les grands ensembles de données:

store = HDFStore('store.h5')

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

Des stratégies plus avancées sont discutées dans le livre de cuisine .


Depuis 0.13, il y a aussi msgpack qui peut être meilleur pour l'interopérabilité, comme une alternative plus rapide à JSON, ou si vous avez des données objet / texte python (voir cette question ).

Andy Hayden
la source
8
@geekazoid save est déconseillé à to_pickle (ce qui crée un cornichon plutôt qu'un csv, qui est un objet beaucoup plus rapide / différent).
Andy Hayden
9
@geekazoid Dans le cas où les données doivent être transformées après le chargement (c'est-à-dire chaîne / objet dans datetime64), cela devrait être fait à nouveau après le chargement d'un fichier csv enregistré, entraînant une perte de performances. pickle enregistre la trame de données dans son état actuel, ainsi les données et leur format sont préservés. Cela peut entraîner une augmentation massive des performances.
harbun
4
Pickle et HDFStore ne peuvent pas enregistrer plus de 8 Go de trame de données. Existe-t-il des alternatives?
user1700890
1
@ user1700890 essayez de générer à partir de données aléatoires (texte et tableaux) et postez une nouvelle question. Je ne pense pas que cela puisse être juste / soupçonner que nous manquons quelque chose. La nouvelle question obtiendra plus de regards, mais essayez d'inclure / générer un DataFrame qui se reproduit :)
Andy Hayden
1
@YixingLiu vous pouvez changer le mode après le fait stackoverflow.com/a/16249655/1240268
Andy Hayden
100

Bien qu'il y ait déjà quelques réponses, j'ai trouvé une belle comparaison dans laquelle ils ont essayé plusieurs façons de sérialiser les Pandas DataFrames: Stocker efficacement les Pandas DataFrames .

Ils comparent:

  • cornichon: format de données ASCII d'origine
  • cPickle, une bibliothèque C
  • pickle-p2: utilise le nouveau format binaire
  • json: bibliothèque jl standardlib
  • json-no-index: comme json, mais sans index
  • msgpack: alternative JSON binaire
  • CSV
  • hdfstore: format de stockage HDF5

Dans leur expérience, ils ont sérialisé un DataFrame de 1 000 000 lignes avec les deux colonnes testées séparément: l'une avec des données texte, l'autre avec des nombres. Leur avertissement dit:

Vous ne devez pas croire que ce qui suit se généralise à vos données. Vous devriez regarder vos propres données et exécuter vous-même des benchmarks

Le code source du test auquel ils se réfèrent est disponible en ligne . Comme ce code ne fonctionnait pas directement, j'ai apporté quelques modifications mineures, que vous pouvez obtenir ici: serialize.py, j'ai obtenu les résultats suivants:

résultats de comparaison de temps

Ils mentionnent également qu'avec la conversion des données textuelles en données catégorielles , la sérialisation est beaucoup plus rapide. Dans leur test environ 10 fois plus rapide (voir aussi le code de test).

Edit : Les temps plus élevés pour le cornichon que le CSV peuvent être expliqués par le format de données utilisé. Par défaut, pickleutilise une représentation ASCII imprimable, qui génère des ensembles de données plus volumineux. Cependant, comme le montre le graphique, le cornichon utilisant le nouveau format de données binaires (version 2, pickle-p2) a des temps de chargement beaucoup plus faibles.

Quelques autres références:

agold
la source
1
J'ai mis à jour ma réponse pour expliquer votre question. Pour résumer: par défaut, cornichon stocke les données au format ASCII.
agold
1
Ah, merci pour cette explication! À noter, pandas DataFrame .to_pickle semble utiliser le pkl.HIGHEST_PROTOCOL (devrait être 2)
ntg
2
Il semble que le blog lié ci-dessus ( Efficiently Store Pandas DataFrames a été supprimé. J'ai fait mes propres comparaisons avec .to_pickle()(qui utilise le stockage binaire) contre .to_hdf()(sans compression). Le but était la vitesse, la taille du fichier pour HDF était 11x Pickle et le temps de chargement était 5x Pickle. Mes données étaient ~ 5k fichiers de ~ 7k lignes x 6 cols chacun, principalement numérique.
hamx0r
1
La page existe toujours, il vous suffit de supprimer la barre oblique de fin: Stocker efficacement les DataFrames Pandas
IanSR
2
@Mike Williamson, dans mon test, le pickle était 5 fois plus rapide à charger que HDF et prenait également 1/11 de l'espace disque (c'est-à-dire que le hdf était 11 fois plus grand sur le disque et prenait 5 fois plus de temps à charger à partir du disque que le pickle). tout cela était sur python 3 avec pandas 0.22.0.
hamx0r
35

Si je comprends bien, vous utilisez déjà, pandas.read_csv()mais souhaitez accélérer le processus de développement afin que vous n'ayez pas à charger le fichier à chaque fois que vous modifiez votre script, n'est-ce pas? J'ai quelques recommandations:

  1. vous pouvez charger uniquement une partie du fichier CSV en utilisant pandas.read_csv(..., nrows=1000)uniquement le bit supérieur de la table pendant le développement

  2. utilisez ipython pour une session interactive, de telle sorte que vous gardiez la table pandas en mémoire lorsque vous éditez et rechargez votre script.

  3. convertir le csv en une table HDF5

  4. utilisation mise à jourDataFrame.to_feather() et pd.read_feather()pour stocker des données dans le format binaire de plumes compatible R qui est super rapide (entre mes mains, légèrement plus rapide que pandas.to_pickle()sur les données numériques et beaucoup plus rapide sur les données de chaîne).

Vous pourriez également être intéressé par cette réponse sur stackoverflow.

Noé
la source
Savez-vous pourquoi to_featherfonctionnerait bien sur les données de chaîne? J'ai comparé to_pickleet to_featuresur mon dataframe numérique et pickle est environ 3 fois plus rapide.
zyxue
@zyxue bonne question, honnêtement, je n'ai pas beaucoup joué avec les trucs de plumes, donc je n'ai pas de réponse
Noah
20

Pickle fonctionne bien!

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
la source
8
Notez que les fichiers générés ne sont pas des fichiers csv, il est peut-être préférable d'utiliser l'extension .pklcomme suggéré dans la réponse @Andy Haydens.
2015 à 8h23
5

Vous pouvez utiliser un fichier au format plume. C'est extrêmement rapide.

df.to_feather('filename.ft')
Huanyu Liao
la source
Et les données peuvent ensuite être utilisées directement en Rutilisant la featherbibliothèque.
James Hirschorn
4

Pandas DataFrames ont la to_picklefonction qui est utile pour enregistrer un 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
la source
4

Comme déjà mentionné, il existe différentes options et formats de fichiers ( HDF5 , JSON , CSV , parquet , SQL ) pour stocker une trame de données. Cependant, picklen'est pas un citoyen de première classe (selon votre configuration), car:

  1. pickleest un risque potentiel pour la sécurité. Former la documentation Python pour cornichon :

Avertissement Le picklemodule n'est pas protégé contre les données erronées ou malveillantes. Ne décompactez jamais les données reçues d'une source non fiable ou non authentifiée.

  1. pickleest lent. Trouvez ici et ici des repères.

Selon votre configuration / utilisation, les deux limitations ne s'appliquent pas, mais je ne recommanderais pas picklela persistance par défaut pour les trames de données pandas.

Michael Dorner
la source
1

Les formats de fichiers Numpy sont assez rapides pour les données numériques

Je préfère utiliser les fichiers numpy car ils sont rapides et faciles à utiliser. Voici un point de référence simple pour enregistrer et charger une trame de données avec 1 colonne de 1 million de points.

import numpy as np
import pandas as pd

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

en utilisant la %%timeitfonction magique d'ipython

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

la sortie est

100 loops, best of 3: 5.97 ms per loop

pour charger les données dans une trame de données

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

data_df = pd.DataFrame(data)

la sortie est

100 loops, best of 3: 5.12 ms per loop

PAS MAL!

LES INCONVÉNIENTS

Il y a un problème si vous enregistrez le fichier numpy en utilisant python 2, puis essayez d'ouvrir en utilisant python 3 (ou vice versa).

Mark Jay
la source
6
notez que cette solution supprimera tous vos noms de colonnes et changera toutes vos données entières pour
Joseph Garvin
0

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

Les formats du protocole pickle:

La version 0 du protocole est le protocole «lisible par l'homme» d'origine et est rétrocompatible avec les versions antérieures de Python.

Le protocole version 1 est un ancien format binaire qui est également compatible avec les versions antérieures de Python.

La version 2 du protocole a été introduite dans Python 2.3. Il fournit un décapage beaucoup plus efficace des classes de nouveau style. Reportez-vous à PEP 307 pour plus d'informations sur les améliorations apportées par le protocole 2.

La version 3 du protocole a été ajoutée dans Python 3.0. Il prend explicitement en charge les objets octets et ne peut pas être décroché par Python 2.x. Il s'agit du protocole par défaut et du protocole recommandé lorsque la compatibilité avec d'autres versions de Python 3 est requise.

La version 4 du protocole a été ajoutée dans Python 3.4. Il ajoute la prise en charge des très grands objets, le décapage de plus de types d'objets et certaines optimisations de format de données. Reportez-vous à PEP 3154 pour plus d'informations sur les améliorations apportées par le protocole 4.

Gilco
la source
0

compatibilité pyarrow entre les versions

Le mouvement global a été vers pyarrow / plume (avertissements de dépréciation de pandas / msgpack). Cependant, j'ai un défi avec pyarrow avec transitoire dans les spécifications Les données sérialisées avec pyarrow 0.15.1 ne peuvent pas être désérialisées avec 0.16.0 ARROW-7961 . J'utilise la sérialisation pour utiliser redis, je dois donc utiliser un encodage binaire.

J'ai retesté diverses options (en utilisant le cahier 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")

Avec les résultats suivants pour ma trame de données (dans la outvariable 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>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

la plume et le parquet ne fonctionnent pas pour mon bloc de données. Je vais continuer à utiliser pyarrow. Cependant je compléterai avec du cornichon (pas de compression). Lors de l'écriture dans le cache, stockez les formulaires sérialisés pyarrow et pickle. Lors de la lecture à partir du cache de secours pour décaper si la désérialisation pyarrow échoue.

Rob Raymond
la source
Cela ne répond pas à la question
Jason S
0

Le format dépend de votre cas d'utilisation

  • Enregistrez DataFrame entre les sessions de bloc-notes - plume , si vous avez l'habitude de décapage - également ok.
  • Enregistrez DataFrame dans la plus petite taille de fichier possible - parquet ou pickle.gz (vérifiez ce qui est le mieux pour vos données)
  • Enregistrer un très gros DataFrame (plus de 10 millions de lignes) - hdf
  • Être capable de lire les données sur une autre plate-forme (pas Python) qui ne prend pas en charge d'autres formats - csv , csv.gz , vérifiez si le parquet est pris en charge
  • Être capable de réviser avec vos yeux / en utilisant Excel / Google Sheets / Git diff - CSV
  • Enregistrer un DataFrame qui prend presque toute la RAM - CSV

La comparaison des formats de fichiers pandas est dans cette vidéo .

artobie
la source