有没有可能给熊猫数据框架添加一些元信息/元数据?
例如,用于测量数据的仪器的名称、负责的仪器等。
一个变通方法是创建一个包含该信息的列,但是在每一行中存储一条信息似乎是浪费!
当然,像大多数 Python 对象一样,您可以将新属性附加到 pandas.DataFrame:
pandas.DataFrame
import pandas as pd df = pd.DataFrame([]) df.instrument_name = 'Binky'
然而,请注意,虽然您可以将属性附加到 DataFrame,但是在 DataFrame 上执行的操作(例如 groupby、 pivot、 join或 loc等等)可能会返回一个新的 DataFrame 没有附加的元数据。大熊猫还没有一个强有力的 繁殖 附加到数据框架的元数据方法。
groupby
pivot
join
loc
保留元数据 在一个文件里是可能的。您可以找到如何在 HDF5文件 给你中存储元数据的示例。
没有。尽管您可以像@unutbu 提到的那样将包含元数据的属性添加到 DataFrame 类中,但是许多 DataFrame 方法返回一个新的 DataFrame,因此您的元数据将丢失。如果需要操作数据框架,那么最好的选择是将元数据和 DataFrame 封装在另一个类中。请参见 GitHub: https://github.com/pydata/pandas/issues/2485上的讨论
目前有一个打开的 撤回请求来添加一个 MetaDataFrame 对象,这将更好地支持元数据。
我自己也遇到了这个问题。从0.13开始,DataFrames 具有一个 _ meta 属性,该属性可以通过返回新 DataFrames 的函数持久化。在序列化过程中似乎也没有什么问题(我只试过 json,但是我想 hdf 也被覆盖了)。
说到这里,我认为如果您需要元数据在 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...
另一个选择是研究像 X 光这样的项目,它在某些方面更复杂,但我认为它确实允许您使用元数据,并且很容易转换为 DataFrame。
正如在其他回答和评论中提到的,_metadata不是公共 API 的一部分,因此在生产环境中使用它肯定不是一个好主意。但是你仍然可能想在研究原型中使用它,如果它停止工作,你可以替换它。现在它与 groupby/apply协同工作,这很有帮助。这是一个例子(我在其他答案中找不到) :
_metadata
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
正如@holdgraf 所提到的,我发现 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
将任意属性附加到 DataFrame 对象的最佳答案是好的,但是如果您使用字典、列表或元组,它将发出一个错误“ Panda 不允许通过新属性名创建列”。下面的解决方案适用于存储任意属性。
from types import SimpleNamespace df = pd.DataFrame() df.meta = SimpleNamespace() df.meta.foo = [1,2,3]
我也遇到了同样的问题,我使用了一个变通方法,从一本带有元数据的字典中创建了一个新的、更小的 DF:
meta = {"name": "Sample Dataframe", "Created": "19/07/2019"} dfMeta = pd.DataFrame.from_dict(meta, orient='index')
这个 dfMeta 然后可以保存在您的原始 DF 在腌菜等
关于使用 pickle 保存和检索多个数据帧,请参阅 在 pickle 文件中保存和加载多个对象?(Lutz 的答案)
至于熊猫1.0,可能更早,现在有一个 Dataframe.attrs属性。这是试验性的,但这可能是你将来想要的。 例如:
Dataframe.attrs
import pandas as pd df = pd.DataFrame([]) df.attrs['instrument_name'] = 'Binky'
在 给你的文件里找到它。
用 to_parquet和 from_parquet试验一下,它似乎不能持久,所以一定要用你的用例检查一下。
to_parquet
from_parquet
我一直在寻找一个解决方案,发现熊猫框架有属性 attrs
attrs
pd.DataFrame().attrs.update({'your_attribute' : 'value'}) frame.attrs['your_attribute']
这个属性将始终坚持您的框架,无论何时您通过它!
向熊猫添加原始属性(例如 df.my_metadata = "source.csv")是 没有的一个好主意。
df.my_metadata = "source.csv"
即使在最新版本(python3.8上是1.2.4)中,这样做也会在使用 read_csv之类的非常简单的操作时随机导致 Segfault。这很难调试,因为 read_csv可以正常工作,但是稍后(看起来是随机的)您会发现数据帧已经从内存中释放。
read_csv
与熊猫有关的 cpython 扩展似乎对数据框架的数据布局做出了非常明确的假设。
attrs是当前使用元数据属性的唯一安全方法: Https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas
例如:。
df.attrs.update({'my_metadata' : "source.csv"})
Attrs 在所有场景中的行为方式尚未完全确定。在本期中,您可以帮助提供关于 attrs的预期行为的反馈: https://github.com/pandas-dev/pandas/issues/28283
参考 定义原始属性(关于熊猫的官方文件)部分,如果 pandas.DataFrame中的 子类化是一个选项,请注意:
要让原始数据结构具有附加属性,应该让 pandas知道添加了哪些属性。
pandas
因此,你可以做的事情-其中 MetaedDataFrame的名称是任意选择的-是
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) : 序列化和 ~.copy的工作就像魔法一样。此外,这种方法允许丰富您的 API,例如,通过向 MetaedDataFrame添加一些基于 instrument_name的成员,如 物业(或方法) :
~.copy
instrument_name
[...] @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()