본문으로 건너뛰기
김신건의 로그

[Pandas] cumsum / cummax / cummin / cumprod

· 수정 · 📖 약 1분 · 403자/단어 #python #pandas #cumulative
Pandas cumsum, Pandas cumulative, 누적 pandas

정의

누적 (cumulative) 연산. 각 위치까지의 누적 결과를 반환, 결과 길이는 원본과 동일.

함수의미
cumsum()누적 합
cumprod()누적 곱
cummax()누적 최댓값
cummin()누적 최솟값
cumcount() (groupby)그룹 내 누적 카운트

사용

python
import pandas as pd
s = pd.Series([1, 2, 3, 4, 5])
print('cumsum :', s.cumsum().tolist())
print('cumprod:', s.cumprod().tolist())
print('cummax :', s.cummax().tolist())
print('cummin :', s.cummin().tolist())
결과
cumsum : [1, 3, 6, 10, 15]
cumprod: [1, 2, 6, 24, 120]
cummax : [1, 2, 3, 4, 5]
cummin : [1, 1, 1, 1, 1]

DataFrame.cumsum

df.cumsum()              # 각 컬럼별 누적
df.cumsum(axis=1)        # 각 행 누적
df.cumsum(skipna=True)   # NaN 처리
python
import pandas as pd
df = pd.DataFrame({
  'month': ['Jan','Feb','Mar','Apr'],
  'sales': [100, 200, 150, 300],
})
df['cumulative'] = df['sales'].cumsum()
print(df)
결과
  month  sales  cumulative
0   Jan    100         100
1   Feb    200         300
2   Mar    150         450
3   Apr    300         750
monthsalescumulative
Jan100100
Feb200300
Mar150450
Apr300750

groupby + cumsum

df['user_cumulative'] = df.groupby('user_id')['amount'].cumsum()
# 각 user 의 누적 결제액

cumcount (그룹 내 순번)

df.groupby('user_id').cumcount()
# 각 user 의 0, 1, 2, ... 순번 (첫 결제, 두 번째 결제, ...)

SQL: ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY ...).

python
import pandas as pd
df = pd.DataFrame({
  'user': ['A','B','A','A','B'],
  'amount': [10, 20, 15, 5, 30],
})
df['nth_order'] = df.groupby('user').cumcount() + 1
df['user_cum'] = df.groupby('user')['amount'].cumsum()
print(df)
결과
  user  amount  nth_order  user_cum
0    A      10          1        10
1    B      20          1        20
2    A      15          2        25
3    A       5          3        30
4    B      30          2        50

expanding 과의 차이

s.cumsum()                 # 시작부터 현재까지 합
s.expanding().sum()         # 같음
s.expanding().mean()        # 누적 평균 (cumsum 으로는 불가능)

cumsum 은 단순 누적, expanding() 은 더 일반적 (mean, std 등 가능).

자주 쓰는 패턴

누적 매출 차트

df['cumulative_revenue'] = df['revenue'].cumsum()
df.plot(x='date', y='cumulative_revenue')

running max (drawdown 계산)

df['peak'] = df['portfolio_value'].cummax()
df['drawdown'] = (df['portfolio_value'] - df['peak']) / df['peak']

누적 비율 (Pareto)

counts = df['category'].value_counts()
cumulative_pct = (counts / counts.sum()).cumsum()
# 상위 N 카테고리가 80% 를 차지하는지

첫 N 명까지의 그룹 통계

df.groupby('cohort').cumcount()

함정

1. NaN 처리

s = pd.Series([1, None, 3])
s.cumsum()                  # [1, NaN, NaN+3=NaN? 아니, [1, NaN, 4]]
# skipna 기본 True, NaN 무시하고 누적
s.cumsum(skipna=False)      # [1, NaN, NaN]

2. 정렬 후 cumsum

df = df.sort_values('date')
df['cum'] = df['amount'].cumsum()    # 시간 순 누적

