web-dev-qa-db-fra.com

Enregistrement dans un fichier d'un tableau ou d'un DataFrame avec d'autres informations

Le logiciel statistique Stata permet d'enregistrer de courts extraits de texte dans un ensemble de données. Pour ce faire, utilisez notes et/ou characteristics .

C'est une fonctionnalité très utile pour moi car elle me permet d'enregistrer une variété d'informations, allant des rappels et des listes de tâches aux informations sur la façon dont j'ai généré les données, ou même quelle était la méthode d'estimation pour une variable particulière.

J'essaie maintenant de proposer une fonctionnalité similaire dans Python 3.6. Jusqu'à présent, j'ai regardé en ligne et consulté un certain nombre de messages, qui ne traitent cependant pas exactement de ce que je veux faire.

Quelques publications de référence:

Pour un petit tableau NumPy, j'ai conclu qu'une combinaison de la fonction numpy.savez() et un dictionary peut stocker de manière adéquate toutes les informations pertinentes dans un un seul fichier.

Par exemple:

a = np.array([[2,4],[6,8],[10,12]])
d = {"first": 1, "second": "two", "third": 3}

np.savez(whatever_name.npz, a=a, d=d)
data = np.load(whatever_name.npz)

arr = data['a']
dic = data['d'].tolist()

Cependant, la question demeure:

Existe-t-il de meilleures façons d'incorporer potentiellement d'autres informations dans un fichier contenant un tableau NumPy ou un (grand) PandasDataFrame?

Je suis particulièrement intéressé à entendre parler des pros et contre des suggestions que vous pourriez avoir avec des exemples. Moins il y a de dépendances, mieux c'est.

44
Pearly Spencer

Il existe de nombreuses options. Je ne parlerai que du HDF5, car j'ai de l'expérience avec ce format.

Avantages : Portable (peut être lu en dehors de Python), compression native, capacités de mémoire insuffisante, prise en charge des métadonnées.

Inconvénients : dépendance à une seule API C de bas niveau, possibilité de corruption de données en tant que fichier unique, la suppression de données ne réduit pas automatiquement la taille.

D'après mon expérience, pour les performances et la portabilité, à éviterpyTables/HDFStore pour stocker des données numériques. Vous pouvez à la place utiliser l'interface intuitive fournie par h5py .

Stocker un tableau

import h5py, numpy as np

arr = np.random.randint(0, 10, (1000, 1000))

f = h5py.File('file.h5', 'w', libver='latest')  # use 'latest' for performance

dset = f.create_dataset('array', shape=(1000, 1000), data=arr, chunks=(100, 100)
                        compression='gzip', compression_opts=9)

Compression et segmentation

Il existe de nombreux choix de compression, par ex. blosc et lzf sont de bons choix pour les performances de compression et de décompression respectivement. Remarque gzip est natif; les autres filtres de compression peuvent ne pas être livrés par défaut avec votre installation HDF5.

La segmentation est une autre option qui, lorsqu'elle est alignée sur la façon dont vous lisez les données hors mémoire, peut améliorer considérablement les performances.

Ajoutez quelques attributs

dset.attrs['Description'] = 'Some text snippet'
dset.attrs['RowIndexArray'] = np.arange(1000)

Stockez un dictionnaire

for k, v in d.items():
    f.create_dataset('dictgroup/'+str(k), data=v)

Accès hors mémoire

dictionary = f['dictgroup']
res = dictionary['my_key']

Rien ne remplace la lecture du h5pydocumentation , qui expose la plupart de l'API C, mais vous devriez voir d'après ce qui précède qu'il y a une grande flexibilité.

22
jpp

Je suis d'accord avec JPP que le stockage hdf5 est une bonne option ici. La différence entre sa solution et la mienne est que la mienne utilise Pandas cadres de données au lieu de tableaux numpy. Je préfère le cadre de données car cela permet des types mixtes, une indexation à plusieurs niveaux (même l'indexation datetime, ce qui est TRÈS important pour mon travail) et l'étiquetage des colonnes, ce qui m'aide à me souvenir de la façon dont les différents ensembles de données sont organisés. De plus, Pandas fournit une multitude de fonctionnalités intégrées (un peu comme numpy). Un autre avantage de l'utilisation de Pandas est qu'il a un créateur hdf intégré (c'est-à-dire pandas.DataFrame.to_hdf), que je trouve pratique

Lors du stockage de la trame de données sur h5, vous avez la possibilité de stocker également un dictionnaire de métadonnées, qui peut être vos notes pour vous-même, ou des métadonnées réelles qui n'ont pas besoin d'être stockées dans la trame de données (je l'utilise également pour définir des indicateurs, par exemple {'is_agl': True, 'scale_factor': 100, 'already_corrected': False, etc.}. À cet égard, il n'y a pas de différence entre l'utilisation d'un tableau numpy et d'une trame de données. Pour la solution complète, voir ma question et solution d'origine ici.

7
tnknepp

Un moyen pratique pourrait être d'incorporer des métadonnées directement dans le tableau Numpy. L'avantage est que, comme vous le souhaitez, il n'y a pas de dépendance supplémentaire et il est très simple à utiliser dans le code. Cependant, cela ne répond pas entièrement à votre question, car vous avez toujours besoin d'un mécanisme pour enregistrer les données, et je recommanderais d'utiliser la solution de jpp utilisant HDF5.

Pour inclure des métadonnées dans un ndarray, il y a un exemple dans la documentation . Vous devez essentiellement sous-classer un ndarray et ajouter un champ info ou metadata ou autre.

Cela donnerait (code du lien ci-dessus)

import numpy as np

class ArrayWithInfo(np.ndarray):

    def __new__(cls, input_array, info=None):
        # Input array is an already formed ndarray instance
        # We first cast to be our class type
        obj = np.asarray(input_array).view(cls)
        # add the new attribute to the created instance
        obj.info = info
        # Finally, we must return the newly created object:
        return obj

    def __array_finalize__(self, obj):
        # see InfoArray.__array_finalize__ for comments
        if obj is None: return
        self.info = getattr(obj, 'info', None)

Pour enregistrer les données via numpy, vous devez surcharger la fonction write ou utiliser une autre solution.

5
Christian

la réponse de jpp est assez complète, je voulais juste mentionner qu'à partir de pandas v22 est une option très pratique et rapide avec presque aucun inconvénient vs csv (acceptez peut-être la pause-café).

lire parquet

écrire parquet

Au moment de la rédaction, vous devrez également

pip install pyarrow

En termes d'ajout d'informations, vous disposez des métadonnées qui sont attachées aux données

import pyarrow as pa
import pyarrow.parquet as pq
import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.normal(size=(1000, 10)))

tab = pa.Table.from_pandas(df)

tab = tab.replace_schema_metadata({'here' : 'it is'})

pq.write_table(tab, 'where_is_it.parq')

pq.read_table('where_is_it.parq')
Pyarrow table
0: double
1: double
2: double
3: double
4: double
5: double
6: double
7: double
8: double
9: double
__index_level_0__: int64
metadata
--------
{b'here': b'it is'}

Pour remettre cela aux pandas:

tab.to_pandas()
3
Darren Brien