Agregar metainformación / metadatos a pandas DataFrame

90

¿Es posible agregar algo de metainformación / metadatos a un DataFrame de pandas?

Por ejemplo, el nombre del instrumento utilizado para medir los datos, el instrumento responsable, etc.

Una solución alternativa sería crear una columna con esa información, ¡pero parece un desperdicio almacenar una sola pieza de información en cada fila!

P3trus
fuente
Tenga en cuenta la respuesta @ryanjdillon (actualmente enterrada cerca de la parte inferior) que menciona el atributo experimental actualizado 'attrs' que parece un comienzo, tal vez
JohnE

Respuestas:

85

Claro, como la mayoría de los objetos de Python, puede adjuntar nuevos atributos a pandas.DataFrame:

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

Nótese, sin embargo, que si bien puede adjuntar atributos a una trama de datos, las operaciones realizadas en la trama de datos (como groupby, pivot, joino locpara nombrar sólo algunos) puede devolver una nueva trama de datos , sin los metadatos adjunto. Pandas aún no tiene un método sólido para propagar metadatos adjuntos a DataFrames .

Es posible conservar los metadatos en un archivo . Puede encontrar un ejemplo de cómo almacenar metadatos en un archivo HDF5 aquí .

unutbu
fuente
5
+1 para elegir el nombre del instrumento. ¿Tiene alguna experiencia tratando de volcar estos atributos adicionales en HDFStore?
Dan Allan
4
@DanAllan: Si store = pd.HDFStore(...), los atributos se pueden almacenar con store.root._v_attrs.key = value.
unutbu
3
Para cualquier otra persona que pueda usar esto: los documentos han agregado una sección sobre esto. pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore
Dan Allan
4
En pandas 0.23.1, la creación de un nuevo atributo mediante la asignación de un diccionario, lista o tupla da una advertencia (es decir, df = pd.DataFrame(); df.meta = {}produce 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). (No se da ninguna advertencia si el atributo ya se ha creado como en df = pd.DataFrame(); df.meta = ''; df.meta = {}).
teichert
13

Me encontré con este problema yo mismo. A partir de pandas 0.13, los DataFrames tienen un atributo _metadata que persiste a través de funciones que devuelven nuevos DataFrames. También parece sobrevivir a la serialización bien (solo he probado json, pero imagino que hdf también está cubierto).

locura
fuente
16
_metadatano es parte de la API pública, por lo que recomiendo encarecidamente no confiar en esta funcionalidad.
shoyer
@Stephan, ¿puedes explicarlo por favor? ¿Por qué es importante ser parte de la API pública? ¿Su afirmación también es cierta para la versión 0.15?
TomCho
1
@TomCho sí, esa respuesta sigue siendo cierta hoy. Puede echar un vistazo a xray ( github.com/xray/xray ) para ver un ejemplo alternativo de una matriz etiquetada que admite metadatos, especialmente si tiene datos multidimensionales ( .attrses parte de la API de xray)
shoyer
17
_metadataes en realidad un atributo de clase, no un atributo de instancia. Entonces, las nuevas DataFrameinstancias heredan de las anteriores, siempre que el módulo permanezca cargado. No lo use _metadatapara nada. +1 para xarray!
j08lue
1
_metadata: ¡una función no compatible que me salvó el día! Gracias.
joctee
12

Realmente no. Aunque puede agregar atributos que contienen metadatos a la clase DataFrame como menciona @unutbu, muchos métodos DataFrame devuelven un nuevo DataFrame, por lo que sus metadatos se perderían. Si necesita manipular su marco de datos, la mejor opción sería envolver sus metadatos y su marco de datos en otra clase. Vea esta discusión en GitHub: https://github.com/pydata/pandas/issues/2485

Actualmente hay una solicitud de extracción abierta para agregar un objeto MetaDataFrame, que admitiría mejor los metadatos.

Matti John
fuente
11

A partir de pandas 1.0, posiblemente antes, ahora hay una Dataframe.attrspropiedad. Es experimental, pero probablemente esto es lo que querrás en el futuro. Por ejemplo:

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

Encuéntrelo en los documentos aquí .

Probar esto con to_parquety luego from_parquet, no parece persistir, así que asegúrese de verificarlo con su caso de uso.

