我有一个带有这样两列的 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]
我正在寻找一种更有效的方法来在纯熊猫中执行此操作。
这可以使用
numpy
来完成,但比 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]
这只是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)
使用列表或数组没有区别。但如果你即时编译这个函数,情况就不是这样了。
与 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)
即使在更复杂的情况下(最终数组大小未知,或者只知道最大数组大小),分配一个数组并在最后收缩它通常是有意义的,或者在简单的情况下,甚至运行一次算法来知道最终的结果数组大小并比进行实际计算。