목록에서 숫자의 누적 합계를 찾는 방법은 무엇입니까?


92
time_interval = [4, 6, 12]

나는 [4, 4+6, 4+6+12]목록을 얻기 위해 같은 숫자를 요약하고 싶습니다 t = [4, 10, 22].

다음을 시도했습니다.

t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3)  # -> 4 10 22

답변:


128

이와 같은 배열로 많은 수치 작업을 수행하는 경우 numpy누적 합계 함수와 함께 제공되는을 제안 합니다 cumsum.

import numpy as np

a = [4,6,12]

np.cumsum(a)
#array([4, 10, 22])

Numpy는 종종 이런 종류의 경우 순수한 파이썬보다 빠릅니다 . @Ashwini의accumu 다음 과 비교하여보십시오 .

In [136]: timeit list(accumu(range(1000)))
10000 loops, best of 3: 161 us per loop

In [137]: timeit list(accumu(xrange(1000)))
10000 loops, best of 3: 147 us per loop

In [138]: timeit np.cumsum(np.arange(1000))
100000 loops, best of 3: 10.1 us per loop

그러나 물론 그것이 당신이 numpy를 사용할 유일한 곳이라면 그것에 의존 할 가치가 없을 수도 있습니다.


3
np.cumsun변환 시간을 고려하여 목록으로 시작 하는 케이스 가 있어야합니다 .
hpaulj

3
@hpaulj에서 시작하는 (또는 목표로하는) 사람들에게 좋은 점 list은 추천하지 않습니다 numpy.
askewchan

numpy가 가장 빠르다고 생각하지 않습니다. stackoverflow.com/questions/15889131/…
Chris_Rands

3
위에서 언급했듯이 동의합니다. / - : 당신처럼 반응을 내 대답의 처음과 마지막 라인의 범위를 제한하려고하는 이유와 @ hpaulj의입니다 방지
askewchan

1
@ 알렉스 : 사용 timeit, "경우에 -n주어지지 않는다, 루프의 적절한 수는 총 시간은 적어도 0.2 초까지 (10)의 연속적인 힘을 시도하여 계산됩니다." 차이를 만들 것으로 기대한다면 -n 1000모두 동등하게 만들 수 있습니다.
askewchan

94

Python 2에서는 다음과 같이 고유 한 생성기 함수를 정의 할 수 있습니다.

def accumu(lis):
    total = 0
    for x in lis:
        total += x
        yield total

In [4]: list(accumu([4,6,12]))
Out[4]: [4, 10, 22]

그리고 Python 3.2 이상에서는 다음을 사용할 수 있습니다 itertools.accumulate().

In [1]: lis = [4,6,12]

In [2]: from itertools import accumulate

In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]

5
PEP 572-할당 표현식 (Python 3.8에서 예상 됨)은 흥미로운 대안을 보여줍니다 total = 0; partial_sums = [total := total + v for v in values]. 나는 여전히 accumulate더 빠를 것으로 기대 합니다.
Steven Rumbalski

3
@StevenRumbalski Man, 개인적으로 그것이 최악의 PEP라고 생각합니다. 나쁜 충분히 ...
애쉬 위니 Chaudhary

19

보다:

a = [4, 6, 12]
reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]

예상대로 출력됩니다.

[4, 10, 22]

17
효율적이지 않습니다 . 계속해서 수행하는 데 드는 총 비용은 c + [c[-1] + x]입력 길이에서 총 런타임 2 차가됩니다.
user2357112 모니카 지원

reduce는 일회성 누적 합계에 적합하지만 cumsum 함수에 대한 많은 호출을 수행하는 경우 생성기는 cumulative_sum 값을 "전처리"하고 각 후속 호출에 대해 O (1)에서 액세스하는 데 유용합니다.
Scott Skiles

17

파이썬 3.4과 상단이 답변의 벤치 마크를했고 나는 발견 itertools.accumulate빨리보다 numpy.cumsum종종 더 빨리, 많은 상황에서. 그러나 의견에서 알 수 있듯이 항상 그런 것은 아니며 모든 옵션을 철저히 탐색하기가 어렵습니다. (관심있는 벤치 마크 결과가 더 있으면 의견을 추가하거나이 게시물을 편집하십시오.)