정렬 안 된 상태에서 누적은 의미가 달라진다.

3. cumprod 의 overflow

df['cumulative_growth'] = (1 + df['return']).cumprod()
# 수치가 너무 작거나 클 경우 부동소수점 오차

expanding vs rolling vs cumsum 비교

방법윈도우사용 상황
cumsum()처음부터 현재누적 합: 빠르고 단순
expanding().sum()처음부터 현재cumsum 과 동일 결과
expanding().mean()처음부터 현재누적 평균: cumsum 으로는 불가
rolling(n).sum()최근 n개이동 합계
rolling(n).mean()최근 n개이동 평균
flowchart LR
    Raw["원본: [100, 200, 150, 300]"]
    Raw --> CS["cumsum: [100, 300, 450, 750]"]
    Raw --> RM["rolling(2).mean(): [NaN, 150, 175, 225]"]
    CS --> UC["누적 매출 차트 (우상향)"]
    RM --> TR["이동 평균 차트 (평활화)"]

시계열 실전 패턴

월별 누적 집계

df['date'] = pd.to_datetime(df['date'])
df = df.set_index('date')

# 월별 합계 후 누적
monthly = df['revenue'].resample('ME').sum()
cumulative = monthly.cumsum()

코호트별 누적 (cohort analysis)

# 첫 구매 달 기준 코호트
df['cohort'] = (
    df.groupby('user_id')['order_date']
    .transform('min')
    .dt.to_period('M')
)
df['period_number'] = (
    df['order_date'].dt.to_period('M') - df['cohort']
).apply(lambda x: x.n)

# 코호트별 nth 주문
df['nth_order'] = df.groupby('user_id').cumcount() + 1

Drawdown 계산 (포트폴리오)

prices = df['price']
peak = prices.cummax()              # 이전 최고점
drawdown = (prices - peak) / peak   # 최고점 대비 하락률

max_drawdown = drawdown.min()
print(f"최대 낙폭: {max_drawdown:.2%}")

SQL 비교

PandasSQL 등가
df['amount'].cumsum()SUM(amount) OVER (ORDER BY date)
df.groupby('u')['amount'].cumsum()SUM(amount) OVER (PARTITION BY u ORDER BY date)
df.groupby('u').cumcount()ROW_NUMBER() OVER (PARTITION BY u ORDER BY date) - 1
df['price'].cummax()MAX(price) OVER (ORDER BY date)

성능 팁

# 대용량: category 타입으로 groupby 속도 개선
df['user_id'] = df['user_id'].astype('category')
df['user_cum'] = df.groupby('user_id')['amount'].cumsum()

# numpy 직접: 단순 Series 누적합에서 약간 빠름
import numpy as np
arr = np.cumsum(df['amount'].values)

cumsum() 은 내부 C 레벨 연산으로 대부분 상황에서 충분히 빠름. 초대형 데이터는 Polars / Dask 고려.

참고

이 글의 용어 (4개)
[Pandas] expanding / ewmpandas
정의 외의 윈도우 연산. - : 시작부터 현재까지 누적 윈도우 - : exponentially weighted (지수 가중) 윈도우 개념 비교 | 항목 | | | | |:---|…
[Pandas] groupbypandas
정의 는 데이터를 그룹으로 나누고 (split), 각 그룹에 함수를 적용 (apply), 결과를 합쳐 (combine) 새 DataFrame 으로 만드는 split-apply-c…
[Pandas] rolling / expandingpandas
정의 - : 고정 크기 슬라이딩 윈도우 (이동 평균 등) - : 시작부터 누적 윈도우 (cumulative) - : 지수 가중 이동 평균 사용 상황 | 상황 | 적합한 방법 | …
[Pandas] shift / diff / pct_changepandas
정의 - : 값을 n 칸 밀기 (lag/lead 생성) - : n 칸 차이 (= ) - : 변화율 (= ) 시계열 분석의 기본 도구. 전일 대비 / 전월 대비 / lag feat…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기