Ryanjdillon
fuente
Esto es interesante y parece persistir para copy / loc / iloc, pero no para groupby.
JohnE
Solo una sugerencia, pero ¿quizás mostrar un ejemplo de cómo usarlo? La documentación es básicamente nada, pero con solo jugar con ella puedo ver que está inicializado como un diccionario vacío y parece estar configurado para que tenga que ser un diccionario aunque, por supuesto, uno podría anidar una lista dentro de él, por ejemplo.
JohnE
1
Puede encontrar útil esta discusión de Stackoverflow, ya que demuestra cómo agregar metadatos personalizados a los archivos de parquet si es necesario
rdmolony
1
@rdmolony Eso es genial. Creo que usar un dataclasspara los metadatos y luego DataFramecrear una subclasificación para tener un método que realice la carga / descarga como en la publicación que compartió podría ser una buena solución.
Ryanjdillon
1
Esto es bonito. En contraste con la respuesta aceptada, ¡esto conserva los atributos después de guardar y cargar desde pickle!
CGFoX
8

La respuesta principal de adjuntar atributos arbitrarios al objeto DataFrame es buena, pero si usa un diccionario, lista o tupla, emitirá un error de "Pandas no permite que se creen columnas a través de un nuevo nombre de atributo". La siguiente solución funciona para almacenar atributos arbitrarios.

from types import SimpleNamespace
df = pd.DataFrame()
df.meta = SimpleNamespace()
df.meta.foo = [1,2,3]
bscan
fuente
Además, si desea que esto persista en las copias de su marco de datos, debe hacerlo pd.DataFrame._metadata += ["meta"]. Tenga en cuenta que esta parte es un atributo de Pandas, no un atributo de su marco de datos específico
bscan
Este enfoque ya no funcionará ya que df.metaactiva una advertencia de que Pandas no permite que se generen nuevas columnas de esta manera.
anishtain4
@ anishtain4, acabo de probarlo con Pandas 25.1 (lanzado hace ~ 2 semanas) y este código todavía funciona para mí. Esa advertencia no se activa ya que df.metaes un SimpleNamespace. Pandas no intentará construir una columna a partir de él.
bscan
6

Como se mencionó en otras respuestas y comentarios, _metadatano es parte de la API pública, por lo que definitivamente no es una buena idea usarla en un entorno de producción. Pero es posible que aún desee utilizarlo en un prototipo de investigación y reemplazarlo si deja de funcionar. Y ahora mismo funciona con groupby/ apply, lo cual es útil. Este es un ejemplo (que no pude encontrar en otras respuestas):

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)

Salida:

val
1    my_value
2    my_value
3    my_value
dtype: object
Dennis Golomazov
fuente
4

Llegando bastante tarde a esto, pensé que esto podría ser útil si necesita que los metadatos persistan en la E / S. Hay un paquete relativamente nuevo llamado h5io que he estado usando para lograr esto.

Debería permitirle hacer una lectura / escritura rápida desde HDF5 para algunos formatos comunes, uno de ellos es un marco de datos. Entonces, puede, por ejemplo, poner un marco de datos en un diccionario e incluir metadatos como campos en el diccionario. P.ej:

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...

Otra opción sería buscar en un proyecto como Xray , que es más complejo en algunos aspectos, pero creo que te permite usar metadatos y es bastante fácil de convertir a un DataFrame.

choldgraf
fuente
4

Como lo menciona @choldgraf, he descubierto que xarray es una excelente herramienta para adjuntar metadatos al comparar datos y trazar resultados entre varios marcos de datos.

En mi trabajo, a menudo comparamos los resultados de varias revisiones de firmware y diferentes escenarios de prueba, agregar esta información es tan simple como esto:

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

He estado buscando una solución y descubrí que pandas frame tiene la propiedad attrs

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

¡Este atributo siempre se adherirá a su marco cada vez que lo pase!

Ayrat Arifullin
fuente
Tenga en cuenta que attrs es experimental y puede cambiar sin previo aviso, pero esta es una solución muy simple. Me pregunto si attrs se transfiere a nuevos marcos de datos.
Liquidgenius
Desafortunadamente, los atributos no se copian en nuevos marcos de datos :(
Adam
1

Tenía el mismo problema y usé una solución alternativa para crear un DF nuevo y más pequeño a partir de un diccionario con los metadatos:

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

Este dfMeta se puede guardar junto con su DF original en pickle, etc.

Consulte ¿ Guardar y cargar varios objetos en un archivo pickle? (Respuesta de Lutz) para una excelente respuesta sobre cómo guardar y recuperar múltiples marcos de datos usando pickle

SenAnan
fuente