Adicionar meta-informação / metadados ao pandas DataFrame

93

É possível adicionar alguma meta-informação / metadados a um DataFrame do pandas?

Por exemplo, o nome do instrumento usado para medir os dados, o instrumento responsável, etc.

Uma solução alternativa seria criar uma coluna com essas informações, mas parece um desperdício armazenar uma única informação em cada linha!

P3trus
fonte
Por favor, observe a resposta @ryanjdillon (atualmente enterrada perto do fim) que menciona o atributo experimental atualizado 'attrs' que parece ser um começo, talvez
JohnE

Respostas:

87

Claro, como a maioria dos objetos Python, você pode anexar novos atributos a pandas.DataFrame:

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

Note, no entanto, que, enquanto você pode anexar atributos para uma trama de dados, operações realizadas na trama de dados (como groupby, pivot, joinou locpara citar apenas alguns) pode retornar uma nova trama de dados sem os metadados anexado. O Pandas ainda não possui um método robusto de propagação de metadados anexados a DataFrames .

É possível preservar os metadados em um arquivo . Você pode encontrar um exemplo de como armazenar metadados em um arquivo HDF5 aqui .

unutbu
fonte
5
1 para sua escolha de nome de instrumento! Você tem alguma experiência em tentar despejar esses atributos extras na HDFStore?
Dan Allan
4
@DanAllan: Se store = pd.HDFStore(...), então, os atributos podem ser armazenados com store.root._v_attrs.key = value.
unutbu
3
Para qualquer pessoa que possa usar isso: os documentos adicionaram uma seção sobre isso. pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore
Dan Allan
5
No pandas 0.23.1, criar um novo atributo atribuindo um dicionário, lista ou tupla dá um aviso (ou seja, df = pd.DataFrame(); df.meta = {}produz 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). (Nenhum aviso é dado se o atributo já foi criado como em df = pd.DataFrame(); df.meta = ''; df.meta = {}).
teichert
14

A partir do pandas 1.0, possivelmente anterior, agora existe uma Dataframe.attrspropriedade. É experimental, mas provavelmente é isso que você desejará no futuro. Por exemplo:

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

Encontre-o na documentação aqui .

Tentando fazer isso com to_parquete então from_parquet, não parece persistir, então certifique-se de verificar isso com seu caso de uso.

Ryanjdillon
fonte
Isso é interessante e parece persistir para copy / loc / iloc, mas não para groupby.
JohnE de
Só uma sugestão, mas quem sabe dar um exemplo de como usar? A documentação é basicamente nada, mas apenas brincando com ela posso ver que ela foi inicializada como um dicionário vazio e parece estar configurada para que seja um dicionário, embora, é claro, alguém possa aninhar uma lista dentro dele, por exemplo.
JohnE de
1
Você pode achar esta discussão Stackoverflow útil, pois demonstra como adicionar metadados personalizados a arquivos em parquet, se necessário
rdmolony
1
@rdmolony Isso é ótimo. Acho que usar um dataclasspara os metadados e, em seguida, DataFramecriar uma subclasse para ter um método de carregar / despejar como no post que você compartilhou pode ser uma boa solução.
Ryanjdillon
1
Isso é legal. Em contraste com a resposta aceita, isso preserva os atributos após salvar e carregar do pickle!
CGFoX
13

Acabei de encontrar esse problema sozinho. A partir do pandas 0.13, DataFrames têm um atributo _metadata que persiste por meio de funções que retornam novos DataFrames. Também parece sobreviver à serialização muito bem (só tentei json, mas imagino que hdf também seja coberto).

follyroof
fonte
16
_metadatanão faz parte da API pública, então eu recomendo fortemente que você não dependa dessa funcionalidade.
atirador de
@Stephan você pode explicar isso, por favor? Por que é importante fazer parte da API pública? A sua afirmação também é verdadeira para a versão 0.15?
TomCho
1
@TomCho sim, essa resposta ainda é verdadeira hoje. Você pode dar uma olhada em xray ( github.com/xray/xray ) para um exemplo alternativo de uma matriz rotulada que suporta metadados, especialmente se você tiver dados multidimensionais ( .attrsfaz parte da API xray)
shoyer
17
_metadataé na verdade um atributo de classe, não um atributo de instância. Portanto, novas DataFrameinstâncias herdam das anteriores, desde que o módulo permaneça carregado. Não use _metadatapara nada. 1 para xarray!
julho
1
_metadata - um recurso sem suporte que salvou meu dia! Obrigado.
joctee
12

