web-dev-qa-db-fra.com

Format de fichier le plus rapide pour les opérations de lecture/écriture avec pandas et/ou Numpy

Je travaille depuis un certain temps avec de très grands DataFrames et j'utilise le format CSV pour stocker les données d'entrée et les résultats. J'ai remarqué que beaucoup de temps est consacré à la lecture et à l'écriture de ces fichiers, ce qui, par exemple, ralentit considérablement le traitement par lots de données. Je me demandais si le format de fichier lui-même est pertinent. Existe-t-il un format de fichier préféré pour une lecture/écriture plus rapide des Pandas DataFrames et/ou des tableaux Numpy?

15
c_david

Utilisez HDF5. Beats écrivant des fichiers plats haut la main. Et vous pouvez interroger. Les documents sont ici

Voici une comparaison perf vs SQL . Mis à jour pour afficher SQL/HDF_fixed/HDF_table/CSV en écriture et en lecture perfs.

Les documents incluent maintenant une section performance:

Voir ici

16
Jeff

C'est toujours une bonne idée d'exécuter des tests de performance pour votre cas d'utilisation. J'ai eu de bons résultats en stockant des structures brutes via numpy:

df.to_records().astype(mytype).tofile('mydata')
df = pd.DataFrame.from_records(np.fromfile('mydata', dtype=mytype))

Il est assez rapide et prend moins d’espace sur le disque. Mais vous devrez garder une trace du type de données pour recharger les données, ce n'est pas portable entre les architectures et il ne supporte pas les fonctionnalités avancées de HDF5. (numpy a un format binaire plus avancé qui est conçu pour surmonter les deux premières limitations, mais je n'ai pas eu beaucoup de succès à le faire fonctionner.)

Mise à jour: Merci de m'appuyer pour les chiffres. Mon repère indique que HDF5 gagne, du moins dans mon cas. C'est les deux plus rapide et plus petit sur le disque! Voici ce que je vois avec un cadre de données d'environ 280 000 lignes, 7 colonnes flottantes et un index de chaîne:

In [15]: %timeit df.to_hdf('test_fixed.hdf', 'test', mode='w')
10 loops, best of 3: 172 ms per loop
In [17]: %timeit df.to_records().astype(mytype).tofile('raw_data')
1 loops, best of 3: 283 ms per loop
In [20]: %timeit pd.read_hdf('test_fixed.hdf', 'test')
10 loops, best of 3: 36.9 ms per loop
In [22]: %timeit pd.DataFrame.from_records(np.fromfile('raw_data', dtype=mytype))
10 loops, best of 3: 40.7 ms per loop
In [23]: ls -l raw_data test_fixed.hdf
-rw-r----- 1 altaurog altaurog 18167232 Apr  8 12:42 raw_data
-rw-r----- 1 altaurog altaurog 15537704 Apr  8 12:41 test_fixed.hdf
9

HDF est en effet un très bon choix, vous pouvez également utiliser npy/npz avec quelques réserves:

Voici un point de repère utilisant un bloc de données de 25 000 lignes et 1 000 colonnes remplies de valeurs flottantes aléatoires:

Saving to HDF took 0.49s
Saving to npy took 0.40s
Loading from HDF took 0.10s
Loading from npy took 0.061s

npy est environ 20% plus rapide à écrire et environ 40% plus rapide à lire si vous ne compressez pas les données.

Code utilisé pour générer la sortie ci-dessus:

#!/usr/bin/python3

import pandas as pd
import random
import numpy as np
import time

start = time.time()
f = pd.DataFrame()
for i in range(1000):
  f['col_{}'.format(i)] = np.random.Rand(25000)
print('Generating data took {}s'.format(time.time() - start))

start = time.time()
f.to_hdf('frame.hdf', 'main', format='fixed')
print('Saving to HDF took {}s'.format(time.time() - start))

start = time.time()
np.savez('frame.npz', f.index, f.values)
print('Saving to npy took {}s'.format(time.time() - start))

start = time.time()
pd.read_hdf('frame.hdf')
print('Loading from HDF took {}s'.format(time.time() - start))

start = time.time()
index, values = np.load('frame.npz')
pd.DataFrame(values, index=index)
print('Loading from npy took {}s'.format(time.time() - start))
4
rahenri

Récemment, les pandas ont ajouté un support pour le format de parquet en utilisant comme base la bibliothèque pyarrow (écrite par Wes Mckinney lui-même, avec son obsession habituelle pour la performance ).

Vous devez uniquement installer la bibliothèque pyarrow et utiliser les méthodes read_parquet et to_parquet. Le parquet est beaucoup plus rapide à lire et à écrire pour des jeux de données plus volumineux (plus de quelques centaines de mégaoctets ou plus) et il garde également une trace des métadonnées dtype afin que vous ne perdiez pas les informations de type de données lors de l'écriture et de la lecture à partir du disque. En fait, il peut stocker plus efficacement certains types de données avec lesquels HDF5 n'est pas très performant (comme les chaînes et les horodatages: HDF5 n'a pas de type de données natif pour ceux-ci, il utilise donc pickle pour les sérialiser, ce qui ralentit les grands ensembles de données).

Pickle est également un format en colonnes, ce qui facilite très bien la réalisation de deux tâches:

  • Filtrez rapidement les colonnes qui ne vous intéressent pas. Avec le format CSV, vous devez réellement lire le fichier dans son intégralité. Vous pourrez ensuite supprimer les colonnes non souhaitées. Avec parquet, vous ne pouvez actuellement lire que les colonnes qui vous intéressent.

  • Faites des requêtes en filtrant les lignes et en ne lisant que ce qui vous intéresse.

Un autre développement récent intéressant est le format de fichier Feather, également développé par Wes Mckinney. Il s’agit essentiellement d’un format arrow non compressé écrit directement sur le disque. Il est donc potentiellement plus rapide à écrire que le format Parquet. L'inconvénient sera les fichiers 2-3 fois plus gros.

0