Если добавить _metadata к пользовательскому подклассу Series, то при выборке по индексам теряется имя последовательности

pandas 2.2.3

Рассмотрим два варианта определения пользовательского подтипа pandas.Series, где в первом нет добавленных пользовательских свойств, а во втором есть:

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         <<< Проблемный результат извлечения по индексам
  getattr(seq[0:1], 'property', 'NA') = 'MyProperty'  
  getattr(seq[[0, 1]], 'property', 'NA') = 'MyProperty'  

То есть, если добавить _metadata, то при извлечении по срезу имя последовательности сохраняется, а при извлечении по индексу - теряется, тогда как без _metadata имя сохраняется в обоих случаях.

Что нужно сделать, чтобы имя пользовательской последовательности с добавленными метаданными сохранялось?


Ответы (1 шт):

Автор решения: Vitalizzare ушел в монастырь

Атрибут name объекта типа Series - это дескриптор, работающий со строковым атрибутом '_name'. При этом имя '_name' по умолчанию включается в _metadata, если последнее не было переопределено пользователем. Благодаря этому имя сохраняется при модификации объекта. Следовательно, при переопределении _metadata необходимо самостоятельно добавить туда '_name' наряду с пользовательскими свойствами:

import pandas as pd


class MySeries(pd.Series):

    _metadata = ['my_property', '_name']

    @property
    def _constructor(self):
        return MySeries


seq = MySeries([*'abc'], name='data')

print(f'{seq[0:1].name = }')
print(f'{seq[[0, 1]].name = }')

# Output:
# seq[0:1].name = 'data'
# seq[[0, 1]].name = 'data'
→ Ссылка