【问题标题】:Perfrom cumulative sum over a column but reset to 0 if sum become negative in Pandas对一列执行累积求和,但如果求和在 Pandas 中变为负数,则重置为 0
【发布时间】:2019-12-21 22:48:08
【问题描述】:

我有一个带有两列的 pandas 数据框,

Item    Value
0   A   7
1   A   2
2   A   -6
3   A   -70
4   A   8
5   A   0

我想在 Value 列上累积总和。但是,如果值变为负数,则在创建累积和时,我想将其重置回 0。

我目前正在使用如下所示的循环来执行此操作,

sum_ = 0
cumsum = []

for val in sample['Value'].values:
    sum_ += val
    if sum_ < 0:
        sum_ = 0
    cumsum.append(sum_)

print(cumsum) # [7, 9, 3, 0, 8, 8]

我正在寻找一种更有效的方法来在纯熊猫中执行此操作。

【问题讨论】:

  • 我想我们没有pandas方法可以实现这个
  • 我也有同样的想法,最终解决了我在问题中发布的循环的解决方案。我想知道我错过了一些可以施展魔法的熊猫把戏
  • 你所做的更像我可以提供的,只是我可能使用 numba 略有不同
  • stackoverflow.com/questions/56904390/… 你可以从那里得到一些解决方案,即使那不是 100% 相同
  • 好问题,将此作为improvement suggestion 发布到 GitHub 上的pandas @WeNYoBen

标签: python pandas


【解决方案1】:

这只是 WeNYoBen 的评论。

如果您可以避免使用列表,通常建议您避免使用它。

示例

from numba import njit
import numpy as np

#with lists
@njit()
def cumli(x, lim):
    total = 0
    result = []
    for i, y in enumerate(x):
        total += y
        if total < lim:
            total = 0
        result.append(total)
    return result

#without lists
@njit()
def cumli_2(x, lim):
    total = 0.
    result = np.empty_like(x)
    for i, y in enumerate(x):
        total += y
        if total < lim:
            total = 0.
        result[i]=total
    return result

时间

没有 Numba(注释掉@njit()):

x=(np.random.rand(1_000)-0.5)*5

  %timeit a=cumli(x, 0.)
  220 µs ± 2.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  %timeit a=cumli_2(x, 0.)
  227 µs ± 1.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

使用列表或数组没有区别。但如果你对这个函数进行 Jit 编译,情况就不是这样了。

使用 Numba:

  %timeit a=cumli(x, 0.)
  27.4 µs ± 210 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
  %timeit a=cumli_2(x, 0.)
  2.96 µs ± 32.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

即使在更复杂的情况下(最终数组大小未知,或者只知道最大数组大小),分配一个数组并在最后缩小它通常是有意义的,或者在简单的情况下,甚至运行一次算法即可知道最终的数组大小,而不是真正的计算。

【讨论】:

    【解决方案2】:

    稍微修改一下这个方法也比较慢,numba解决方案

    sumlm = np.frompyfunc(lambda a,b: 0 if a+b < 0 else a+b,2,1)
    newx=sumlm.accumulate(df.Value.values, dtype=np.object)
    newx
    Out[147]: array([7, 9, 3, 0, 8, 8], dtype=object)
    

    numba解决方案

    from numba import njit
    @njit
    def cumli(x, lim):
        total = 0
        result = []
        for i, y in enumerate(x):
            total += y
            if total < lim:
                total = 0
            result.append(total)
        return result
    cumli(df.Value.values,0)
    Out[166]: [7, 9, 3, 0, 8, 8]
    

    【讨论】:

    • 太棒了,按预期工作。我会在测试时间后回复您。
    • 这种方法是否曾针对 divakar 和 pirsquared 的解决方案进行过测试?只是想知道速度
    • @Erfan 不确定速度,但因为我列出了上面的链接,所以 op 可以选择他想要的:-)
    • @SreeramTP 给你,我会说我的原始方法更具可读性,但最慢。
    • 您可以获得大约。如果您使用数组而不是列表,则加速 10 倍。例如。 result = np.empty_like(x); idx=0 像这样将结果写入数组result[idx]=total; idx+=1 并在末尾缩小数组return result[:idx]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-16
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    相关资源
    最近更新 更多