web-dev-qa-db-fra.com

Améliorez les performances d'écriture de la table HDF5 pandas (PyTables?)

J'utilise pandas pour la recherche maintenant depuis environ deux mois avec un grand effet. Avec un grand nombre de jeux de données d'événement de trace de taille moyenne, pandas + PyTables ( l'interface HDF5) me permet de traiter des données hétérogènes en utilisant tous les outils Python que je connais et que j'aime).

D'une manière générale, j'utilise le format Fixed (anciennement "Storer") dans PyTables, car mon flux de travail est en écriture unique, en lecture-multiple, et bon nombre de mes ensembles de données sont dimensionnés de sorte que je puisse en charger 50 à 100 en mémoire à un temps sans inconvénients graves. (NB: je fais une grande partie de mon travail sur les machines Opteron de classe serveur avec 128 Go + de mémoire système.)

Cependant, pour les grands ensembles de données (500 Mo et plus), je voudrais pouvoir utiliser les capacités d'accès aléatoire et de requête plus évolutives du format "Tables" de PyTables, afin de pouvoir effectuer mes requêtes sans mémoire, puis charger le jeu de résultats beaucoup plus petit en mémoire pour le traitement. Le gros obstacle ici, cependant, est la performance d'écriture. Oui, comme je l'ai dit, mon flux de travail est en écriture unique, en lecture multiple, mais les temps relatifs sont toujours inacceptables.

À titre d'exemple, j'ai récemment exécuté une grande factorisation Cholesky qui a pris 3 minutes, 8 secondes (188 secondes) sur ma machine à 48 cœurs. Cela a généré un fichier de trace de ~ 2,2 Go - la trace est générée en parallèle avec le programme, il n'y a donc pas de "temps de création de trace" supplémentaire.

La conversion initiale de mon fichier de trace binaire au format pandas/PyTables prend un temps décent, mais en grande partie parce que le format binaire est délibérément hors service afin de réduire l'impact sur les performances du générateur de trace lui-même. Cela n'est pas non plus pertinent pour la perte de performances lors du passage du format Storer au format Table.

Mes tests ont été initialement exécutés avec pandas 0.12, numpy 1.7.1, PyTables 2.4.0 et numexpr 0.20.1. Ma machine à 48 cœurs fonctionne à 2,8 GHz par cœur, et j'écris à un système de fichiers ext3 qui est probablement (mais pas certainement) sur un SSD.

Je peux écrire l'ensemble de données complet dans un fichier HDF5 au format Storer (taille de fichier résultante: 3,3 Go) en 7,1 secondes. Le même ensemble de données, écrit au format Table (la taille du fichier résultant est également de 3,3 Go), prend 178,7 secondes pour être écrit.

Le code est comme suit:

with Timer() as t:
    store = pd.HDFStore('test_storer.h5', 'w')
    store.put('events', events_dataset, table=False, append=False)
print('Fixed format write took ' + str(t.interval))
with Timer() as t:
    store = pd.HDFStore('test_table.h5', 'w')
    store.put('events', events_dataset, table=True, append=False)
print('Table format write took ' + str(t.interval))

et la sortie est tout simplement

Fixed format write took 7.1
Table format write took 178.7

Mon ensemble de données comporte 28 880 943 lignes et les colonnes sont des types de données de base:

node_id           int64
thread_id         int64
handle_id         int64
type              int64
begin             int64
end               int64
duration          int64
flags             int64
unique_id         int64
id                int64
DSTL_LS_FULL    float64
L2_DMISS        float64
L3_MISS         float64
kernel_type     float64
dtype: object

... donc je ne pense pas qu'il devrait y avoir de problèmes spécifiques aux données avec la vitesse d'écriture.

J'ai également essayé d'ajouter la compression BLOSC, pour exclure tout problème d'E/S étrange qui pourrait affecter un scénario ou l'autre, mais la compression semble diminuer les performances des deux de manière égale.

Maintenant, je me rends compte que la documentation pandas dit que le format Storer offre des écritures beaucoup plus rapides et des lectures légèrement plus rapides. (Je fais l'expérience des lectures plus rapides, car une lecture du format Storer semble prendre environ 2,5 secondes, tandis qu'une lecture du format Table prend environ 10 secondes.) Mais il semble vraiment excessif que l'écriture au format Table prenne 25 fois plus de temps que l'écriture au format Storer.

