是否可以向pandas DataFrame添加一些元信息/元数据?
例如,仪器的名称用于测量数据,仪器负责等等。
一种解决方法是创建一个包含该信息的列,但在每一行中存储一条信息似乎很浪费!
答案 0 :(得分:62)
当然,与大多数Python对象一样,您可以将新属性附加到pandas.DataFrame
:
import pandas as pd
df = pd.DataFrame([])
df.instrument_name = 'Binky'
但请注意,虽然您可以将属性附加到DataFrame,但在DataFrame上执行的操作(例如groupby
,pivot
,join
或loc
只是一些)可能会返回一个新的DataFrame ,而不会附加元数据。 Pandas还没有一种强大的传播方法 metadata attached to DataFrames。
可以在文件中保留元数据。您可以找到如何在HDF5文件here中存储元数据的示例。
答案 1 :(得分:14)
从熊猫1.0(可能更早)开始,现在有了Dataframe.attrs
属性。这是实验性的,但这可能是您将来想要的。
例如:
import pandas as pd
df = pd.DataFrame([])
df.attrs['instrument_name'] = 'Binky'
在文档here中找到它。
先用to_parquet
然后再用from_parquet
进行尝试,它似乎并不持久,因此请确保使用用例进行检验。
答案 2 :(得分:11)
我自己也遇到过这个问题。从pandas 0.13开始,DataFrames上有一个_metadata属性,它通过返回新DataFrames的函数继续存在。似乎在序列化中幸存下来(我只尝试了json,但我想hdf也被覆盖了)。
答案 3 :(得分:10)
不是真的。虽然您可以将包含元数据的属性添加到DataFrame类作为@unutbu提及,但许多DataFrame方法会返回一个新的DataFrame,因此您的元数据将会丢失。如果您需要操作数据框,那么最好的选择是将元数据和DataFrame包装在另一个类中。请参阅GitHub上的讨论:https://github.com/pydata/pandas/issues/2485
目前有一个开放的pull request来添加MetaDataFrame对象,它可以更好地支持元数据。
答案 4 :(得分:3)
到目前为止,我认为如果您需要元数据来保持I / O,这可能会有所帮助。这是一个名为h5io的相对较新的软件包,我一直用它来完成此任务。
它应该允许您从HDF5快速读取/写入一些常见格式,其中一种是数据帧。例如,您可以将数据框放在字典中,并将元数据作为字典中的字段包含在内。 E.g:
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。
答案 5 :(得分:3)
正如其他答案和评论所述,_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
答案 6 :(得分:2)
正如@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
答案 7 :(得分:2)
将任意属性附加到DataFrame对象的最佳答案是好的,但是如果使用字典,列表或元组,它将发出错误消息“ Pandas不允许通过新的属性名称创建列”。以下解决方案适用于存储任意属性。
from types import SimpleNamespace
df = pd.DataFrame()
df.meta = SimpleNamespace()
df.meta.foo = [1,2,3]
答案 8 :(得分:2)
我一直在寻找解决方案,发现熊猫框架具有属性attrs
pd.DataFrame().attrs.update({'your_attribute' : 'value'})
frame.attrs['your_attribute']
只要您通过此属性,它就会始终粘贴在您的框架上!
答案 9 :(得分:0)
我遇到了同样的问题,并使用了一种变通方法,该方法是从包含元数据的字典中创建一个新的,较小的DF:
meta = {"name": "Sample Dataframe", "Created": "19/07/2019"}
dfMeta = pd.DataFrame.from_dict(meta, orient='index')
此dfMeta然后可以与原始DF一起保存在泡菜等中
有关使用pickle保存和检索多个数据框的出色答案,请参见Saving and loading multiple objects in pickle file?(卢茨的答案)
答案 10 :(得分:0)
使用 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
答案 11 :(得分:0)
参考 Define original properties(official Pandas documentation) 部分,如果 subclassing 来自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
-预先指定的)属性实例化您的数据框
>>> mdf = MetaedDataFrame(instrument_name='Binky')
>>> mdf.instrument_name
'Binky'
甚至在实例化之后
>>> mdf = MetaedDataFrame()
>>> mdf.instrument_name = 'Binky'
'Binky'
没有任何警告(截至 2021/06/15):serialization 和 ~.copy
就像一个魅力。此外,这种方法可以丰富您的 API,例如通过向 instrument_name
添加一些基于 MetaedDataFrame
的成员,例如 properties(或方法):
[...]
@property
def lower_instrument_name(self) -> str:
if self.instrument_name is not None:
return self.instrument_name.lower()
[...]
>>> mdf.lower_instrument_name
'binky'
... 但这超出了这个问题的范围 ...
答案 12 :(得分:0)
对于那些希望将数据帧存储在 HDFStore 中的人,根据 pandas.pydata.org,推荐的方法是:
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()