Ajout de méta-informations / métadonnées à Pandas DataFrame


90

Est-il possible d'ajouter des méta-informations / métadonnées à un pandas DataFrame?

Par exemple, le nom de l'instrument utilisé pour mesurer les données, l'instrument responsable, etc.

Une solution de contournement serait de créer une colonne avec ces informations, mais il semble inutile de stocker une seule information dans chaque ligne!


Veuillez noter la réponse @ryanjdillon (actuellement enterrée vers le bas) qui mentionne l'attribut expérimental mis à jour 'attrs' qui semble être un début, peut
JohnE

Réponses:


85

Bien sûr, comme la plupart des objets Python, vous pouvez attacher de nouveaux attributs à un pandas.DataFrame:

import pandas as pd
df = pd.DataFrame([])
df.instrument_name = 'Binky'

Notez toutefois que si vous pouvez attacher des attributs à une trame de données, les opérations effectuées sur le dataframe ( par exemple groupby, pivot, joinou locpour ne citer que quelques - uns) peut retourner une nouvelle trame de données sans les métadonnées ci - joint. Pandas ne dispose pas encore d'une méthode robuste de propagation des métadonnées attachées aux DataFrames .

La conservation des métadonnées dans un fichier est possible. Vous pouvez trouver un exemple de stockage de métadonnées dans un fichier HDF5 ici .


5
+1 pour votre choix du nom de l'instrument! Avez-vous une expérience en essayant de vider ces attributs supplémentaires dans HDFStore?
Dan Allan

4
@DanAllan: Si store = pd.HDFStore(...), alors les attributs peuvent être stockés avec store.root._v_attrs.key = value.
unutbu

3
À toute autre personne qui pourrait utiliser ceci: la documentation a ajouté une section à ce sujet. pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore
Dan Allan


4
Dans pandas 0.23.1, créer un nouvel attribut en assignant un dictionnaire, une liste ou un tuple donne un avertissement (c'est-à-dire df = pd.DataFrame(); df.meta = {}produit UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access). (Aucun avertissement n'est donné si l'attribut a déjà été créé comme dans df = pd.DataFrame(); df.meta = ''; df.meta = {}).
teichert

13

Je viens de rencontrer ce problème moi-même. Depuis pandas 0.13, les DataFrames ont un attribut _metadata sur eux qui persiste à travers les fonctions qui renvoient de nouvelles DataFrames. Semble également survivre à la sérialisation (je n'ai essayé que json, mais j'imagine que hdf est également couvert).


16
_metadatane fait pas partie de l'API publique, je déconseille donc fortement de s'appuyer sur cette fonctionnalité.
shoyer

@Stephan pouvez-vous élaborer sur cela s'il vous plaît? Pourquoi est-il important de faire partie de l'API publique? Votre affirmation est-elle également vraie pour la version 0.15?
TomCho

1
@TomCho oui, cette réponse est toujours vraie aujourd'hui. Vous pouvez jeter un œil à xray ( github.com/xray/xray ) pour un autre exemple de tableau étiqueté prenant en charge les métadonnées, surtout si vous avez des données multidimensionnelles ( .attrsfait partie de l'API xray)
shoyer

17
_metadataest en fait un attribut de classe, pas un attribut d'instance. Ainsi, les nouvelles DataFrameinstances héritent des précédentes, tant que le module reste chargé. Ne l'utilisez _metadatapour rien. +1 pour xarray!
j08lue

1
_metadata - une fonctionnalité non prise en charge qui m'a sauvé la journée! Merci.
joctee

12

Pas vraiment. Bien que vous puissiez ajouter des attributs contenant des métadonnées à la classe DataFrame comme le mentionne @unutbu, de nombreuses méthodes DataFrame renvoient un nouveau DataFrame, vos métadonnées seraient donc perdues. Si vous devez manipuler votre dataframe, la meilleure option serait d'encapsuler vos métadonnées et DataFrame dans une autre classe. Voir cette discussion sur GitHub: https://github.com/pydata/pandas/issues/2485

Il existe actuellement une demande d'extraction ouverte pour ajouter un objet MetaDataFrame, qui prendrait mieux en charge les métadonnées.


11

Depuis pandas 1.0, peut-être plus tôt, il existe désormais une Dataframe.attrspropriété. C'est expérimental, mais c'est probablement ce que vous voudrez à l'avenir. Par exemple:

import pandas as pd
df = pd.DataFrame([])
df.attrs['instrument_name'] = 'Binky'

Trouvez-le dans la documentation ici .

Essayer cela avec to_parquetpuis from_parquet, cela ne semble pas persister, alors assurez-vous de vérifier cela avec votre cas d'utilisation.


Ceci est intéressant et semble persister pour copy / loc / iloc, mais pas pour groupby.
JohnE

Juste une suggestion, mais peut-être montrer un exemple de la façon de l'utiliser? La documentation n'est fondamentalement rien, mais juste en jouant avec elle, je peux voir qu'elle est initialisée comme un dictionnaire vide et qu'elle semble être configurée de manière à ce que ce soit un dictionnaire bien que bien sûr on puisse y imbriquer une liste, par exemple.
JohnE

1
Vous pouvez trouver cette discussion Stackoverflow utile car elle montre comment ajouter des métadonnées personnalisées aux fichiers parquet si nécessaire
rdmolony

