在 Python Pandas 中使用 cumsum 和 groupby 并在值为 0 时重置 cumsum

Posted

技术标签:

【中文标题】在 Python Pandas 中使用 cumsum 和 groupby 并在值为 0 时重置 cumsum【英文标题】:In Python Pandas using cumsum with groupby and reset of cumsum when value is 0 【发布时间】:2017-02-06 01:23:21 【问题描述】:

我对 python 比较陌生。 我尝试为每个客户获取一个累积总和,以查看相应的不活动月份(标志:1 或 0)。因此,当我们有一个 0 时,需要重置 1 的累积总和。当我们有一个新客户端时,也需要进行重置。请参阅下面的示例,其中 a 是客户列,b 是日期。

经过一些研究,我发现了“Cumsum 在 NaN 处重置”和“在 Python Pandas 中使用 cumsum 和 groupby”的问题。我想我有点需要把它们放在一起。 将'Cumsum reset at NaN'的代码调整为向0重置,成功:

cumsum = v.cumsum().fillna(method='pad')
reset = -cumsum[v.isnull() !=0].diff().fillna(cumsum)
result = v.where(v.notnull(), reset).cumsum()

但是,我没有成功添加 groupby。我的计数还在继续......

所以,数据集应该是这样的: 将熊猫导入为 pd

df =  pd.DataFrame('a' : [1,1,1,1,1,1,1,2,2,2,2,2,2,2], 
                    'b' : [1/15,2/15,3/15,4/15,5/15,6/15,1/15,2/15,3/15,4/15,5/15,6/15], 
                    'c' : [1,0,1,0,1,1,0,1,1,0,1,1,1,1])

这应该会产生一个包含 a、b、c 和 d 列的数据框,其中包含

'd' : [1,0,1,0,1,2,0,1,2,0,1,2,3,4]

请注意,我有一个非常大的数据集,因此计算时间非常重要。

谢谢你帮助我

【问题讨论】:

你看过这个吗? ***.com/questions/18196811/cumsum-reset-at-nan 【参考方案1】:

在组中找到连续值后使用groupby.applycumsum。然后groupby.cumcount 将整数计数到每个连续值,然后加1。

与原始行相乘以创建取消所有零且仅考虑正值的 AND 逻辑。

df['d'] = df.groupby('a')['c']                                                            \
            .apply(lambda x: x * (x.groupby((x != x.shift()).cumsum()).cumcount() + 1))

print(df['d'])

0     1
1     0
2     1
3     0
4     1
5     2
6     0
7     1
8     2
9     0
10    1
11    2
12    3
13    4
Name: d, dtype: int64

另一种做法是在series.expanding 之后对 groupby 对象应用一个函数,该函数基本上计算从第一个索引到当前索引的系列值。

稍后使用reduce 将两个args的函数累积应用于iterable的项目,以将其减少为单个值。

from functools import reduce

df.groupby('a')['c'].expanding()                                         \
  .apply(lambda i: reduce(lambda x, y: x+1 if y==1 else 0, i, 0))

a    
1  0     1.0
   1     0.0
   2     1.0
   3     0.0
   4     1.0
   5     2.0
   6     0.0
2  7     1.0
   8     2.0
   9     0.0
   10    1.0
   11    2.0
   12    3.0
   13    4.0
Name: c, dtype: float64

时间安排:

%%timeit
df.groupby('a')['c'].apply(lambda x: x * (x.groupby((x != x.shift()).cumsum()).cumcount() + 1))
100 loops, best of 3: 3.35 ms per loop

%%timeit
df.groupby('a')['c'].expanding().apply(lambda s: reduce(lambda x, y: x+1 if y==1 else 0, s, 0))
1000 loops, best of 3: 1.63 ms per loop

【讨论】:

这太棒了,正是我想要的,+1 时间和易于阅读的代码【参考方案2】:

我认为您需要 groupby 的自定义函数:

#change row with index 6 to 1 for better testing
df =  pd.DataFrame('a' : [1,1,1,1,1,1,1,2,2,2,2,2,2,2], 
                    'b' : [1/15,2/15,3/15,4/15,5/15,6/15,1/15,2/15,3/15,4/15,5/15,6/15,7/15,8/15], 
                    'c' : [1,0,1,0,1,1,1,1,1,0,1,1,1,1],
                    'd' : [1,0,1,0,1,2,3,1,2,0,1,2,3,4])

print (df)
    a         b  c  d
0   1  0.066667  1  1
1   1  0.133333  0  0
2   1  0.200000  1  1
3   1  0.266667  0  0
4   1  0.333333  1  1
5   1  0.400000  1  2
6   1  0.066667  1  3
7   2  0.133333  1  1
8   2  0.200000  1  2
9   2  0.266667  0  0
10  2  0.333333  1  1
11  2  0.400000  1  2
12  2  0.466667  1  3
13  2  0.533333  1  4
def f(x):
    x.ix[x.c == 1, 'e'] = 1
    a = x.e.notnull()
    x.e = a.cumsum()-a.cumsum().where(~a).ffill().fillna(0).astype(int)
    return (x)

print (df.groupby('a').apply(f))
    a         b  c  d  e
0   1  0.066667  1  1  1
1   1  0.133333  0  0  0
2   1  0.200000  1  1  1
3   1  0.266667  0  0  0
4   1  0.333333  1  1  1
5   1  0.400000  1  2  2
6   1  0.066667  1  3  3
7   2  0.133333  1  1  1
8   2  0.200000  1  2  2
9   2  0.266667  0  0  0
10  2  0.333333  1  1  1
11  2  0.400000  1  2  2
12  2  0.466667  1  3  3
13  2  0.533333  1  4  4

【讨论】:

以上是关于在 Python Pandas 中使用 cumsum 和 groupby 并在值为 0 时重置 cumsum的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中使用 pandas 从文本文件创建列表

如何在 Python 中使用 Pandas 创建会计年度列?

使用 numpy / pandas 读取 Python 中 CSV 文件的最后 N 行

使用 numpy / pandas 读取 Python 中 CSV 文件的最后 N 行

Python pandas用法

使用 Pandas 在 Python 中过滤嵌套的 JSON 数据