Python 条件numpy累积和

Python 条件numpy累积和,python,numpy,cumulative-sum,Python,Numpy,Cumulative Sum,我正在寻找一种使用numpy计算累积和的方法,但不希望在累积和非常接近零且为负的情况下向前滚动该值(或将其设置为零) 比如说 a = np.asarray([0, 4999, -5000, 1000]) np.cumsum(a) 返回[04999,-11999] 但是,我想在计算过程中将[2]-值(-1)设置为零。问题是,这个决定只能在计算过程中进行,因为中间结果不是先验的 所需的数组是:[04999,01000] 原因是我得到了非常小的值(浮点数,而不是示例中的整数),这是由于浮点计算造成的

我正在寻找一种使用
numpy
计算累积和的方法,但不希望在累积和非常接近零且为负的情况下向前滚动该值(或将其设置为零)

比如说

a = np.asarray([0, 4999, -5000, 1000])
np.cumsum(a)
返回
[04999,-11999]

但是,我想在计算过程中将
[2]
-值(-1)设置为零。问题是,这个决定只能在计算过程中进行,因为中间结果不是先验的

所需的数组是:
[04999,01000]

原因是我得到了非常小的值(浮点数,而不是示例中的整数),这是由于浮点计算造成的,实际上应该是零。计算累积和会使那些导致错误的值复合。

可以解决这个问题。不幸的是,事实并非如此。这意味着需要自定义实现:

def kahan_cumsum(x):
    x = np.asarray(x)
    cumulator = np.zeros_like(x)
    compensation = 0.0

    cumulator[0] = x[0]    
    for i in range(1, len(x)):
        y = x[i] - compensation
        t = cumulator[i - 1] + y
        compensation = (t - cumulator[i - 1]) - y
        cumulator[i] = t
    return cumulator

我必须承认,这并不是问题中所要求的。(在本例中,积算和的第三个输出值为-1是正确的)。但是,我希望这能解决问题背后的实际问题,这与浮点精度有关。

我想知道舍入是否能满足您的要求:

np.cumsum(np.around(a,-1))
# the -1 means it rounds to the nearest 10
给予


这与您在回答中输入的预期数组不完全相同,但是当您将其应用于浮点问题时,使用
小数设置为0可能会起作用。

最好的方法可能是用Cython(文件名为cumsum_eps.pyx)写入此位:

或者像这样(Linux use.so extension/Cygwin use.dll extension,gcc):

然后像这样使用:

from cumsum_eps import *
import numpy as np
x = np.array([[1,2,3,4], [5,6,7,8]], dtype=np.float32)

>>> print cumsum_eps(x)
[  1.   3.   6.  10.  15.  21.  28.  36.]
>>> print cumsum_eps(x, axis=0)
[[  1.   2.   3.   4.]
 [  6.   8.  10.  12.]]
>>> print cumsum_eps(x, axis=1)
[[  1.   3.   6.  10.]
 [  5.  11.  18.  26.]]
>>> print cumsum_eps(x, axis=0, eps=1)
[[  1.   2.   3.   4.]
 [  6.   8.  10.  12.]]
>>> print cumsum_eps(x, axis=0, eps=2)
[[  0.   2.   3.   4.]
 [  5.   8.  10.  12.]]
>>> print cumsum_eps(x, axis=0, eps=3)
[[  0.   0.   3.   4.]
 [  5.   6.  10.  12.]]
>>> print cumsum_eps(x, axis=0, eps=4)
[[  0.   0.   0.   4.]
 [  5.   6.   7.  12.]]
>>> print cumsum_eps(x, axis=0, eps=8)
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  8.]]
>>> print cumsum_eps(x, axis=1, eps=3)
[[  0.   0.   3.   7.]
 [  5.  11.  18.  26.]]
以此类推,当然通常eps是一些小值,这里使用整数只是为了演示/便于键入

如果您也需要double,那么_f8变体编写起来很简单,另一种情况必须在cumsum_eps()中处理

当您对实现感到满意时,应将其作为安装程序的适当部分。py-

更新#1:如果您在运行环境中拥有良好的编译器支持,您可以尝试[Theano][3]实现补偿算法或您的原始想法:

import numpy as np
import theano
import theano.tensor as T
from theano.ifelse import ifelse

A=T.vector('A')

sum=T.as_tensor_variable(np.asarray(0, dtype=np.float64))

res, upd=theano.scan(fn=lambda cur_sum, val: ifelse(T.lt(cur_sum+val, 1.0), np.asarray(0, dtype=np.float64), cur_sum+val), outputs_info=sum, sequences=A)

