web-dev-qa-db-fra.com

OverflowError lors de l'enregistrement d'un grand Pandas df en hdf

J'ai un grand Pandas dataframe (~ 15 Go, 83m de lignes) que je souhaite enregistrer en tant que fichier h5 (Ou feather). Une colonne contient longues chaînes d'ID de nombres, qui devraient avoir un type chaîne/objet. Mais même lorsque je m'assure que pandas analyse toutes les colonnes comme object:

df = pd.read_csv('data.csv', dtype=object)
print(df.dtypes)  # sanity check
df.to_hdf('df.h5', 'df')

> client_id                object
  event_id                 object
  account_id               object
  session_id               object
  event_timestamp          object
  # etc...

Je reçois cette erreur:

  File "foo.py", line 14, in <module>
    df.to_hdf('df.h5', 'df')
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/core/generic.py", line 1996, in to_hdf
    return pytables.to_hdf(path_or_buf, key, self, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 279, in to_hdf
    f(store)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 273, in <lambda>
    f = lambda store: store.put(key, value, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 890, in put
    self._write_to_group(key, value, append=append, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 1367, in _write_to_group
    s.write(obj=value, append=append, complib=complib, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 2963, in write
    self.write_array('block%d_values' % i, blk.values, items=blk_items)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 2730, in write_array
    vlarr.append(value)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/tables/vlarray.py", line 547, in append
    self._append(nparr, nobjects)
  File "tables/hdf5extension.pyx", line 2032, in tables.hdf5extension.VLArray._append
OverflowError: value too large to convert to int

Apparemment, il essaie de convertir cela en int de toute façon et échoue.

Lors de l'exécution de df.to_feather() J'ai un problème similaire:

df.to_feather('df.feather')
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/core/frame.py", line 1892, in to_feather
    to_feather(self, fname)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/feather_format.py", line 83, in to_feather
    feather.write_dataframe(df, path)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/feather.py", line 182, in write_feather
    writer.write(df)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/feather.py", line 93, in write
    table = Table.from_pandas(df, preserve_index=False)
  File "pyarrow/table.pxi", line 1174, in pyarrow.lib.Table.from_pandas
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/pandas_compat.py", line 501, in dataframe_to_arrays
    convert_fields))
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 586, in result_iterator
    yield fs.pop().result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
    return self.__get_result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/pandas_compat.py", line 487, in convert_column
    raise e
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/pandas_compat.py", line 481, in convert_column
    result = pa.array(col, type=type_, from_pandas=True, safe=safe)
  File "pyarrow/array.pxi", line 191, in pyarrow.lib.array
  File "pyarrow/array.pxi", line 78, in pyarrow.lib._ndarray_to_array
  File "pyarrow/error.pxi", line 85, in pyarrow.lib.check_status
pyarrow.lib.ArrowInvalid: ('Could not convert 1542852887489 with type str: tried to convert to double', 'Conversion failed for column session_id with type object')

Alors:

  1. Est-ce que quelque chose qui ressemble à un nombre est converti de force en un nombre stocké?
  2. La présence de NaN pourrait-elle affecter ce qui se passe ici?
  3. Existe-t-il une solution de stockage alternative? Quel serait le meilleur?
10
Josh Friedlander

Après avoir fait quelques lectures sur ce sujet, il semble que le problème concerne les colonnes de type string. Mes colonnes string contiennent un mélange de chaînes de nombres entiers et de chaînes de caractères. Pandas a l'option flexible de conserver les chaînes en tant que object, sans type déclaré, mais lors de la sérialisation vers hdf5 Ou feather le contenu de la colonne est convertie en type (str ou double, par exemple) et ne peut pas être mélangée. Ces deux bibliothèques échouent lorsqu'elles sont confrontées à une bibliothèque suffisamment grande de type mixte.

La conversion forcée de ma colonne mixte en chaînes m'a permis de l'enregistrer en plume, mais en HDF5, le fichier a explosé et le processus s'est terminé lorsque j'ai manqué d'espace disque.

Ici est une réponse dans un cas comparable où un commentateur note (il y a 2 ans) "Ce problème est très standard, mais les solutions sont rares".

Quelques antécédents:

Les types de chaîne dans Pandas sont appelés object, mais cela masque qu'il peut s'agir de chaînes pures ou de dtypes mixtes (numpy a des types de chaîne intégrés, mais Pandas ne les utilise jamais pour le texte). Donc la première chose à faire dans un cas comme celui-ci est d'appliquer tous les cols de chaîne comme type de chaîne (avec df[col].astype(str)). Mais même ainsi, dans un fichier suffisamment volumineux (16 Go, avec de longues chaînes), cela a toujours échoué. Pourquoi?

La raison pour laquelle je rencontrais cette erreur était que j'avais des données qui longues et à haute entropie (nombreuses valeurs uniques différentes) chaînes. (Avec des données à faible entropie, cela aurait peut-être valu la peine de passer à categorical dtype.) Dans mon cas, j'ai réalisé que je n'avais besoin que de ces chaînes pour identifier les lignes - afin de pouvoir les remplacer par des entiers uniques !

df[col] = df[col].map(dict(Zip(df[col].unique(), range(df[col].nunique()))))

Autres solutions:

Pour les données texte, il existe d'autres solutions recommandées que hdf5/feather, notamment:

  • json
  • msgpack (notez que dans Pandas 0,25 read_msgpack est déconseillé))
  • pickle (qui a connu problèmes de sécurité , alors soyez prudent - mais cela devrait être OK pour le stockage interne/transfert de trames de données)
  • parquet, qui fait partie de l'écosystème Apache Arrow.

Ici est une réponse de Matthew Rocklin (l'un des développeurs dask) comparant msgpack et pickle. Il a écrit une comparaison plus large sur son blog .

5
Josh Friedlander

HDF5 n'est pas la solution appropriée pour ce cas d'utilisation. hdf5 est une meilleure solution si vous avez plusieurs cadres de données que vous souhaitez stocker dans une seule structure. Il a plus de frais généraux lors de l'ouverture du fichier, puis il vous permet de charger efficacement chaque trame de données et également de charger facilement des tranches d'entre eux. Il doit être considéré comme un système de fichiers qui stocke les trames de données.

Dans le cas d'un événement de trame de données unique de séries chronologiques, les formats recommandés seraient l'un des formats de projet Apache Arrow, à savoir feather ou parquet. On devrait les considérer comme des fichiers csv basés sur des colonnes (compressés). Le compromis particulier entre ces deux est bien présenté sous Quelles sont les différences entre la plume et le parquet? .

Un problème particulier à considérer concerne les types de données. Puisque feather n'est pas conçu pour optimiser l'espace disque par compression, il peut prendre en charge un plus grande variété de types de données . Alors que parquet essaie de fournir une compression très efficace, il ne peut prendre en charge qu'un sous-ensemble limité qui lui permettrait de mieux gérer la compression des données.

3
Alex Fish