【问题标题】:Cumulative operations on dtype objects对 dtype 对象的累积操作
【发布时间】:2018-03-02 17:03:22
【问题描述】:

我试图弄清楚如何将累积函数应用于对象。对于数字,有几种选择,例如 cumsumcumcount。还有df.expanding 可以与apply 一起使用。但是我传递给apply 的函数不适用于对象。

import pandas as pd
df = pd.DataFrame({"C1": [1, 2, 3, 4], 
                   "C2": [{"A"}, {"B"}, {"C"}, {"D"}], 
                   "C3": ["A", "B", "C", "D"], 
                   "C4": [["A"], ["B"], ["C"], ["D"]]})

df
Out: 
   C1   C2 C3   C4
0   1  {A}  A  [A]
1   2  {B}  B  [B]
2   3  {C}  C  [C]
3   4  {D}  D  [D]

在数据框中,我有整数值、集合、字符串和列表。现在,如果我尝试expanding().apply(sum),我就有了累积总和:

df.expanding().apply(sum)
Out[69]: 
     C1   C2 C3   C4
0   1.0  {A}  A  [A]
1   3.0  {B}  B  [B]
2   6.0  {C}  C  [C]
3  10.0  {D}  D  [D]

我的期望是,由于求和是在列表和字符串上定义的,我会得到这样的结果:

     C1   C2  C3     C4
0   1.0  {A}  A      [A]
1   3.0  {B}  AB     [A, B]
2   6.0  {C}  ABC    [A, B, C]
3  10.0  {D}  ABCD   [A, B, C, D]

我也试过这样的:

df.expanding().apply(lambda r: reduce(lambda x, y: x+y**2, r))
Out: 
     C1   C2 C3   C4
0   1.0  {A}  A  [A]
1   5.0  {B}  B  [B]
2  14.0  {C}  C  [C]
3  30.0  {D}  D  [D]

它按我的预期工作:之前的结果是x,当前的行值是y。但例如,我不能减少使用x.union(y)

所以,我的问题是:我可以在对象上使用 expanding 的替代品吗?该示例只是为了表明 expanding().apply() 不适用于对象 dtypes。我正在寻找一个支持将函数应用于这两个输入的通用解决方案:先前的结果和当前元素。

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    事实证明这是不可能的。

    继续相同的样本:

    def burndowntheworld(ser):
        print('Are you sure?')
        return ser/0
    
    df.select_dtypes(['object']).expanding().apply(burndowntheworld)
    Out: 
        C2 C3   C4
    0  {A}  A  [A]
    1  {B}  B  [B]
    2  {C}  C  [C]
    3  {D}  D  [D]
    

    如果列的类型是对象,则永远不会调用该函数。而且 pandas 没有适用于对象的替代方法。 rolling().apply() 也一样。

    从某种意义上说,这是一件好事,因为带有自定义函数的expanding.apply 具有 O(n**2) 复杂度。对于cumsumewma 等特殊情况,操作的递归性质可以将复杂度降低到线性时间,但在最一般的情况下,它应该计算前 n 个元素的函数,然后计算前 n+ 1个元素等等。因此,特别是对于仅依赖于当前值和函数先前值的函数,扩展是非常低效的。更不用说在 DataFrame 中存储列表或集合从来都不是一个好主意。

    所以答案是:如果您的数据不是数字并且函数依赖于先前的结果和当前元素,则只需使用 for 循环。反正效率会更高。

    【讨论】:

      【解决方案2】:

      我认为你可以使用cumsum,除了set,那么你需要先转换为list,然后再转换为set。顺便说一句,不建议在DataFrame 的列中存储set (C2) 或listslists (C4)。

      print df
         C1   C2 C3   C4
      0   1  {A}  A  [A]
      1   2  {B}  B  [B]
      2   3  {C}  C  [C]
      3   4  {D}  D  [D]
      
      print df[['C1','C3','C4']].cumsum()
         C1    C3            C4
      0   1     A           [A]
      1   3    AB        [A, B]
      2   6   ABC     [A, B, C]
      3  10  ABCD  [A, B, C, D]
      
      df['C2'] = df['C2'].apply(list)
      df = df.cumsum()
      df['C2'] = df['C2'].apply(set)
      print df
         C1            C2    C3            C4
      0   1           {A}     A           [A]
      1   3        {A, B}    AB        [A, B]
      2   6     {A, C, B}   ABC     [A, B, C]
      3  10  {A, C, B, D}  ABCD  [A, B, C, D]
      

      【讨论】:

        【解决方案3】:

        嗯,你可以定义一个自定义函数

        def custom_cumsum(df):
            from functools import reduce
            nrows, ncols = df.shape
            index, columns = df.index, df.columns
            rets = {}
            new_col = None
            for col in df.columns:
                try:
                    new_col = {col:df.loc[:, col].cumsum()}
                except TypeError as e:
                    if 'set' in str(e):
                        new_col = {col:[ reduce(set.union, df.loc[:, col][:(i+1)]) for i in range(nrows)]}
                rets.update(new_col)
            frame = pd.DataFrame(rets, index=index, columns=columns)
            return frame
        

        【讨论】:

        • 谢谢,我猜这个例子有点误导。我包含该示例以表明expanding().apply() 未处理对象。我正在寻找同样适用于对象的 expanding().apply() 的替代方法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-05
        • 1970-01-01
        • 1970-01-01
        • 2012-09-28
        • 1970-01-01
        相关资源
        最近更新 更多