일부 타이밍 ...

짧은 목록의 accumulate경우 약 4 배 더 빠릅니다.

from timeit import timeit

def sum1(l):
    from itertools import accumulate
    return list(accumulate(l))

def sum2(l):
    from numpy import cumsum
    return list(cumsum(l))

l = [1, 2, 3, 4, 5]

timeit(lambda: sum1(l), number=100000)
# 0.4243644131347537
timeit(lambda: sum2(l), number=100000)
# 1.7077815784141421

긴 목록의 accumulate경우 약 3 배 더 빠릅니다.

l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416

numpy array캐스트되지 않은 경우list , accumulate빠른 2 회에 대해 여전히 :

from timeit import timeit

def sum1(l):
    from itertools import accumulate
    return list(accumulate(l))

def sum2(l):
    from numpy import cumsum
    return cumsum(l)

l = [1, 2, 3, 4, 5]*1000

print(timeit(lambda: sum1(l), number=100000))
# 19.18597290944308
print(timeit(lambda: sum2(l), number=100000))
# 37.759664884768426

당신은이 개 기능으로 외부의 수입을 놓고 여전히 돌아 가면 numpy array, accumulate거의 2 배 빠른 여전히 :

from timeit import timeit
from itertools import accumulate
from numpy import cumsum

def sum1(l):
    return list(accumulate(l))

def sum2(l):
    return cumsum(l)

l = [1, 2, 3, 4, 5]*1000

timeit(lambda: sum1(l), number=100000)
# 19.042188624851406
timeit(lambda: sum2(l), number=100000)
# 35.17324400227517

10
특히 티켓 구매 및 보안 검색을 포함하여 도시를 여행하는 기차보다 비행기가 더 빠를 것이라고 기대하지 않을 것입니다. 마찬가지로 list5 개 항목 중 a를 처리하는 데 numpy를 사용하지 않을 것입니다 . 특히 array대가 로 수락하지 않으려는 경우에는 더욱 그렇습니다 . 문제의 목록이 너무 짧다면 실행 시간이 중요하지 않을 것입니다 .-- 의존성과 가독성이 확실히 지배적입니다. 그러나 list상당한 길이의 균일 한 숫자 데이터 유형을 광범위하게 사용하는 것은 어리석은 일입니다. 이를 위해 numpy array 적절하고 일반적으로 더 빠릅니다.
askewchan

@askewchan 잘 나는 짧은 목록에 대해 이것을 찾지 않으며 OP의 질문은 numpy 배열이 아닌 출력으로 목록을 요청합니다. 각각의 사용 : 적절한 때 아마 당신은에 명확하게하는 답변을 편집 할 수 있습니다
Chris_Rands

@askewchan 사실 나는 훨씬 더 자세한 비교를 통해 내 대답을 편집했습니다. 어떤 상황에서도 numpy간과하지 않는 한 더 빠르다 는 것을 알 수 있습니까?
Chris_Rands

2
오, 그렇습니다 :) 나는 당신이 무언가를 간과했다고 말하지 않을 것입니다. 그러나 당신의 입력과 출력을 고려하지 않고는 비교하기가 어렵습니다. sum2함수 에서 대부분의 시간 은 아마도 l배열 로 변환 하는 것입니다 . 타이밍 a = np.array(l)np.cumsum(a)별도로 시도하십시오 . 그런 다음 a = np.tile(np.arange(1, 6), 1000)l = [1,2,3,4,5]*1000. 다른 수치 프로세스를 수행하는 프로그램 l에서 (처음에 생성 또는로드하는 것과 같은 ) 작업 데이터는 아마도 이미 배열에있을 것이고 생성은 일정한 비용이 될 것입니다.
askewchan 18:39에

1
@askewchan 나는 당신과 같은 아이디어를 얻었으므로 a = np.array (l) 시간을 보냈습니다. 목록으로 변환하지 않고 numpy 배열을 입력으로 사용하는 sum2의 경우 sum2는 긴 목록 / 배열의 경우 내 컴퓨터에서 sum1에게 감사드립니다.
Mantxu

