是否可以向 pandas DataFrame 添加一些元信息/元数据?
例如,用于测量数据的仪器名称、负责的仪器等。
一种解决方法是使用该信息创建一个列,但在每一行中存储一条信息似乎很浪费!
当然,像大多数 Python 对象一样,您可以将新属性附加到 a pandas.DataFrame
:
import pandas as pd
df = pd.DataFrame([])
df.instrument_name = 'Binky'
但是请注意,虽然您可以将属性附加到 DataFrame ,但对 DataFrame 执行的操作(例如groupby
、pivot
或仅举几例)可能会返回一个没有附加元数据的新 DataFrame。Pandas 还没有一种强大的方法来传播附加到 DataFrames 的元数据。join
loc
可以将元数据保存在文件中。您可以在此处找到有关如何将元数据存储在 HDF5 文件中的示例。
从 pandas 1.0 开始,可能更早,现在有一个Dataframe.attrs
属性。它是实验性的,但这可能是您将来想要的。例如:
import pandas as pd
df = pd.DataFrame([])
df.attrs['instrument_name'] = 'Binky'
在此处的文档中找到它。
尝试使用to_parquet
and then from_parquet
,它似乎不会持续存在,因此请务必使用您的用例进行检查。
我自己也遇到了这个问题。从 pandas 0.13 开始,DataFrame 上有一个 _metadata 属性,该属性通过返回新 DataFrame 的函数持续存在。似乎也可以在序列化中幸存下来(我只尝试过 json,但我想 hdf 也被覆盖了)。
并不真地。尽管您可以像@unutbu 提到的那样将包含元数据的属性添加到 DataFrame 类中,但许多 DataFrame 方法会返回一个新的 DataFrame,因此您的元数据会丢失。如果您需要操作数据框,那么最好的选择是将元数据和数据框包装在另一个类中。请参阅 GitHub 上的此讨论:https ://github.com/pydata/pandas/issues/2485
目前有一个开放的拉取请求来添加一个 MetaDataFrame 对象,这将更好地支持元数据。
将任意属性附加到 DataFrame 对象的最佳答案是好的,但是如果您使用字典、列表或元组,它将发出“Pandas 不允许通过新属性名称创建列”的错误。以下解决方案适用于存储任意属性。
from types import SimpleNamespace
df = pd.DataFrame()
df.meta = SimpleNamespace()
df.meta.foo = [1,2,3]
正如@choldgraf 所提到的,我发现xarray是在比较数据和绘制多个数据帧之间的结果时附加元数据的绝佳工具。
在我的工作中,我们经常会比较几个固件版本和不同测试场景的结果,添加这些信息就这么简单:
df = pd.read_csv(meaningless_test)
metadata = {'fw': foo, 'test_name': bar, 'scenario': sc_01}
ds = xr.Dataset.from_dataframe(df)
ds.attrs = metadata
正如其他答案和评论中提到的,_metadata
它不是公共 API 的一部分,因此在生产环境中使用它绝对不是一个好主意。但是您仍然可能希望在研究原型中使用它并在它停止工作时更换它。现在它可以与groupby
/一起使用apply
,这很有帮助。这是一个例子(我在其他答案中找不到):
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)
输出:
val
1 my_value
2 my_value
3 my_value
dtype: object
来晚了,我认为如果您需要元数据通过 I/O 持久化,这可能会有所帮助。我一直在使用一个相对较新的包h5io来完成此任务。
它应该可以让您从 HDF5 快速读取/写入一些常见格式,其中之一是数据帧。因此,例如,您可以将数据框放入字典中,并将元数据作为字段包含在字典中。例如:
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...
另一种选择是研究像xray这样的项目,它在某些方面更复杂,但我认为它确实允许您使用元数据并且很容易转换为 DataFrame。
我一直在寻找解决方案,发现pandas框架有这个属性attrs
pd.DataFrame().attrs.update({'your_attribute' : 'value'})
frame.attrs['your_attribute']
每当您通过它时,此属性将始终粘在您的框架上!
我遇到了同样的问题,并使用了一种解决方法,即从带有元数据的字典中创建一个新的、更小的 DF:
meta = {"name": "Sample Dataframe", "Created": "19/07/2019"}
dfMeta = pd.DataFrame.from_dict(meta, orient='index')
然后可以将此 dfMeta 与您的原始 DF 一起保存在 pickle 等中
请参阅在泡菜文件中保存和加载多个对象?(Lutz 的回答)对于使用 pickle 保存和检索多个数据帧的出色回答
使用 pandas(例如df.my_metadata = "source.csv"
)添加原始属性不是一个好主意。
即使在最新版本(python 3.8 上的 1.2.4)上,当使用read_csv
. 这将很难调试,因为read_csv
可以正常工作,但稍后(似乎是随机的)您会发现数据帧已从内存中释放。
似乎与 pandas 相关的 cpython 扩展似乎对数据帧的数据布局做出了非常明确的假设。
attrs
是目前使用元数据属性的唯一安全方法:
https ://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.attrs.html
例如
df.attrs.update({'my_metadata' : "source.csv"})
attrs 在所有场景中的行为方式并没有完全充实。您可以帮助提供有关此问题中预期行为的反馈attrs
:https ://github.com/pandas-dev/pandas/issues/28283
参考定义原始属性部分(官方 Pandas 文档),如果子类化pandas.DataFrame
是一个选项,请注意:
要让原始数据结构具有附加属性,您应该
pandas
知道添加了哪些属性。
因此,您可以做的事情 -任意选择名称MetaedDataFrame
- 是
class MetaedDataFrame(pd.DataFrame):
"""s/e."""
_metadata = ['instrument_name']
@property
def _constructor(self):
return self.__class__
# Define the following if providing attribute(s) at instantiation
# is a requirement, otherwise, if YAGNI, don't.
def __init__(
self, *args, instrument_name: str = None, **kwargs
):
super().__init__(*args, **kwargs)
self.instrument_name = instrument_name
然后使用您的(_metadata
-prespecified)属性实例化您的数据框
>>> mdf = MetaedDataFrame(instrument_name='Binky')
>>> mdf.instrument_name
'Binky'
甚至在实例化之后
>>> mdf = MetaedDataFrame()
>>> mdf.instrument_name = 'Binky'
'Binky'
没有任何警告(截至 2021 年 6 月 15 日):序列化并~.copy
像魅力一样工作。此外,这种方法允许丰富您的 API,例如通过向 中添加一些instrument_name
基于 - 的成员MetaedDataFrame
,例如属性(或方法):
[...]
@property
def lower_instrument_name(self) -> str:
if self.instrument_name is not None:
return self.instrument_name.lower()
[...]
>>> mdf.lower_instrument_name
'binky'
...但这超出了这个问题的范围...
根据pandas.pydata.org的说法,对于那些希望将数据帧存储在 HDFStore 中的人,推荐的方法是:
import pandas as pd
df = pd.DataFrame(dict(keys=['a', 'b', 'c'], values=['1', '2', '3']))
df.to_hdf('/tmp/temp_df.h5', key='temp_df')
store = pd.HDFStore('/tmp/temp_df.h5')
store.get_storer('temp_df').attrs.attr_key = 'attr_value'
store.close()