【问题标题】:Adding meta-information/metadata to pandas DataFrame将元信息/元数据添加到 pandas DataFrame
【发布时间】:2013-01-19 05:52:48
【问题描述】:

是否可以向 pandas DataFrame 添加一些元信息/元数据?

例如用于测量数据的仪器名称、负责的仪器等

一种解决方法是使用该信息创建一个列,但在每一行中存储一条信息似乎很浪费!

【问题讨论】:

  • 请注意@ryanjdillon 的答案(目前埋在底部附近),其中提到了更新的实验属性“attrs”,这似乎是一个开始,也许

标签: python pandas


【解决方案1】:

当然,像大多数 Python 对象一样,您可以将新属性附加到 pandas.DataFrame

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

但是请注意,虽然您可以将属性附加到 DataFrame,但对 DataFrame 执行的操作(例如 groupbypivotjoinloc 仅举几例)可能会返回一个新的DataFrame 没有附加的元数据。 Pandas 还没有一个强大的传播metadata attached to DataFrames方法。

将元数据保存在文件中 是可能的。您可以找到有关如何将元数据存储在 HDF5 文件 here 中的示例。

【讨论】:

  • +1 供您选择仪器名称!您是否有任何尝试将这些额外属性转储到 HDFStore 的经验?
  • @DanAllan: 如果store = pd.HDFStore(...),那么属性可以用store.root._v_attrs.key = value存储。
  • 致其他可能使用此功能的人:文档已为此添加了一个部分。 pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore
  • 在 pandas 0.23.1 中,通过分配字典、列表或元组来创建新属性会发出警告(即 df = pd.DataFrame(); df.meta = {} 产生 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)。 (如果属性已经像df = pd.DataFrame(); df.meta = ''; df.meta = {} 一样创建,则不会给出警告)。
【解决方案2】:

不是真的。尽管您可以像@unutbu 提到的那样将包含元数据的属性添加到 DataFrame 类中,但许多 DataFrame 方法返回一个新的 DataFrame,因此您的元数据会丢失。如果您需要操作数据框,那么最好的选择是将元数据和数据框包装在另一个类中。在 GitHub 上查看此讨论:https://github.com/pydata/pandas/issues/2485

目前有一个开放的pull request 添加一个 MetaDataFrame 对象,这将更好地支持元数据。

【讨论】:

    【解决方案3】:

    我自己也遇到了这个问题。从 pandas 0.13 开始,DataFrame 上有一个 _metadata 属性,该属性通过返回新 DataFrame 的函数持续存在。似乎也可以在序列化中幸存下来(我只尝试过 json,但我想 hdf 也被覆盖了)。

    【讨论】:

    • _metadata 不是公共 API 的一部分,因此我强烈建议不要依赖此功能。
    • @Stephan 你能详细说明一下吗?为什么成为公共 API 的一部分很重要?您的说法是否也适用于 0.15 版?
    • @TomCho 是的,这个答案今天仍然适用。您可以查看 xray (github.com/xray/xray),了解支持元数据的标记数组的另一个示例,尤其是在您拥有多维数据时(.attrs 是 xray API 的一部分)
    • _metadata 实际上是类属性,而不是实例属性。因此,只要模块保持加载状态,新的 DataFrame 实例就会继承以前的实例。不要将_metadata 用于任何事情。 +1 xarray!
    • _metadata -- 一个不受支持的功能,它拯救了我的一天!谢谢。
    【解决方案4】:

    很晚才开始这样做,我认为如果您需要元数据在 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。

    【讨论】:

      【解决方案5】:

      正如其他答案和 cmets 中提到的,_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】:

        正如@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】:

          将任意属性附加到 DataFrame 对象的最佳答案是好的,但是如果您使用字典、列表或元组,它将发出错误“Pandas 不允许通过新属性名称创建列”。以下解决方案适用于存储任意属性。

          from types import SimpleNamespace
          df = pd.DataFrame()
          df.meta = SimpleNamespace()
          df.meta.foo = [1,2,3]
          

          【讨论】:

          • 另外,如果您希望它在数据帧的副本中持续存在,您需要执行 pd.DataFrame._metadata += ["meta"] 。请注意,这部分是 Pandas 的属性,而不是您的特定数据框的属性
          • 这种方法将不再有效,因为df.meta 会触发警告说 Pandas 不允许以这种方式生成新列。
          • @anishtain4,我刚刚使用 Pandas 25.1(大约 2 周前发布)对其进行了测试,这段代码仍然适用于我。由于df.meta 是一个简单命名空间,因此不会触发该警告。 Pandas 不会尝试从中构建列。
          【解决方案8】:

          我遇到了同样的问题,并使用了一种解决方法,即从带有元数据的字典中创建一个新的、更小的 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?(Lutz 的回答),了解有关使用 pickle 保存和检索多个数据帧的出色回答

          【讨论】:

          • 是的,如果元数据文件只是一个字典,您也可以将元数据文件保存在 json 中,而不是转换为 pandas 数据帧然后保存数据帧。
          【解决方案9】:

          从可能更早的 pandas 1.0 开始,现在有一个 Dataframe.attrs 属性。它是实验性的,但这可能是您将来想要的。 例如:

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

          在文档here 中找到它。

          to_parquetfrom_parquet 尝试这个,它似乎不会持续,所以一定要检查一下你的用例。

          【讨论】:

          • 这很有趣,并且似乎对于 copy/loc/iloc 仍然存在,但对于 groupby 则不存在。
          • 只是一个建议,但可能会显示一个如何使用它的示例?该文档基本上什么都没有,但只是通过玩它我可以看到它被初始化为一个空字典,并且它似乎被设置为它必须是一个字典,尽管当然可以在其中嵌套一个列表,例如。
          • 您可能会发现这个Stackoverflow discussion 很有用,因为它演示了如何在需要时将自定义元数据添加到镶木地板文件中
          • @rdmolony 太好了。我认为使用dataclass 作为元数据,然后对DataFrame 进行子类化以使用您分享的帖子中的方法进行加载/转储可能是一个不错的解决方案。
          • 这很好。与接受的答案相反,这确实在从泡菜保存和加载后保留了属性!
          【解决方案10】:

          我一直在寻找解决方案,发现pandas框架有attrs的属性

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

          无论何时传递,此属性都会始终粘在您的框架上!

          【讨论】:

          • 请注意 attrs 是实验性的,可能会在没有警告的情况下更改,但这是一个非常简单的解决方案。我想知道 attrs 是否会转移到新的数据帧。
          • 不幸的是,attrs 没有被复制到新的数据帧:(
          【解决方案11】:

          使用 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

          【讨论】:

            【解决方案12】:

            参考 Define original propertiesofficial 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-prespecified) 属性实例化您的数据框

            >>> mdf = MetaedDataFrame(instrument_name='Binky')
            >>> mdf.instrument_name
            'Binky'
            

            甚至在实例化之后

            >>> mdf = MetaedDataFrame()
            >>> mdf.instrument_name = 'Binky'
            'Binky'
            

            没有任何警告(截至 2021 年 6 月 15 日):serialization~.copy 就像一个魅力。此外,这种方法可以丰富您的 API,例如通过向MetaedDataFrame 添加一些基于instrument_name 的成员,例如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'
            

            ... 但这超出了这个问题的范围 ...

            【讨论】:

              【解决方案13】:

              对于那些希望将数据帧存储在 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()
              

              【讨论】:

                猜你喜欢
                • 2021-12-24
                • 2022-11-29
                • 2019-08-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-10-26
                • 1970-01-01
                相关资源
                最近更新 更多