自定义带_metadata的pandas Series子类,索引时如何保留序列名称?
解决pandas Series子类带自定义元数据时索引操作丢失名称的问题
在pandas 2.2.3版本中,我们定义两种pandas.Series的自定义子类变体:第一种未添加自定义属性,第二种通过_metadata指定自定义元数据。
代码示例
import pandas as pd class MySeries(pd.Series): @property def _constructor(self): return MySeries seq = MySeries([*'abc'], name='data') print(f'''Case without _metadata: {isinstance(seq[0:1], MySeries) = } {isinstance(seq[[0, 1]], MySeries) = } {seq[0:1].name = } {seq[[0, 1]].name = } ''') class MySeries(pd.Series): _metadata = ['property'] @property def _constructor(self): return MySeries seq = MySeries([*'abc'], name='data') seq.property = 'MyProperty' print(f'''Case with defined _metadata: {isinstance(seq[0:1], MySeries) = } {isinstance(seq[[0, 1]], MySeries) = } {seq[0:1].name = } {seq[[0, 1]].name = } {getattr(seq[0:1], 'property', 'NA') = } {getattr(seq[[0, 1]], 'property', 'NA') = } ''')
运行输出
Case without _metadata: isinstance(seq[0:1], MySeries) = True isinstance(seq[[0, 1]], MySeries) = True seq[0:1].name = 'data' seq[[0, 1]].name = 'data' Case with defined _metadata: isinstance(seq[0:1], MySeries) = True isinstance(seq[[0, 1]], MySeries) = True seq[0:1].name = 'data' seq[[0, 1]].name = None <<< Problematic result of indexing getattr(seq[0:1], 'property', 'NA') = 'MyProperty' getattr(seq[[0, 1]], 'property', 'NA') = 'MyProperty'
可见,定义_metadata后,切片操作可保留序列名称,但列表索引操作会丢失名称;未定义_metadata时两种操作均保留名称。
解决方案
方法1:将name加入_metadata列表
pandas在处理自定义元数据时,会自动复制_metadata中声明的属性到新实例。将name添加到_metadata中,就能确保索引操作后name属性被保留:
class MySeries(pd.Series): # 将name加入自定义元数据列表 _metadata = ['property', 'name'] @property def _constructor(self): return MySeries seq = MySeries([*'abc'], name='data') seq.property = 'MyProperty' print(f'''Case with name in _metadata: {isinstance(seq[[0, 1]], MySeries) = } {seq[[0, 1]].name = } {getattr(seq[[0, 1]], 'property', 'NA') = } ''')
运行后输出:
Case with name in _metadata: isinstance(seq[[0, 1]], MySeries) = True seq[[0, 1]].name = 'data' getattr(seq[[0, 1]], 'property', 'NA') = 'MyProperty'
方法2:重写__getitem__方法手动保留名称
如果不想修改_metadata,可以重写__getitem__方法,在返回新实例时检查并设置name:
class MySeries(pd.Series): _metadata = ['property'] @property def _constructor(self): return MySeries def __getitem__(self, key): result = super().__getitem__(key) # 仅当结果是MySeries实例且name为空时,继承原实例的name if isinstance(result, MySeries) and result.name is None: result.name = self.name return result seq = MySeries([*'abc'], name='data') seq.property = 'MyProperty' print(f'''Case with overridden __getitem__: {isinstance(seq[[0, 1]], MySeries) = } {seq[[0, 1]].name = } {getattr(seq[[0, 1]], 'property', 'NA') = } ''')
运行后同样能得到保留name的结果。
内容的提问来源于stack exchange,提问作者vitalizzare
相关产品推荐
相关产品推荐

