Ошибка при копировании составного объекта, состоящего в основном из pandas.DataFrame

Я пытаюсь использовать composition с pandas.DataFrame следующим образом, но при попытке скопировать объект возникают ошибки.

import numpy as np
import pandas as pd
import copy


class Foo(object):
    """
    Foo is composed mostly of a pd.DataFrame, and behaves like it too. 
    """

    def __init__(self, df, attr_custom):
        self._ = df
        self.attr_custom = attr_custom

    # the following code allows Foo objects to behave like pd.DataFame,
    # and I want to keep this behavior.
    def __getattr__(self, attr):
        return getattr(self._, attr)


df = pd.DataFrame(np.random.randint(0,2,(3,2)), columns=['A','B'])
foo = Foo(df)
foo_cp = copy.deepcopy(foo)

Ошибка, которую я получаю:

---> 16 foo_cp = copy.deepcopy(foo)

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.pyc in deepcopy(x, memo, _nil)
    188                             raise Error(
    189                                 "un(deep)copyable object of type %s" % cls)
--> 190                 y = _reconstruct(x, rv, 1, memo)
    191 
    192     memo[d] = y

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.pyc in _reconstruct(x, info, deep, memo)
    341                 slotstate = None
    342             if state is not None:
--> 343                 y.__dict__.update(state)
    344             if slotstate is not None:
    345                 for key, value in slotstate.iteritems():

TypeError: 'BlockManager' object is not iterable 

Мои вопросы:

  1. Любая идея, что здесь происходит?
  2. Каков «рекомендуемый» способ использования композиции с pandas.DataFrame?
  3. Если по каким-то причинам использование _ в качестве имени фиктивного атрибута является плохой идеей, сообщите мне об этом.

person Lei    schedule 10.04.2015    source источник
comment
См. документы версии 0.16.1 здесь   -  person Jeff    schedule 02.06.2015


Ответы (1)


Стандартный способ сделать это — определить свойство _constructor:

class Foo(pd.DataFrame):
    @property
    def _constructor(self):
        return Foo

Тогда большинство методов DataFrame должны работать и возвращать Foo.

In [11]: df = pd.DataFrame([[1, 2], [3, 4]])

In [12]: foo = Foo(df)

In [13]: foo.copy()
Out[13]:
   0  1
0  1  2
1  3  4

In [14]: type(foo.copy())
Out[14]: __main__.Foo

Включая copy.deepcopy:

In [15]: copy.deepcopy(foo)
Out[15]:
   0  1
0  1  2
1  3  4

In [16]: type(copy.deepcopy(foo))
Out[16]: __main__.Foo

В сторону: я бы не стал использовать _ в качестве имени переменной/метода, оно вообще не является описательным. Вы можете добавить к имени префикс _, чтобы показать, что его следует считать «частным», но дать ему (описательное!) имя, например. _df.

_ часто используется в python для обозначения «отбросить эту переменную», поэтому вы можете написать:

sum(1 for _ in x)  # this is basically the same as len!

Хотя было бы совершенно правильно использовать python для использования _, например:

sum( _ ** 2 for _ in x)

Обычно это не одобряется (вместо этого используйте i или что-то в этом роде).

В ipython _ означает предыдущее возвращенное значение.

person Andy Hayden    schedule 10.04.2015
comment
Большое спасибо за Вашу помощь. Но, выполняя class Foo(pd.DataFrame), мы создаем подкласс pd.DataFrame, то есть наследование, а не композицию, верно? И создание подклассов pandas.DataFrame подвержено некоторым проблемам, таким как потеря пользовательских атрибутов после сериализации (например, groups.google.com/forum/#!topic/pydata/9Qimqt4ekvQ), верно? В основном поэтому я пробовал композицию. - person Lei; 10.04.2015
comment
@LeiHuang Я не понимаю, как это решит проблему сериализации. Создание подклассов IMO и определение методов, для которых вы хотите сохранить метаданные (сначала сделайте это), тогда вызов super будет лучшим решением? - person Andy Hayden; 10.04.2015
comment
Для справки: есть официальная документация о создании подклассов структур данных pandas. - person Lei; 05.06.2015
comment
@LeiHuang круто! Благодарность! (Это блестяще и ново), я соответствующим образом обновлю этот ответ. - person Andy Hayden; 06.06.2015