对数组中每个元素之后的所有内容求和

时间:2019-02-22 22:43:28

标签: python numpy

我有一个numPy数组,例如arr = [1, 2, 3, 4],我想对每个元素后的元素求和,得出s = [10, 9, 7, 4]

在循环中,可以这样做:

for i in range(arr.size):
    if i == 0:
        s[i] = np.sum(arr)
    else:
        s[i] = np.sum(arr[:-i])

5 个答案:

答案 0 :(得分:3)

您可以为此使用numpy的cumulative sum函数。您需要先反转原始数组,然后反转结果以按想要的顺序获取它:

a = np.array([1,2,3,4])
np.flip(np.cumsum(np.flip(a)))  # array([10,  9,  7,  4], dtype=int32)

或者使用[::-1]进行反转:

np.cumsum(a[::-1])[::-1]

this question的回答包括对在python中计算累积总和的不同选项的完整讨论。 itertools.accumulate在Python 3.2或更高版本中似乎是一个不错的选择。

答案 1 :(得分:2)

您可以使用numpy的ufuncs及其accumulate函数来获取所需的输出。

np.add.accumulate(arr[::-1])[::-1]

答案 2 :(得分:2)

这是一种简洁(尽管成本很高)的方式:

arr = [1, 2, 3, 4] 
s   = np.sum(np.triu(arr),1)

尽管这是仅使用矩阵运算符的非过程(概念性)方法,但它是迄今为止最慢的解决方案。

我尝试了这里提出的各种解决方案,并亲自研究了哪种方法最快。结果如下:

subFromSum     0.06761518699999997  @Sarcoma
procedural     0.07242122200000001  @Alain T.
generator      0.08231979099999998  @Sarcoma
recursive      0.10890062199999995  @Alain T.
arraySum       0.1370264969999999   @JosepJoestar
listComp       0.13318894400000003  @student
iterAccumulate 0.14017220000000008  @Stuart (linked in comment)
funcReduce     0.1828948370000001   @Alain T.
npAccumulate   0.23582439700000002  @user2699  
npCumSum       0.60332129           @Suart
npSumTriu      1.951785406          @Alain T.

所有numpy函数最后死在一个小列表上。

同一测试在更大的数组上执行:[1,2,3,4] * 100(重复10000次,而不是100000次)得出不同的结果,反映了这些解决方案的可伸缩性:

iterAccumulate 0.12888180999999932  @Stuart (linked in comment)
generator      0.24920542199999995  @Sarcoma
procedural     0.2719608119999999   @Alain T.
npCumSum       0.27731459299999983  @Suart
npAccumulate   0.30234721600000114  @user2699
subFromSum     0.339745362          @Sarcoma
funcReduce     1.845360363000001    @Alain T.
recursive      2.2268321760000003   @Alain T.
npSumTriu      3.234387397999999    @Alain T.
listComp       6.1435246800000005   @student
arraySum       6.342716752          @JosepJoestar

numpy开始在大型阵列上显示其功能,但对于这种类型的问题仍然不是最好的选择。 itertools模块(累积)似乎是最可扩展的方法。

这里是功能...

from timeit import timeit

array = [1, 2, 3, 4] 

# Subtracting from sum :: @Sarcoma
# timeit: 0.6
def subFromSum(arr):
    total = sum(arr)
    result = []
    for value in arr:
        result.append(total)
        total -= value
    return result
print("subFromSum    ", timeit(lambda :subFromSum(array), number=100000))


# Procedure for-loop assigning list items
# timeit: 0.07
def procedural(arr): 
    result = arr.copy()
    total  = 0
    index  = len(arr)-1 
    for value in reversed(arr):
        total += value
        result[index] = total
        index -= 1
    return result
print("procedural    ", timeit(lambda :procedural(array), number=100000))

# generator :: @Sarcoma
# timeit: 0.08
def gen(a):
    r = 0
    for x in a:
        r += x
        yield r
def generator(arr):
    return [*gen(arr[::-1])][::-1]
print("generator     ", timeit(lambda : generator(array), number=100000))