9

이것을 시도하십시오 : 누적 기능과 함께 연산자 추가는 실행 추가를 수행합니다.

import itertools  
import operator  
result = itertools.accumulate([1,2,3,4,5], operator.add)  
list(result)

5
operator.add어쨌든 기본 작업은 추가이므로 통과 할 필요가 없습니다 .
Eugene Yarmash

8

PEP 572 (Python 3.8의 새로운 기능)의 할당 표현식 이를 해결하는 또 다른 방법을 제공합니다.

time_interval = [4, 6, 12]

total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]

5

간단한 for루프를 사용하여 선형 시간의 누적 합계 목록을 계산할 수 있습니다 .

def csum(lst):
    s = lst.copy()
    for i in range(1, len(s)):
        s[i] += s[i-1]
    return s

time_interval = [4, 6, 12]
print(csum(time_interval))  # [4, 10, 22]

표준 라이브러리 itertools.accumulate는 더 빠른 대안이 될 수 있습니다 (C로 구현 되었기 때문에).

from itertools import accumulate
time_interval = [4, 6, 12]
print(list(accumulate(time_interval)))  # [4, 10, 22]

2
values = [4, 6, 12]
total  = 0
sums   = []

for v in values:
  total = total + v
  sums.append(total)

print 'Values: ', values
print 'Sums:   ', sums

이 코드를 실행하면

Values: [4, 6, 12]
Sums:   [4, 10, 22]

2

Python3에서 ith 요소가 원래 목록의 첫 번째 i + 1 요소의 합인 목록의 누적 합계를 찾으려면 다음을 수행 할 수 있습니다.

a = [4 , 6 , 12]
b = []
for i in range(0,len(a)):
    b.append(sum(a[:i+1]))
print(b)

또는 목록 이해력을 사용할 수 있습니다.

b = [sum(a[:x+1]) for x in range(0,len(a))]

산출

[4,10,22]

이것은 옳게 보이지만 문서에 대한 링크를 삭제할 수 있습니다.
S Meaden

2

2.7에서 numpy가 작동하지 않는 파이썬적인 방법을 원한다면 이것이 내 방법이 될 것입니다.

l = [1,2,3,4]
_d={-1:0}
cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]

이제 시도해보고 다른 모든 구현에 대해 테스트 해 보겠습니다.

import timeit, sys
L=list(range(10000))
if sys.version_info >= (3, 0):
    reduce = functools.reduce
    xrange = range


def sum1(l):
    cumsum=[]
    total = 0
    for v in l:
        total += v
        cumsum.append(total)
    return cumsum


def sum2(l):
    import numpy as np
    return list(np.cumsum(l))

def sum3(l):
    return [sum(l[:i+1]) for i in xrange(len(l))]

def sum4(l):
    return reduce(lambda c, x: c + [c[-1] + x], l, [0])[1:]

def this_implementation(l):
    _d={-1:0}
    return [_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]


# sanity check
sum1(L)==sum2(L)==sum3(L)==sum4(L)==this_implementation(L)
>>> True    

