代码之家  ›  专栏  ›  技术社区  ›  Brad Solomon

为什么数据帧的子类化会改变原始对象?

  •  2
  • Brad Solomon  · 技术社区  · 8 年前

    我忽略了 warnings

    • 我想保留所有现有的 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
    

    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

    1 回复  |  直到 6 年前
        1
  •  3
  •   piRSquared    8 年前

    我将添加警告。我并不是想让你气馁,而是为你的努力喝彩。

    然而,这并不是你关于发生了什么的最后一个问题。

    也就是说,一旦你跑步:

    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
    

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

    为了回答您的问题,子类化并不是允许原始对象发生变异……而是熊猫从传递的数据帧构造数据帧的方式。

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

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

    发生了什么事 __init__

    你想把所有的 args kwargs pd.DataFrame.__init__ 除了特定的 关键字参数 freq ddof . pop 关键字参数 pd.数据帧_uinit__


    我将如何实施 pipe

    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')