python - 为什么子类化 DataFrame 会改变原始对象?

标签 python python-3.x pandas dataframe subclass

我忽略了warnings并尝试对 pandas DataFrame 进行子类化。我这样做的原因如下:

  • 我想保留 DataFrame 的所有现有方法。
  • 我想在类实例化时设置一些附加属性,稍后将使用这些属性来定义我可以在子类上调用的附加方法。

这是一个片段:

class SubFrame(pd.DataFrame):

    def __init__(self, *args, **kwargs):
        freq = kwargs.pop('freq', None)
        ddof = kwargs.pop('ddof', None)
        super(SubFrame, self).__init__(*args, **kwargs)
        self.freq = freq
        self.ddof = ddof
        self.index.freq = pd.tseries.frequencies.to_offset(self.freq)

    @property
    def _constructor(self):
        return SubFrame

这是一个使用示例。假设我有 DataFrame

print(df)
               col0     col1     col2
2014-07-31  0.28393  1.84587 -1.37899
2014-08-31  5.71914  2.19755  3.97959
2014-09-30 -3.16015 -7.47063 -1.40869
2014-10-31  5.08850  1.14998  2.43273
2014-11-30  1.89474 -1.08953  2.67830

索引没有频率

print(df.index)
DatetimeIndex(['2014-07-31', '2014-08-31', '2014-09-30', '2014-10-31',
               '2014-11-30'],
              dtype='datetime64[ns]', freq=None)

使用 SubFrame 允许我一步指定该频率:

sf = SubFrame(df, freq='M')
print(sf.index)
DatetimeIndex(['2014-07-31', '2014-08-31', '2014-09-30', '2014-10-31',
               '2014-11-30'],
              dtype='datetime64[ns]', freq='M')

问题是,这会修改 df:

print(df.index.freq)
<MonthEnd>

这是怎么回事,我该如何避免这种情况?

此外,我声称使用 copied我不太理解的代码。上面的 __init__ 中发生了什么?这里是否有必要将 args/kwargs 与 pop 一起使用? (为什么我不能像往常一样指定参数?)

最佳答案

我将添加警告。并不是我想让你泄气,我实际上对你的努力表示赞赏。

但是,这并不是您对正在发生的事情的最后一个疑问。

也就是说,一旦你运行:

super(SubFrame, self).__init__(*args, **kwargs)

self 是一个真实的数据帧。您通过将另一个数据帧传递给构造函数来创建它。

尝试一下这个实验

d1 = pd.DataFrame(1, list('AB'), list('XY'))
d2 = pd.DataFrame(d1)

d2.index.name = 'IDX'

d1

     X  Y
IDX      
A    1  1
B    1  1

因此观察到的行为是一致的,因为当您通过将另一个数据帧传递给构造函数来构造一个数据帧时,您最终会指向相同的对象。

为了回答你的问题,子类化并不是允许原始对象发生变异的原因......它是 pandas 从传递的数据帧构造数据帧的方式。

通过使用副本实例化来避免这种情况

d2 = pd.DataFrame(d1.copy())

__init__ 中发生了什么

您希望将所有 argskwargs 传递给 pd.DataFrame.__init__,但特定的 kwargs 除外 用于您的子类。在本例中,为 freqddofpop 是一种在将值传递给 pd.DataFrame.__init__

之前获取值并从 kwargs 中删除键的便捷方法

我如何实现管道

def add_freq(df, freq):
    df = df.copy()
    df.index.freq = pd.tseries.frequencies.to_offset(freq)
    return df

df = pd.DataFrame(dict(A=[1, 2]), pd.to_datetime(['2017-03-31', '2017-04-30']))

df.pipe(add_freq, 'M')

关于python - 为什么子类化 DataFrame 会改变原始对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45246072/

相关文章:

Python:复制并粘贴到特定的行和列

Python 向数组中添加一个项目

python-3.x - 我无法按列值过滤数据框

python - 检查一组字母是否可以组成一个单词(考虑字母可以使用的次数)?

python-3.x - 如何在 Selenium 中打开保存在桌面上的链接

python - Scrapy 导入错误 : No module named Item

python - 如何仅在 DataFrame 中存在行时才删除 Pandas 中的行

Python 在每个异常上显示自定义错误消息/回溯

python - 调整 matplotlib 注释框内的填充

python - 编写函数以传递参数或参数元组的大多数 pythonic 方式