dtype对象的累积操作

6

我正在尝试找出如何将累加函数应用于对象。对于数字,有几种替代方法,例如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()在对象数据类型上不起作用。我正在寻找一个通用解决方案,支持将函数应用于这两个输入: 前一个结果和当前元素。

3个回答

5
原来这是无法完成的。
继续看同样的示例:
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)的复杂度。对于像cumsum、ewma等特殊情况,操作的递归性质可以将复杂度降低到线性时间,但在最一般的情况下,它应该计算前n个元素的函数,然后计算前n + 1个元素的函数,依此类推。因此,特别是对于一个仅依赖于当前值和函数先前值的函数,expanding是相当低效的。更不用说在DataFrame中存储列表或集合从一开始就不是一个好主意。
所以答案是:如果你的数据不是数字,并且函数依赖于先前结果和当前元素,只需使用for循环。无论如何,这都会更有效率。

2
我认为您可以使用cumsum函数,但需要注意的是不能对set类型进行操作,因此您需要先将其转换为list类型,再进行操作。另外,不建议在DataFrame列中存储setC2)或lists of listsC4)。
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]

1
好的,你可以定义一个自定义函数。
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()的替代方法。 - ayhan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接