f=theano.function(inputs=[A], outputs=res)

f([0.9, 2, 3, 4])

将给出[0234]输出。在Cython或this中,您至少可以获得+/-本机代码的性能。

是的,我可能会在Cython中使用我自己的,因为它看起来不像是一个简单的numpy解决方案。顺便说一句,您不需要预编译pyx文件。您可以使用
pyximport.install导入它,它会在第一次导入时编译。我假设运行环境不是构建环境(即没有C编译器)。我明白了。在我的例子中,运行环境具有编译功能。如果您有良好的编译器支持,您也可以尝试Theano<代码>导入No从No.ifelse导入ifelse A=T.vector('A')sum=T.as_tensor_变量(np.asarray(0,dtype=np.float64))res,upd=theano.scan(fn=lambda cur_sum,val:ifelse(T.lt(cur_sum+val,1.0),np.asarray(0,dtype=np.float64),cur_sum+val),输出函数(输入=[A],outputs=res)f([0.9,2,3,4])
将给出[0.2,3,4]输出,但我认为这行不通。决定是否舍入的中间值是在累积和计算期间创建的。它的第一次出现可以在求和后四舍五入,但它已经被添加到数组中我试图避免的下一个元素中。@orange您能给您的问题添加一个带浮点数的示例吗?谢谢。很高兴知道。但是数值误差不是聚合的结果,而是在以前的(更复杂的)计算中已经发生了。你确定这个错误实际上是个问题吗?求和通常对小偏差非常稳健。如果这些值足够接近零(达到数值精度),则结果总和可能足够接近所需结果。只是想一想:)你可能是对的。复合误差可能没有什么大不了的,因为它无论如何都接近于零,并且不会在累积和循环中经历更多的迭代。我可能已经把它复杂化了,希望得到一个“更干净”的解决方案……数组中“所需”部分的值有多大?如果负值接近于零,那么仅仅让它们累积起来会对你的计算造成多大的损害?事实上,请看我的评论。我也得出了这个结论。
\Python27\Scripts\cython.exe cumsum_eps.pyx
cl /c cumsum_eps.c /IC:\Python27\include /IC:\Python27\Lib\site-packages\numpy\core\include
F:\Users\sadaszew\Downloads>link /dll cumsum_eps.obj C:\Python27\libs\python27.lib /OUT:cumsum_eps.pyd
cython cumsum_eps.pyx
gcc -c cumsum_eps.c -o cumsum_eps.o -I/usr/include/python2.7 -I/usr/lib/python2.7/site-packages/numpy/core/include
gcc -shared cumsum_eps.o -o cumsum_eps.so -lpython2.7
from cumsum_eps import *
import numpy as np
x = np.array([[1,2,3,4], [5,6,7,8]], dtype=np.float32)

>>> print cumsum_eps(x)
[  1.   3.   6.  10.  15.  21.  28.  36.]
>>> print cumsum_eps(x, axis=0)
[[  1.   2.   3.   4.]
 [  6.   8.  10.  12.]]
>>> print cumsum_eps(x, axis=1)
[[  1.   3.   6.  10.]
 [  5.  11.  18.  26.]]
>>> print cumsum_eps(x, axis=0, eps=1)
[[  1.   2.   3.   4.]
 [  6.   8.  10.  12.]]
>>> print cumsum_eps(x, axis=0, eps=2)
[[  0.   2.   3.   4.]
 [  5.   8.  10.  12.]]
>>> print cumsum_eps(x, axis=0, eps=3)
[[  0.   0.   3.   4.]
 [  5.   6.  10.  12.]]
>>> print cumsum_eps(x, axis=0, eps=4)
[[  0.   0.   0.   4.]
 [  5.   6.   7.  12.]]
>>> print cumsum_eps(x, axis=0, eps=8)
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  8.]]
>>> print cumsum_eps(x, axis=1, eps=3)
[[  0.   0.   3.   7.]
 [  5.  11.  18.  26.]]
import numpy as np
import theano
import theano.tensor as T
from theano.ifelse import ifelse

A=T.vector('A')

sum=T.as_tensor_variable(np.asarray(0, dtype=np.float64))

res, upd=theano.scan(fn=lambda cur_sum, val: ifelse(T.lt(cur_sum+val, 1.0), np.asarray(0, dtype=np.float64), cur_sum+val), outputs_info=sum, sequences=A)

f=theano.function(inputs=[A], outputs=res)

f([0.9, 2, 3, 4])