# PERFORMANCE TEST
timeit.timeit('sum1(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.001018061637878418

timeit.timeit('sum2(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.000829620361328125

timeit.timeit('sum3(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.4606760001182556 

timeit.timeit('sum4(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.18932826995849608

timeit.timeit('this_implementation(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.002348129749298096

2

목록의 길이와 성능에 따라 이에 대한 답변이 많이있을 수 있습니다. 성능을 생각하지 않고 생각할 수있는 매우 간단한 방법은 다음과 같습니다.

a = [1, 2, 3, 4]
a = [sum(a[0:x:1]) for x in range(len(a)+1)][1:]
print(a)

[1, 3, 6, 10]

이것은 목록 이해력을 사용하는 것이며 이것은 상당히 잘 작동 할 수 있습니다. 여기에 하위 배열을 여러 번 추가하고 있기 때문에 즉석에서 간단하게 만들 수 있습니다!

당신의 노력에 건배!


1

먼저 실행중인 하위 시퀀스 목록이 필요합니다.

subseqs = (seq[:i] for i in range(1, len(seq)+1))

그런 다음 sum각 하위 시퀀스를 호출 합니다.

sums = [sum(subseq) for subseq in subseqs]

(모든 접두사를 반복적으로 추가하기 때문에 이것이 가장 효율적인 방법은 아닙니다.하지만 대부분의 사용 사례에서는 중요하지 않을 것이며 생각할 필요가 없다면 이해하기 더 쉽습니다. 누계.)

Python 3.2 이상을 사용 itertools.accumulate하는 경우 다음을 사용 하여 수행 할 수 있습니다.

sums = itertools.accumulate(seq)

이전 3.1을 사용하거나하는 경우, 당신은 곧바로 워드 프로세서의 (변경을 제외하고 소스 "에 해당하는"를 그냥 복사 할 수 있습니다 next(it)it.next()2.5 및 이전 버전에 대한).


9
이것은 2 차 시간으로 실행됩니다 (아마도 OP에는 중요하지 않지만 언급 할 가치가 있습니다).
Chris Taylor

첫째, N = 3 일 때 누가 2 차 시간에 관심이 있습니까? 그리고 나는 그것이 너무 복잡하다고 생각하지 않습니다. 이는 매우 간단한 두 단계로, 각각 하나의 반복자를 다른 반복기로 변환하고 영어 설명을 직접 번역합니다. (그가 0 길이 접두사가 계산되지 않는 시리즈를 정의하는 흔하지 않은 방법을 사용하고 있다는 사실은 좀 더 복잡해 지지만 ... 그것은 문제에 내재되어 있고 나는 그것을 넣는 것이 더 낫다고 생각했습니다. 마지막 range에 수행하여 해킹 [1:]하거나 무시하는 것보다.)
abarnert 2013-04-08

1
아마도 OP의 실제 문제는 [4,6,12]그가 질문에서 썼 듯이 이미 그것이 무엇인지 알고 있기 때문에 부분 합계를 얻는 것이 아닙니다 !
Chris Taylor

@ChrisTaylor : 그는 이미 이것을 작성하는 방법을 알고 있지만 "더 쉽게 작성할 수있는 방법"을 원한다고 명시 적으로 말했습니다.
abarnert 2013

1

이 시도:

result = []
acc = 0
for i in time_interval:
    acc += i
    result.append(acc)

-1
In [42]: a = [4, 6, 12]

In [43]: [sum(a[:i+1]) for i in xrange(len(a))]
Out[43]: [4, 10, 22]

이다 slighlty 빠르게 발생 방법보다 상기 @Ashwini 의해 작은 목록을위한

In [48]: %timeit list(accumu([4,6,12]))
  100000 loops, best of 3: 2.63 us per loop

In [49]: %timeit [sum(a[:i+1]) for i in xrange(len(a))]
  100000 loops, best of 3: 2.46 us per loop

더 큰 목록의 경우 생성기가 확실하게 이동하는 방법입니다. . .

In [50]: a = range(1000)

In [51]: %timeit [sum(a[:i+1]) for i in xrange(len(a))]
  100 loops, best of 3: 6.04 ms per loop

In [52]: %timeit list(accumu(a))
  10000 loops, best of 3: 162 us per loop

1
당신은 단지 3 개의 항목 목록을위한 타이밍입니다. 10 ^ 4 개의 항목을 시도하십시오.
Ashwini Chaudhary 2013

1
사실, 큰 목록의 경우 생성기가 훨씬 빠릅니다!
reptilicus 2011

-1

다소 엉망이지만 작동하는 것 같습니다.

def cumulative_sum(l):
  y = [0]
  def inc(n):
    y[0] += n
    return y[0]
  return [inc(x) for x in l]

내부 함수가 y외부 어휘 범위에서 선언 된 내용 을 수정할 수 있다고 생각 했지만 작동하지 않았으므로 대신 구조 수정을 사용하여 몇 가지 불쾌한 해킹을 수행합니다. 발전기를 사용하는 것이 더 우아 할 것입니다.


-1

Numpy를 사용하지 않고도 배열을 직접 반복하고 그 과정에서 합계를 누적 할 수 있습니다. 예를 들면 :

a=range(10)
i=1
while((i>0) & (i<10)):
    a[i]=a[i-1]+a[i]
    i=i+1
print a

결과 :

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]

-1

누적 합계를위한 순수한 파이썬 oneliner :

cumsum = lambda X: X[:1] + cumsum([X[0]+X[1]] + X[2:]) if X[1:] else X

이것은 재귀 누적 합계에서 영감을 얻은 재귀 버전 입니다. 몇 가지 설명 :

  1. 첫 번째 용어 X[:1]는 이전 요소를 포함하는 목록이며 거의 동일합니다 [X[0]](빈 목록에 대해 불만이 있음).
  2. cumsum두 번째 용어 의 재귀 호출은 현재 요소 [1]와 길이가 1 씩 줄어드는 나머지 목록을 처리합니다 .
  3. if X[1:]이 짧습니다 if len(X)>1.

테스트:

cumsum([4,6,12])
#[4, 10, 22]

cumsum([])
#[]

누적 제품과 유사합니다.

cumprod = lambda X: X[:1] + cumprod([X[0]*X[1]] + X[2:]) if X[1:] else X

테스트:

cumprod([4,6,12])
#[4, 24, 288]

-1
l = [1,-1,3]
cum_list = l

def sum_list(input_list):
    index = 1
    for i in input_list[1:]:
        cum_list[index] = i + input_list[index-1]
        index = index + 1 
    return cum_list

print(sum_list(l))

-1

또 다른 재미있는 해결책이 있습니다. 이것은 locals()이해 의 사전, 즉 목록 이해 범위 내에서 생성 된 지역 변수를 활용합니다.

>>> [locals().setdefault(i, (elem + locals().get(i-1, 0))) for i, elem 
     in enumerate(time_interval)]
[4, 10, 22]

locals()각 반복 의 모습은 다음과 같습니다 .

>>> [[locals().setdefault(i, (elem + locals().get(i-1, 0))), locals().copy()][1] 
     for i, elem in enumerate(time_interval)]
[{'.0': <enumerate at 0x21f21f7fc80>, 'i': 0, 'elem': 4, 0: 4},
 {'.0': <enumerate at 0x21f21f7fc80>, 'i': 1, 'elem': 6, 0: 4, 1: 10},
 {'.0': <enumerate at 0x21f21f7fc80>, 'i': 2, 'elem': 12, 0: 4, 1: 10, 2: 22}]

작은 목록의 경우 성능이 좋지 않습니다.

>>> %timeit list(accumulate([4, 6, 12]))
387 ns ± 7.53 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

>>> %timeit np.cumsum([4, 6, 12])
5.31 µs ± 67.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit [locals().setdefault(i, (e + locals().get(i-1,0))) for i,e in enumerate(time_interval)]
1.57 µs ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

그리고 더 큰 목록의 경우 분명히 평평합니다.

>>> l = list(range(1_000_000))
>>> %timeit list(accumulate(l))
95.1 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit np.cumsum(l)
79.3 ms ± 1.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit np.cumsum(l).tolist()
120 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit [locals().setdefault(i, (e + locals().get(i-1, 0))) for i, e in enumerate(l)]
660 ms ± 5.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

방법이 못 생기고 실용적이지는 않지만 확실히 재미 있습니다.


-2
lst = [4,6,12]

[sum(lst[:i+1]) for i in xrange(len(lst))]

보다 효율적인 솔루션 (더 큰 목록?)을 찾고 있다면 생성기가 좋은 호출이 될 수 있습니다 (또는 numpy성능에 대해 정말로 관심이있는 경우 사용 ).

def gen(lst):
    acu = 0
    for num in lst:
        yield num + acu
        acu += num

print list(gen([4, 6, 12]))

-3

이것은 Haskell 스타일입니다.

def wrand(vtlg):

    def helpf(lalt,lneu): 

        if not lalt==[]:
            return helpf(lalt[1::],[lalt[0]+lneu[0]]+lneu)
        else:
            lneu.reverse()
            return lneu[1:]        

    return helpf(vtlg,[0])
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.