1
@rdmolony C'est super. Je pense que l'utilisation d'un dataclasspour les métadonnées, puis le sous-classement DataFramepour avoir une méthode de chargement / déchargement comme dans le post que vous avez partagé pourrait être une bonne solution.
ryanjdillon

1
C'est sympa. Contrairement à la réponse acceptée, cela préserve les attributs après l'enregistrement et le chargement à partir de pickle!
CGFoX

8

La réponse principale consistant à attacher des attributs arbitraires à l'objet DataFrame est bonne, mais si vous utilisez un dictionnaire, une liste ou un tuple, il émettra une erreur "Pandas ne permet pas la création de colonnes via un nouveau nom d'attribut". La solution suivante fonctionne pour stocker des attributs arbitraires.

from types import SimpleNamespace
df = pd.DataFrame()
df.meta = SimpleNamespace()
df.meta.foo = [1,2,3]

De plus, si vous souhaitez que cela persiste sur les copies de votre dataframe, vous devez le faire pd.DataFrame._metadata += ["meta"]. Notez que cette partie est un attribut de Pandas, pas un attribut de votre dataframe spécifique
bscan

Cette approche ne fonctionnera plus car elle df.metadéclenche un avertissement indiquant que Pandas n'autorise pas la génération de nouvelles colonnes de cette façon.
anishtain4

@ anishtain4, je viens de le tester avec Pandas 25.1 (sorti il ​​y a environ 2 semaines) et ce code fonctionne toujours pour moi. Cet avertissement n'est pas déclenché car df.metaest un SimpleNamespace. Les pandas n'essaieront pas de construire une colonne à partir de cela.
bscan le

6

Comme mentionné dans d'autres réponses et commentaires, _metadatane fait pas partie de l'API publique, ce n'est donc certainement pas une bonne idée de l'utiliser dans un environnement de production. Mais vous voudrez peut-être toujours l'utiliser dans un prototypage de recherche et le remplacer s'il cesse de fonctionner. Et pour le moment, cela fonctionne avec groupby/ apply, ce qui est utile. Voici un exemple (que je n'ai pas trouvé dans d'autres réponses):

df = pd.DataFrame([1, 2, 2, 3, 3], columns=['val']) 
df.my_attribute = "my_value"
df._metadata.append('my_attribute')
df.groupby('val').apply(lambda group: group.my_attribute)

Production:

val
1    my_value
2    my_value
3    my_value
dtype: object

4

Arrivant assez tard à cela, j'ai pensé que cela pourrait être utile si vous avez besoin de métadonnées pour persister sur les E / S. Il existe un package relativement nouveau appelé h5io que j'ai utilisé pour accomplir cela.

Il devrait vous permettre d'effectuer une lecture / écriture rapide à partir de HDF5 pour quelques formats courants, l'un d'entre eux étant un dataframe. Ainsi, vous pouvez, par exemple, mettre un dataframe dans un dictionnaire et inclure des métadonnées en tant que champs dans le dictionnaire. Par exemple:

save_dict = dict(data=my_df, name='chris', record_date='1/1/2016')
h5io.write_hdf5('path/to/file.hdf5', save_dict)
in_data = h5io.read_hdf5('path/to/file.hdf5')
df = in_data['data']
name = in_data['name']
etc...

Une autre option serait de se pencher sur un projet comme xray , qui est plus complexe à certains égards, mais je pense qu'il vous permet d'utiliser des métadonnées et qu'il est assez facile à convertir en DataFrame.


4

Comme mentionné par @choldgraf, j'ai trouvé que xarray était un excellent outil pour attacher des métadonnées lors de la comparaison des données et du traçage des résultats entre plusieurs dataframes.

Dans mon travail, nous comparons souvent les résultats de plusieurs révisions de firmware et de différents scénarios de test, l'ajout de ces informations est aussi simple que ceci:

df = pd.read_csv(meaningless_test)
metadata = {'fw': foo, 'test_name': bar, 'scenario': sc_01}
ds = xr.Dataset.from_dataframe(df)
ds.attrs = metadata

2

J'ai cherché une solution et j'ai trouvé que le cadre de pandas avait la propriété attrs

pd.DataFrame().attrs.update({'your_attribute' : 'value'})
frame.attrs['your_attribute']

Cet attribut restera toujours fidèle à votre cadre chaque fois que vous le passerez!


Notez que attrs est expérimental et peut changer sans avertissement, mais c'est une solution très simple. Je me demande si attrs les transferts vers de nouvelles dataframes.
Liquidgenius

Malheureusement, les attrs ne sont pas copiés dans les nouvelles dataframes :(
Adam le

1

J'avais le même problème et j'ai utilisé une solution de contournement pour créer un nouveau DF plus petit à partir d'un dictionnaire avec les métadonnées:

    meta = {"name": "Sample Dataframe", "Created": "19/07/2019"}
    dfMeta = pd.DataFrame.from_dict(meta, orient='index')

Ce dfMeta peut ensuite être enregistré à côté de votre DF d'origine dans pickle, etc.

Voir Enregistrement et chargement de plusieurs objets dans un fichier pickle? (Réponse de Lutz) pour une excellente réponse sur l'enregistrement et la récupération de plusieurs dataframes à l'aide de pickle

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.