L'une des personnes impliquées dans PyTables ou pandas peut-elle expliquer les raisons architecturales (ou autres) pour lesquelles l'écriture au format interrogeable (qui nécessite clairement très peu de données supplémentaires) devrait prendre un ordre de grandeur plus long? Et y a-t-il un espoir pour améliorer cela à l'avenir? Je serais ravi de contribuer à l'un ou l'autre projet, car mon domaine est l'informatique haute performance et je vois un cas d'utilisation significatif pour les deux projets dans ce domaine .. .. mais il serait utile d'obtenir des éclaircissements sur les problèmes impliqués en premier, et/ou des conseils sur la façon d'accélérer les choses de ceux qui savent comment le système est construit.

ÉDITER:

L'exécution des anciens tests avec% prun dans IPython donne la sortie de profil suivante (quelque peu réduite pour la lisibilité) pour le format Storer/Fixed:

%prun -l 20 profile.events.to_hdf('test.h5', 'events', table=False, append=False)

3223 function calls (3222 primitive calls) in 7.385 seconds

Ordered by: internal time
List reduced from 208 to 20 due to restriction <20>

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    6    7.127    1.188    7.128    1.188 {method '_createArray' of 'tables.hdf5Extension.Array' objects}
    1    0.242    0.242    0.242    0.242 {method '_closeFile' of 'tables.hdf5Extension.File' objects}
    1    0.003    0.003    0.003    0.003 {method '_g_new' of 'tables.hdf5Extension.File' objects}
   46    0.001    0.000    0.001    0.000 {method 'reduce' of 'numpy.ufunc' objects}

et les éléments suivants pour le format Tables:

   %prun -l 40 profile.events.to_hdf('test.h5', 'events', table=True, append=False, chunksize=1000000)

   499082 function calls (499040 primitive calls) in 188.981 seconds

   Ordered by: internal time
   List reduced from 526 to 40 due to restriction <40>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       29   92.018    3.173   92.018    3.173 {pandas.lib.create_hdf_rows_2d}
      640   20.987    0.033   20.987    0.033 {method '_append' of 'tables.hdf5Extension.Array' objects}
       29   19.256    0.664   19.256    0.664 {method '_append_records' of 'tables.tableExtension.Table' objects}
      406   19.182    0.047   19.182    0.047 {method '_g_writeSlice' of 'tables.hdf5Extension.Array' objects}
    14244   10.646    0.001   10.646    0.001 {method '_g_readSlice' of 'tables.hdf5Extension.Array' objects}
      472   10.359    0.022   10.359    0.022 {method 'copy' of 'numpy.ndarray' objects}
       80    3.409    0.043    3.409    0.043 {tables.indexesExtension.keysort}
        2    3.023    1.512    3.023    1.512 common.py:134(_isnull_ndarraylike)
       41    2.489    0.061    2.533    0.062 {method '_fillCol' of 'tables.tableExtension.Row' objects}
       87    2.401    0.028    2.401    0.028 {method 'astype' of 'numpy.ndarray' objects}
       30    1.880    0.063    1.880    0.063 {method '_g_flush' of 'tables.hdf5Extension.Leaf' objects}
      282    0.824    0.003    0.824    0.003 {method 'reduce' of 'numpy.ufunc' objects}
       41    0.537    0.013    0.668    0.016 index.py:607(final_idx32)
    14490    0.385    0.000    0.712    0.000 array.py:342(_interpret_indexing)
       39    0.279    0.007   19.635    0.503 index.py:1219(reorder_slice)
        2    0.256    0.128   10.063    5.031 index.py:1099(get_neworder)
        1    0.090    0.090  119.392  119.392 pytables.py:3016(write_data)
    57842    0.087    0.000    0.087    0.000 {numpy.core.multiarray.empty}
    28570    0.062    0.000    0.107    0.000 utils.py:42(is_idx)
    14164    0.062    0.000    7.181    0.001 array.py:711(_readSlice)

EDIT 2:

En réexécutant avec une copie préliminaire de pandas 0,13 (tiré le 20 novembre 2013 vers 11 h 00 HNE), les temps d'écriture pour le format Tables s'améliorent considérablement mais ne se comparent toujours pas "raisonnablement" aux vitesses d'écriture du format Storer/Fixed.

