为什么复制Pandas DataFrame后属性会丢失

A H*_*A H 14 python

为什么不能通过副本传递实例的属性?我想将该name属性传递给另一个数据帧.

import copy
df = pd.DataFrame([1,2,3])
df.name = 'sheet1'
df2 = copy.deepcopy(df)

print(f'df.name: {df.name}')
>> df.name: sheet1

print(f'df2.name: {df2.name}')
>>    AttributeError    
        ...      
      'DataFrame' object has no attribute 'name'
Run Code Online (Sandbox Code Playgroud)

同样,在创建类并从中继承时,为什么这也不起作用?

class DfWithName(pd.DataFrame):

    def __init__(self, *args, **kwargs):
        self.__init__ = super().__init__(*args, **kwargs)
        print('lol')

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
Run Code Online (Sandbox Code Playgroud)

并使用相同的代码:

import copy
df = DfWithName([1,2,3])
df.name = 'sheet1'
df2 = copy.deepcopy(df) 
print(f'df.name: {df2.name}')
>>    AttributeError    
        ...      
      'DataFrame' object has no attribute 'name'
Run Code Online (Sandbox Code Playgroud)

Igu*_*aut 9

如其他地方所述,DataFrame该类具有自定义__deepcopy__方法,该方法不一定复制分配给实例的任意属性,如同普通对象一样.

有趣的是,有一个内部_metadata属性似乎能够列出NDFrame复制/序列化时应保留的其他属性.这里有一些讨论:https://github.com/pandas-dev/pandas/issues/9317

不幸的是,这仍然被认为是未记录的内部细节,因此可能不应该使用它.通过查看代码,您原则上可以:

mydf = pd.DataFrame(...)
mydf.name = 'foo'
mydf._metadata += ['name']
Run Code Online (Sandbox Code Playgroud)

当你复制它时,它应该带有它的名字.

您可以将子类化为DataFrame默认值:

import functools

class NamedDataFrame(pd.DataFrame):
    _metadata = pd.DataFrame._metadata + ['name']

    def __init__(self, name, *args, **kwargs):
        self.name = name
        super().__init__(*args, **kwargs)

    @property
    def _constructor(self):
        return functools.partial(self.__class__, self.name)
Run Code Online (Sandbox Code Playgroud)

_metadata如果您为现有copy方法提供自己的包装器,并且可能还有__getstate__,那么您也可以在不依赖此内部属性的情况下执行此操作__setstate__.

更新:它实际上似乎使用的_metadata属性用于扩展熊猫班现在记录.所以上面的例子应该或多或少有效.这些文档更多用于开发Pandas本身,因此它可能仍然有点不稳定.但这就是熊猫本身如何扩展子类的NDFrame.