Na verdade não. Embora você possa adicionar atributos contendo metadados à classe DataFrame como @unutbu menciona, muitos métodos DataFrame retornam um novo DataFrame, portanto, seus metadados seriam perdidos. Se você precisar manipular seu dataframe, a melhor opção seria envolver seus metadados e DataFrame em outra classe. Veja esta discussão no GitHub: https://github.com/pydata/pandas/issues/2485

Atualmente, há uma solicitação de pull aberta para adicionar um objeto MetaDataFrame, que ofereceria um suporte melhor aos metadados.

Matti John
fonte
8

A principal resposta de anexar atributos arbitrários ao objeto DataFrame é boa, mas se você usar um dicionário, lista ou tupla, será emitido um erro de "Pandas não permite que colunas sejam criadas por meio de um novo nome de atributo". A solução a seguir funciona para armazenar atributos arbitrários.

from types import SimpleNamespace
df = pd.DataFrame()
df.meta = SimpleNamespace()
df.meta.foo = [1,2,3]
bscan
fonte
Além disso, se você deseja que isso persista em todas as cópias do seu dataframe, é necessário fazer pd.DataFrame._metadata += ["meta"]. Observe que esta parte é um atributo dos Pandas, não um atributo do seu dataframe específico
bscan
Esta abordagem não funcionará mais, pois df.metadispara um aviso de que o Pandas não permite que novas colunas sejam geradas dessa forma.
anishtain4
@ anishtain4, acabei de testá-lo com o Pandas 25.1 (lançado há cerca de 2 semanas) e esse código ainda funciona para mim. Esse aviso não é acionado porque df.metaé um SimpleNamespace. Os Pandas não vão tentar construir uma coluna a partir dele.
bscan de
6

Conforme mencionado em outras respostas e comentários, _metadatanão faz parte da API pública, portanto, definitivamente não é uma boa ideia usá-lo em um ambiente de produção. Mas você ainda pode querer usá-lo em um protótipo de pesquisa e substituí-lo se ele parar de funcionar. E agora ele funciona com groupby/ apply, o que é útil. Este é um exemplo (que não consegui encontrar em outras respostas):

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)

Resultado:

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

Chegando bem tarde a isso, achei que isso poderia ser útil se você precisar que os metadados persistam na E / S. Há um pacote relativamente novo chamado h5io que estou usando para fazer isso.

Ele deve permitir que você faça uma leitura / gravação rápida de HDF5 para alguns formatos comuns, um deles sendo um dataframe. Portanto, você pode, por exemplo, colocar um dataframe em um dicionário e incluir metadados como campos no dicionário. Por exemplo:

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

Outra opção seria examinar um projeto como o xray , que é mais complexo em alguns aspectos, mas acho que permite que você use metadados e é muito fácil de converter em DataFrame.

Choldgraf
fonte
4

Conforme mencionado por @choldgraf, descobri que o xarray é uma excelente ferramenta para anexar metadados ao comparar dados e traçar resultados entre vários dataframes.

Em meu trabalho, frequentemente comparamos os resultados de várias revisões de firmware e diferentes cenários de teste, adicionar essas informações é tão simples quanto isto:

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
fonte
2

Tenho procurado uma solução e descobri que a moldura dos pandas tem a propriedade attrs

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

Este atributo sempre ficará no seu quadro sempre que você o passar!

Ayrat Arifullin
fonte
Observe que o attrs é experimental e pode mudar sem aviso, mas esta é uma solução muito simples. Eu me pergunto se atrs transferências para novos dataframes.
Liquidgenius
Infelizmente, os atributos não são copiados para novos dataframes :(
Adam
1

Eu estava tendo o mesmo problema e usei uma solução alternativa para criar um novo DF menor a partir de um dicionário com os metadados:

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

Este dfMeta pode então ser salvo ao lado de seu DF original em picles, etc.

Consulte Salvando e carregando vários objetos no arquivo pickle? (Resposta de Lutz) pela excelente resposta sobre como salvar e recuperar vários dataframes usando pickle

SenAnan
fonte