%prun -l 40 profile.events.to_hdf('test.h5', 'events', table=True, append=False, chunksize=1000000)

         499748 function calls (499720 primitive calls) in 117.187 seconds

   Ordered by: internal time
   List reduced from 539 to 20 due to restriction <20>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      640   22.010    0.034   22.010    0.034 {method '_append' of 'tables.hdf5Extension.Array' objects}
       29   20.782    0.717   20.782    0.717 {method '_append_records' of 'tables.tableExtension.Table' objects}
      406   19.248    0.047   19.248    0.047 {method '_g_writeSlice' of 'tables.hdf5Extension.Array' objects}
    14244   10.685    0.001   10.685    0.001 {method '_g_readSlice' of 'tables.hdf5Extension.Array' objects}
      472   10.439    0.022   10.439    0.022 {method 'copy' of 'numpy.ndarray' objects}
       30    7.356    0.245    7.356    0.245 {method '_g_flush' of 'tables.hdf5Extension.Leaf' objects}
       29    7.161    0.247   37.609    1.297 pytables.py:3498(write_data_chunk)
        2    3.888    1.944    3.888    1.944 common.py:197(_isnull_ndarraylike)
       80    3.581    0.045    3.581    0.045 {tables.indexesExtension.keysort}
       41    3.248    0.079    3.294    0.080 {method '_fillCol' of 'tables.tableExtension.Row' objects}
       34    2.744    0.081    2.744    0.081 {method 'ravel' of 'numpy.ndarray' objects}
      115    2.591    0.023    2.591    0.023 {method 'astype' of 'numpy.ndarray' objects}
      270    0.875    0.003    0.875    0.003 {method 'reduce' of 'numpy.ufunc' objects}
       41    0.560    0.014    0.732    0.018 index.py:607(final_idx32)
    14490    0.387    0.000    0.712    0.000 array.py:342(_interpret_indexing)
       39    0.303    0.008   19.617    0.503 index.py:1219(reorder_slice)
        2    0.288    0.144   10.299    5.149 index.py:1099(get_neworder)
    57871    0.087    0.000    0.087    0.000 {numpy.core.multiarray.empty}
        1    0.084    0.084   45.266   45.266 pytables.py:3424(write_data)
        1    0.080    0.080   55.542   55.542 pytables.py:3385(write)

J'ai remarqué lors de l'exécution de ces tests qu'il y a de longues périodes où l'écriture semble "s'arrêter" (le fichier sur le disque ne se développe pas activement), et pourtant il y a aussi une faible utilisation du processeur pendant certaines de ces périodes.

Je commence à soupçonner que certaines limitations ext3 connues peuvent mal interagir avec pandas ou PyTables. Ext3 et d'autres systèmes de fichiers sans extension ont parfois du mal à dissocier rapidement les fichiers volumineux et des performances système similaires (faible L'utilisation du processeur, mais de longs temps d'attente) est évidente, même lors d'une simple `` rm '' d'un fichier de 1 Go, par exemple.

Pour clarifier, dans chaque cas de test, je me suis assuré de supprimer le fichier existant, le cas échéant, avant de commencer le test, afin de ne pas encourir de pénalité de suppression/écrasement de fichier ext3.

Cependant, lors de la réexécution de ce test avec index = Aucun, les performances s'améliorent considérablement (~ 50 s contre ~ 120 lors de l'indexation). Il semblerait donc que ce processus soit toujours lié au processeur (mon système a des processeurs AMD Opteron Istanbul relativement anciens fonctionnant à 2,8 GHz, bien qu'il ait également 8 sockets avec 6 processeurs principaux dans chacun, tous sauf un, parmi lesquels bien sûr, restez inactif pendant l'écriture), ou qu'il y a un conflit entre la façon dont PyTables ou pandas tente de manipuler/lire/analyser le fichier lorsqu'il est déjà partiellement ou complètement sur le système de fichiers, ce qui provoque pathologiquement mauvais comportement d'E/S lors de l'indexation.

EDIT 3:

Les tests suggérés par @ Jeff sur un ensemble de données plus petit (1,3 Go sur le disque), après la mise à niveau de PyTables de 2.4 à 3.0.0, m'ont amené ici:

In [7]: %timeit f(df)
1 loops, best of 3: 3.7 s per loop

In [8]: %timeit f2(df) # where chunksize= 2 000 000
1 loops, best of 3: 13.8 s per loop

In [9]: %timeit f3(df) # where chunksize= 2 000 000
1 loops, best of 3: 43.4 s per loop

