dtype对象的累积操作

时间:2016-04-19 11:45:09

标签: python pandas dataframe

我试图找出如何将累积函数应用于对象。对于数字,有几种选择,例如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。我正在寻找支持将函数应用于这两个输入的通用解决方案:前一个结果和当前元素。

3 个答案:

答案 0 :(得分:2)

我认为您可以使用cumsum异常set,然后您需要先转换为list然后转换为set。顺便说一下,不建议在set的列中存储C2lists)或lists C4DataFrame)。

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 :(得分:2)

原来这不可能。

继续使用相同的样本:

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]

如果列的类型是object,则永远不会调用该函数。并且pandas没有可用于对象的替代方案。 rolling().apply()也是如此。

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

所以答案是:如果你的数据不是数字而且函数依赖于前面的结果和当前元素,那么只需使用for循环。无论如何它会更有效率。

答案 2 :(得分: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