# recursive concatenation
# timeit: 0.11
def recursive(arr,size=None):
    size = (size or len(arr))
    value = arr[size-1]
    if size == 1 : return [value]
    previous = recursive(arr,size-1)
    return previous + [value+previous[-1]]
print("recursive     ", timeit(lambda :recursive(array), number=100000))

# iterative array sum()  :: @JosepJoestar
# timeit: 0.14
def arraySum(arr):
    s = []
    for i in range(len(arr)):
        s.append(sum(arr[i:]))
    return s
print("arraySum      ", timeit(lambda : arraySum(array), number=100000))

# list comprehension :: @student
# timeit: 0.13
def listComp(arr):
    return [sum(arr[i:]) for i in range(len(arr))]
print("listComp      ", timeit(lambda : listComp(array), number=100000))

# accumulate() function form itertools
# timeit: 0.14
def iterAccumulate(arr): 
    from itertools import accumulate
    return list(accumulate(arr[::-1]))[::-1]
print("iterAccumulate", timeit(lambda : iterAccumulate(array), number=100000))

# assigning list items using functools' reduce() function
# timeit: 0.18
def funcReduce(arr):
    from functools import reduce
    return reduce(lambda a,v: a + [a[-1]-v], arr[1:], [sum(arr)])
print("funcReduce    ", timeit(lambda : funcReduce(array), number=100000))

# npAccumulate() function form numpy :: @ user2699
# timeit: 0.24
def mpAccumulate(arr):
    import numpy as np
    return np.add.accumulate(arr[::-1])[::-1]
print("npAccumulate  ", timeit(lambda : mpAccumulate(array), number=100000))

# numpy's cumsum() function
# timeit: 0.55
def npCumSum(arr): 
    from numpy import cumsum
    return cumsum(arr[::-1])[::-1]
print("npCumSum      ", timeit(lambda : npCumSum(array), number=100000))

# conceptual matrix operations (using numpy)
# timeit: 2.05
def npSumTriu(arr): 
    import numpy as np
    return np.sum(np.triu(arr),1)
print("npSumTriu     ", timeit(lambda : npSumTriu(array), number=100000))

答案 3 :(得分:1)

求和一次,将当前总数t附加到结果数组r上,并减去当前值a

arr = [1, 2, 3, 4]

t = sum(arr)
r = []
for a in arr:
    r.append(t)
    t -= a

print r

虽然它不是一个Numpy数组,这很重要吗?

一些其他答案似乎将每次迭代的其余部分相加。在我看来这效率低下。

@ User2699指出,反转数组并将其简单地相加是实现此目的的最有效方法。

我能找到的最快方法是使用发电机:

def gen(a):
    r = 0
    for x in a:
        r += x
        yield r


def reverse_sum_with_generator(arr):
    return [*gen(arr[::-1])][::-1]

更新

我发现有趣的是,基于Numpy的脚本似乎使Numpy Array快了多少。因此,我进行了进一步的测试,以了解其原因。

我意识到,我并没有考虑到生成列表的方式。每种创建列表的方法都有不同的开销,这在大多数情况下都说明了速度的差异。杰出的例外是np.arange(),这对于基于Numpy的脚本来说要快得多。

基准:https://repl.it/repls/SumEachItemAfter

基准要点:https://gist.github.com/sarcoma/8fc4b87c3cf649d6ef9af92bffe5a771

答案 4 :(得分:0)

我认为您的意思是之后,而不是之前。要对每个元素之后的元素求和,请执行以下操作:

s = []
for i in range(len(arr)):
    s.append(sum(arr[i:]))

或者您也可以使用列表理解符号来使其更短,更优雅:

s = [sum(arr[i:]) for i in range(len(arr))]

但是,当列表足够大时,这可能会导致性能下降。就效率而言,最好的解决方案是对列表进行迭代,以仅计算所见的最后一个元素与当前元素之和,因为最后一个元素是先前所有元素的总和:

s = list(arr) # Avoid copying reference if modify arr is not desired

# Iterate from last position - 1, until first position, descending
for i in range(len(arr) - 2, -1, -1):
    s[i] += s[i + 1]