En fait, ma performance semble battre la sienne dans tous les scénarios, sauf lorsque l'indexation est activée (par défaut). Cependant, l'indexation semble toujours être un tueur, et si la façon dont j'interprète la sortie de top et ls lorsque j'exécute ces tests est correcte, il reste des périodes où il y a ni traitement significatif ni écriture de fichier (c.-à-d. l'utilisation du processeur pour le processus Python est proche de 0 et la taille du fichier reste constante). Je ne peux que supposer qu'il s'agit de lectures de fichiers. Pourquoi les lectures de fichiers être à l'origine de ralentissements est difficile à comprendre pour moi, car je peux charger de manière fiable un fichier entier de 3 Go à partir de ce disque dans la mémoire en moins de 3 secondes. S'ils ne sont pas des fichiers lus, alors qu'est-ce que le système `` attend ''? ( Personne d'autre n'est connecté à la machine et il n'y a aucune autre activité du système de fichiers.)

À ce stade, avec les versions mises à niveau des modules python pertinents), les performances de mon jeu de données d'origine sont réduites aux chiffres suivants. Le temps système, qui je suppose, est au moins un limite supérieure sur le temps passé à effectuer les E/S et le temps du mur, qui semble peut-être expliquer ces périodes mystérieuses sans écriture/sans activité du processeur.

In [28]: %time f(profile.events)
CPU times: user 0 ns, sys: 7.16 s, total: 7.16 s
Wall time: 7.51 s

In [29]: %time f2(profile.events)
CPU times: user 18.7 s, sys: 14 s, total: 32.7 s
Wall time: 47.2 s

In [31]: %time f3(profile.events)
CPU times: user 1min 18s, sys: 14.4 s, total: 1min 32s
Wall time: 2min 5s

Néanmoins, il semblerait que l'indexation entraîne un ralentissement important pour mon cas d'utilisation. Peut-être devrais-je essayer de limiter les champs indexés au lieu de simplement effectuer le cas par défaut (qui peut très bien être l'indexation sur tous les champs dans le DataFrame)? Je ne sais pas comment cela est susceptible d'affecter les temps de requête, en particulier dans les cas où une requête sélectionne en fonction d'un champ non indexé.

Selon la demande de Jeff, un ptdump du fichier résultant.

ptdump -av test.h5
/ (RootGroup) ''
  /._v_attrs (AttributeSet), 4 attributes:
   [CLASS := 'GROUP',
    PYTABLES_FORMAT_VERSION := '2.1',
    TITLE := '',
    VERSION := '1.0']
/df (Group) ''
  /df._v_attrs (AttributeSet), 14 attributes:
   [CLASS := 'GROUP',
    TITLE := '',
    VERSION := '1.0',
    data_columns := [],
    encoding := None,
    index_cols := [(0, 'index')],
    info := {1: {'type': 'Index', 'names': [None]}, 'index': {}},
    levels := 1,
    nan_rep := 'nan',
    non_index_axes := 
    [(1, ['node_id', 'thread_id', 'handle_id', 'type', 'begin', 'end', 'duration', 'flags', 'unique_id', 'id', 'DSTL_LS_FULL', 'L2_DMISS', 'L3_MISS', 'kernel_type'])],
    pandas_type := 'frame_table',
    pandas_version := '0.10.1',
    table_type := 'appendable_frame',
    values_cols := ['values_block_0', 'values_block_1']]
/df/table (Table(28880943,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": Int64Col(shape=(10,), dflt=0, pos=1),
  "values_block_1": Float64Col(shape=(4,), dflt=0.0, pos=2)}
  byteorder := 'little'
  chunkshape := (4369,)
  autoindex := True
  colindexes := {
    "index": Index(6, medium, shuffle, zlib(1)).is_csi=False}
  /df/table._v_attrs (AttributeSet), 15 attributes:
   [CLASS := 'TABLE',
    FIELD_0_FILL := 0,
    FIELD_0_NAME := 'index',
    FIELD_1_FILL := 0,
    FIELD_1_NAME := 'values_block_0',
    FIELD_2_FILL := 0.0,
    FIELD_2_NAME := 'values_block_1',
    NROWS := 28880943,
    TITLE := '',
    VERSION := '2.7',
    index_kind := 'integer',
    values_block_0_dtype := 'int64',
    values_block_0_kind := ['node_id', 'thread_id', 'handle_id', 'type', 'begin', 'end', 'duration', 'flags', 'unique_id', 'id'],
    values_block_1_dtype := 'float64',
    values_block_1_kind := ['DSTL_LS_FULL', 'L2_DMISS', 'L3_MISS', 'kernel_type']]

et un autre% prun avec les modules mis à jour et l'ensemble de données complet:

%prun -l 25  %time f3(profile.events)
CPU times: user 1min 14s, sys: 16.2 s, total: 1min 30s
Wall time: 1min 48s

        542678 function calls (542650 primitive calls) in 108.678 seconds

   Ordered by: internal time
   List reduced from 629 to 25 due to restriction <25>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      640   23.633    0.037   23.633    0.037 {method '_append' of 'tables.hdf5extension.Array' objects}
       15   20.852    1.390   20.852    1.390 {method '_append_records' of 'tables.tableextension.Table' objects}
      406   19.584    0.048   19.584    0.048 {method '_g_write_slice' of 'tables.hdf5extension.Array' objects}
    14244   10.591    0.001   10.591    0.001 {method '_g_read_slice' of 'tables.hdf5extension.Array' objects}
      458    9.693    0.021    9.693    0.021 {method 'copy' of 'numpy.ndarray' objects}
       15    6.350    0.423   30.989    2.066 pytables.py:3498(write_data_chunk)
       80    3.496    0.044    3.496    0.044 {tables.indexesextension.keysort}
       41    3.335    0.081    3.376    0.082 {method '_fill_col' of 'tables.tableextension.Row' objects}
       20    2.551    0.128    2.551    0.128 {method 'ravel' of 'numpy.ndarray' objects}
      101    2.449    0.024    2.449    0.024 {method 'astype' of 'numpy.ndarray' objects}
       16    1.789    0.112    1.789    0.112 {method '_g_flush' of 'tables.hdf5extension.Leaf' objects}
        2    1.728    0.864    1.728    0.864 common.py:197(_isnull_ndarraylike)
       41    0.586    0.014    0.842    0.021 index.py:637(final_idx32)
    14490    0.292    0.000    0.616    0.000 array.py:368(_interpret_indexing)
        2    0.283    0.142   10.267    5.134 index.py:1158(get_neworder)
      274    0.251    0.001    0.251    0.001 {method 'reduce' of 'numpy.ufunc' objects}
       39    0.174    0.004   19.373    0.497 index.py:1280(reorder_slice)
    57857    0.085    0.000    0.085    0.000 {numpy.core.multiarray.empty}
        1    0.083    0.083   35.657   35.657 pytables.py:3424(write_data)
        1    0.065    0.065   45.338   45.338 pytables.py:3385(write)
    14164    0.065    0.000    7.831    0.001 array.py:615(__getitem__)
    28570    0.062    0.000    0.108    0.000 utils.py:47(is_idx)
       47    0.055    0.001    0.055    0.001 {numpy.core.multiarray.arange}
    28570    0.050    0.000    0.090    0.000 leaf.py:397(_process_range)
    87797    0.048    0.000    0.048    0.000 {isinstance}
32
Peter Gaultney

Voilà une discussion intéressante. Je pense que Peter obtient des performances impressionnantes pour le format fixe parce que le format écrit en un seul coup et aussi qu'il a un très bon SSD (il peut écrire à plus de 450 Mo/s).

L'ajout à la table est une opération plus complexe (l'ensemble de données doit être agrandi et de nouveaux enregistrements doivent être vérifiés afin que nous puissions nous assurer qu'ils suivent le schéma de la table). C'est pourquoi l'ajout de lignes dans les tableaux est généralement plus lent (mais quand même, Jeff obtient ~ 70 Mo/s, ce qui est plutôt bien). Le fait que Jeff gagne plus de vitesse que Peter est probablement dû au fait qu'il a un meilleur processeur.

Enfin, l'indexation dans PyTables utilise un seul processeur, oui, et c'est normalement une opération coûteuse, vous devez donc vraiment le désactiver si vous ne souhaitez pas interroger les données sur le disque.

5
Francesc

Voici une comparaison similaire que je viens de faire. C'est environ 1/3 des 10 millions de lignes de données. La taille finale est d'environ 1,3 Go

Je définis 3 fonctions de chronométrage:

Testez le format fixe (appelé Storer dans 0.12). Cela écrit dans un format de tableau PyTables

def f(df):
    store = pd.HDFStore('test.h5','w')
    store['df'] = df
    store.close()

Écrivez au format Tableau, en utilisant le format Tableau PyTables. Ne créez pas d'index.

def f2(df):
    store = pd.HDFStore('test.h5','w')
    store.append('df',df,index=False)
    store.close()

Identique à f2, mais crée un index (ce qui est normalement fait)

def f3(df):
    store = pd.HDFStore('test.h5','w')
    store.append('df',df)
    store.close()

Créer le cadre

In [25]: df = concat([DataFrame(np.random.randn(10000000,10)),DataFrame(np.random.randint(0,10,size=50000000).reshape(10000000,5))],axis=1)

In [26]: df
Out[26]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 10000000 entries, 0 to 9999999
Columns: 15 entries, 0 to 4
dtypes: float64(10), int64(5)


v0.12.0

In [27]: %timeit f(df)
1 loops, best of 3: 14.7 s per loop

In [28]: %timeit f2(df)
1 loops, best of 3: 32 s per loop

In [29]: %timeit f3(df)
1 loops, best of 3: 40.1 s per loop

master/v0.13.0

In [5]: %timeit f(df)
1 loops, best of 3: 12.9 s per loop

In [6]: %timeit f2(df)
1 loops, best of 3: 17.5 s per loop

In [7]: %timeit f3(df)
1 loops, best of 3: 24.3 s per loop

Timing s'exécute avec le même fichier que celui fourni par l'OP (lien ci-dessous)

In [4]: df = pd.read_hdf('test.h5','df')

In [5]: df
Out[5]: 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 28880943 entries, 0 to 28880942
Columns: 14 entries, node_id to kernel_type
dtypes: float64(4), int64(10)

Comme f1, format fixe

In [6]: %timeit df.to_hdf('test.hdf','df',mode='w')
1 loops, best of 3: 36.2 s per loop

Comme f2, format de tableau, pas d'index

In [7]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',index=False)
1 loops, best of 3: 45 s per loop

In [8]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',index=False,chunksize=2000000)
1 loops, best of 3: 44.5 s per loop

Comme f3, format de table avec index

In [9]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',chunksize=2000000)
1 loops, best of 3: 1min 36s per loop

Comme f3, format de table avec index, compressé avec blosc

In [10]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',chunksize=2000000,complib='blosc')
1 loops, best of 3: 46.5 s per loop

In [11]: %timeit pd.read_hdf('test.hdf','df')
1 loops, best of 3: 10.8 s per loop

Afficher le fichier d'origine (test.h5 et compressé, test.hdf)

In [13]: !ls -ltr test.h*
-rw-r--r-- 1 jreback users 3471518282 Nov 20 18:20 test.h5
-rw-rw-r-- 1 jreback users  649327780 Nov 20 21:17 test.hdf

Plusieurs points à noter.

  • Ne pas créer d'index peut faire une différence de temps non négligeable. Je crois également que si vous avez un index basé sur des chaînes, cela peut considérablement allonger le temps d'écriture. Cela dit, vous voulez toujours créer un index pour rendre la récupération très rapide.

    Vous n'avez pas inclus ce qu'est votre index, ni s'il est trié (même si je pense que cela fait une petite différence).

  • la pénalité en écriture dans mes exemples est à peu près 2x (bien que je l'ai vue être un peu plus grande en INCLUANT le temps d'indexation). Ainsi vos 7 (1/2 de mon temps), pour 3x le nombre que j'écris est assez suspect. J'utilise une matrice de disques assez rapide. Si vous utilisiez un disque flash, cela est cependant possible.

  • master/v0.13.0 (sortie très prochainement), améliore considérablement les temps d'écriture sur les tables.

  • vous pouvez essayer de définir le paramètre chunksize sur un nombre plus élevé lorsque vous écrivez les données (sa valeur par défaut est 100000). Le but du nombre "relativement" faible est d'avoir une utilisation constante de la mémoire. (Par exemple, si est plus grand, vous utiliserez plus de mémoire, en théorie, il devrait cependant écrire plus rapidement).

  • Les tableaux offrent 2 avantages par rapport au format fixe: 1) la récupération des requêtes et 2) l'adaptabilité. La lecture de la table entière ne profite pas non plus, donc si vous voulez UNIQUEMENT lire la table entière, le format fixe est recommandé. (D'après mon expérience, la flexibilité des tableaux surpasse considérablement la pénalité d'écriture, mais YMMV)

L'essentiel est de répéter les timings (utilisez ipython car il exécutera plusieurs tests). Si vous pouvez reproduire vos résultats, veuillez poster un% prun et je vais y jeter un œil.

Mettre à jour:

donc la méthode recommandée pour une table de cette taille est de compresser avec blosc et d'utiliser pandas master/0.13.0 avec PyTables 3.0.0

9
Jeff