[Pandas] cumsum / cummax / cummin / cumprod
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| month | sales | cumulative |
|---|---|---|
| Jan | 100 | 100 |
| Feb | 200 | 300 |
| Mar | 150 | 450 |
| Apr | 300 | 750 |
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 50expanding 과의 차이
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 비교
| Pandas | SQL 등가 |
|---|---|
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…
💬 댓글