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

时间:2018-05-16 13:39:50

标签: 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'

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

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

并使用相同的代码:

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'

4 个答案:

答案 0 :(得分:8)

如其他地方所述,DataFrame类有一个自定义__deepcopy__方法,它不一定复制分配给实例的任意属性,就像普通对象一样。

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

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

mydf = pd.DataFrame(...)
mydf.name = 'foo'
mydf._metadata += ['name']

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

您可以将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)

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

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

答案 1 :(得分:3)

copy.deepcopy将使用自定义__deepcopy__方法,如果它在MRO中找到,可以返回任何喜欢的内容(包括完全虚假的结果)。实际上,数据帧实现了__deepcopy__方法:

def __deepcopy__(self, memo=None):
    if memo is None:
        memo = {}
    return self.copy(deep=True)

它委托给self.copy,您会在其中找到this note in the docstring

Notes
-----
When ``deep=True``, data is copied but actual Python objects
will not be copied recursively, only the reference to the object.
This is in contrast to `copy.deepcopy` in the Standard Library,
which recursively copies object data (see examples below).

您会在v0.13 release notes(合并于PR 4039)中找到:

  

__deepcopy__现在返回数据的浅表副本(当前:视图) - 允许更改元数据。

相关问题:17406

答案 2 :(得分:1)

将自定义元数据附加到DataFrames似乎不支持pandas。请参阅this answer(可能重复?)和this github issue

答案 3 :(得分:-2)

此代码有效:

>>> class test():
...     @property
...     def name(self):
...         return self._name
...     @name.setter
...     def name(self, value):
...         self._name = value
...
>>>
>>> a = test()
>>> a.name = 'Test123'
>>> import copy
>>> a2 = copy.deepcopy(a)
>>> print(a2.name)
Test123

所以我认为这种行为是由pd.DataFrame

定义的

我发现pandas定义了函数__deepcopy__,但我不能完全理解它的原因。

pandas/core/indexes/base.py#L960