달나라 노트

Python Pandas : cumsum, cumprod (누적합, 누적곱) 본문

Python/Python Pandas

Python Pandas : cumsum, cumprod (누적합, 누적곱)

CosmosProject 2024. 8. 1. 19:05
728x90
반응형

 

 

 

cumsum은 누적합을 구하며

cumprod는 누적곱을 구합니다.

 

cumsum부터 알아봅시다.

 

Syntax

cumsum(skipna=True/False, axis=0/1)
cumprod(skipna=True/False, axis=0/1)

 

- skipna

True일 경우 NaN값을 무시하고 계산합니다.

False일 경우 NaN값을 고려하고 계산합니다.

 

- axis

누적합을 구할 축을 지정합니다.

기본값은 0이며 0으로 지정해야 컬럼 기준 누적합이 됩니다.

 

 

 

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, 150, 50, 10, 20, 180, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

df_test_cumsum = df_test.loc[:, 'qty'].cumsum(skipna=True)
print(type(df_test_cumsum))
print(df_test_cumsum)



-- Result
   seq  id  qty
0    0   1  200
1    1   1   80
2    2   1  150
3    3   1   50
4    4   1   10
5    5   2   20
6    6   2  180
7    7   2   70
8    8   2  100
9    9   2  100


<class 'pandas.core.series.Series'>
0    200
1    280
2    430
3    480
4    490
5    510
6    690
7    760
8    860
9    960

 

위 예시는 DataFrame의 qty 컬럼에 cumsum을 적용한 결과입니다.

 

 

 

df_test_cumsum = df_test.loc[:, 'qty'].cumsum(skipna=False)

 

cumsum의 결과를 보면 200, 280, 430 등등의 값이 적힌 Series가 return되었습니다.

 

 

 

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, 150, 50, 10, 20, 180, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

df_test_cumsum = df_test.loc[:, 'qty'].cumsum(skipna=True)
print(type(df_test_cumsum))
print(df_test_cumsum)

df_test.loc[:, 'qty_cumsum'] = df_test.loc[:, 'qty'].cumsum(skipna=True)
print(df_test)



-- Result
   seq  id  qty
0    0   1  200
1    1   1   80
2    2   1  150
3    3   1   50
4    4   1   10
5    5   2   20
6    6   2  180
7    7   2   70
8    8   2  100
9    9   2  100


<class 'pandas.core.series.Series'>
0    200
1    280
2    430
3    480
4    490
5    510
6    690
7    760
8    860
9    960
Name: qty, dtype: int64


   seq  id  qty  qty_cumsum
0    0   1  200         200
1    1   1   80         280
2    2   1  150         430
3    3   1   50         480
4    4   1   10         490
5    5   2   20         510
6    6   2  180         690
7    7   2   70         760
8    8   2  100         860
9    9   2  100         960

 

cumsum의 결과를 좀 더 보기 쉽게 하기 위해 df_test의 qty_cumsum 컬럼에 삽입했습니다.

 

 

 

 

 

   seq  id  qty  qty_cumsum
0    0   1  200         200
1    1   1   80         280
2    2   1  150         430
3    3   1   50         480
4    4   1   10         490
5    5   2   20         510
6    6   2  180         690
7    7   2   70         760
8    8   2  100         860
9    9   2  100         960

 

이 결과를 보면 cumsum의 기능을 알 수 있습니다.

qty 컬럼을 첫 행부터 읽어내고 첫행부터 현재 행까지의 값을 더하는 것입니다.

 

그래서 1행은 200이고

2행은 200 + 80 = 280

3행은 200 + 80 + 150 = 430

4행은 200 + 80 + 150 + 50 = 480

이런 식으로 계산이 되는 것입니다.

 

 

cumsum은 이렇게 첫 행부터 순차적으로 더하기 때문에 순서가 중요합니다.

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, 150, 50, 10, 20, 180, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
df_test = df_test.sort_values(by=['seq'], ascending=False, inplace=False)
print(df_test)

df_test.loc[:, 'qty_cumsum'] = df_test.loc[:, 'qty'].cumsum(skipna=True)
print(df_test)



-- Result
   seq  id  qty
9    9   2  100
8    8   2  100
7    7   2   70
6    6   2  180
5    5   2   20
4    4   1   10
3    3   1   50
2    2   1  150
1    1   1   80
0    0   1  200


   seq  id  qty  qty_cumsum
9    9   2  100         100
8    8   2  100         200
7    7   2   70         270
6    6   2  180         450
5    5   2   20         470
4    4   1   10         480
3    3   1   50         530
2    2   1  150         680
1    1   1   80         760
0    0   1  200         960

 

위 예시는 DataFrame의 값을 seq 컬럼을 기준으로 내림차순하여 cumsum을 적용한 예시인데

cumsum의 결과를 보면 그 값이 마찬가지로 DataFrame의 첫 행부터 더하는 것을 알 수 있습니다.

 

 

 

 

 

cumprod도 마찬가지입니다.

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, 150, 50, 10, 20, 180, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

df_test_cumsum = df_test.loc[:, 'qty'].cumprod(skipna=True)
print(type(df_test_cumsum))
print(df_test_cumsum)

df_test.loc[:, 'qty_cumprod'] = df_test.loc[:, 'qty'].cumprod(skipna=True)
print(df_test)



-- Result
   seq  id  qty
0    0   1  200
1    1   1   80
2    2   1  150
3    3   1   50
4    4   1   10
5    5   2   20
6    6   2  180
7    7   2   70
8    8   2  100
9    9   2  100


<class 'pandas.core.series.Series'>
0                    200
1                  16000
2                2400000
3              120000000
4             1200000000
5            24000000000
6          4320000000000
7        302400000000000
8      30240000000000000
9    3024000000000000000
Name: qty, dtype: int64


   seq  id  qty          qty_cumprod
0    0   1  200                  200
1    1   1   80                16000
2    2   1  150              2400000
3    3   1   50            120000000
4    4   1   10           1200000000
5    5   2   20          24000000000
6    6   2  180        4320000000000
7    7   2   70      302400000000000
8    8   2  100    30240000000000000
9    9   2  100  3024000000000000000

 

결과를 보면

1행은 200

2행은 200 * 80 = 16000

3행은 200 * 80 * 150 = 2400000

4행은 200 * 80 * 150 * 50 = 120000000

이런식으로 계산이 되는 것이죠.

 

 

 

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, None, 50, 10, 20, None, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

df_test.loc[:, 'qty_cumsum'] = df_test.loc[:, 'qty'].cumsum(skipna=True)
print(df_test)


-- Result
   seq  id    qty
0    0   1  200.0
1    1   1   80.0
2    2   1    NaN
3    3   1   50.0
4    4   1   10.0
5    5   2   20.0
6    6   2    NaN
7    7   2   70.0
8    8   2  100.0
9    9   2  100.0


   seq  id    qty  qty_cumsum
0    0   1  200.0       200.0
1    1   1   80.0       280.0
2    2   1    NaN         NaN
3    3   1   50.0       330.0
4    4   1   10.0       340.0
5    5   2   20.0       360.0
6    6   2    NaN         NaN
7    7   2   70.0       430.0
8    8   2  100.0       530.0
9    9   2  100.0       630.0

 

이번에는 qty 컬럼에 NaN값을 넣어봤습니다.

 

 

   seq  id    qty  qty_cumsum
0    0   1  200.0       200.0
1    1   1   80.0       280.0
2    2   1    NaN         NaN
3    3   1   50.0       330.0
4    4   1   10.0       340.0
5    5   2   20.0       360.0
6    6   2    NaN         NaN
7    7   2   70.0       430.0
8    8   2  100.0       530.0
9    9   2  100.0       630.0

 

결과를 보면 위와 같습니다.

 

1행은 200

2행은 200 + 80 = 280

여기까진 동일합니다.

 

근데

3행은 200 + 80 + NaN이 됩니다. 이 경우 skipna=True로 지정되어있으나 3행은 그 자체의 값이 NaN이므로 NaN이 return됩니다.

4행도 특이한데

4행은 200 + 80 + NaN + 50 = 330이 됩니다.

 

여기서 skipna=True의 진짜 의미를 알 수 있는데

더할 값 중에 NaN이 있으면 이것은 그냥 없는 셈 쳐서 skip하라는 것이 skipna=True의 의미입니다.

다만 해당 행이 NaN이라면 NaN을 return한다는 예외가 있죠.

 

 

 

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, None, 50, 10, 20, None, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

df_test.loc[:, 'qty_cumsum'] = df_test.loc[:, 'qty'].cumsum(skipna=False)
print(df_test)



-- Result
   seq  id    qty
0    0   1  200.0
1    1   1   80.0
2    2   1    NaN
3    3   1   50.0
4    4   1   10.0
5    5   2   20.0
6    6   2    NaN
7    7   2   70.0
8    8   2  100.0
9    9   2  100.0


   seq  id    qty  qty_cumsum
0    0   1  200.0       200.0
1    1   1   80.0       280.0
2    2   1    NaN         NaN
3    3   1   50.0         NaN
4    4   1   10.0         NaN
5    5   2   20.0         NaN
6    6   2    NaN         NaN
7    7   2   70.0         NaN
8    8   2  100.0         NaN
9    9   2  100.0         NaN

 

skipna=False 예시입니다.

1행 = 200

2행 = 200 + 80 = 280

3행 = 200 + 80 + NaN = NaN

여기까진 동일합니다.

 

근데 보면 4행부터 결과가 모두 NaN으로 찍혀있습니다.

그 이유는 skipna=False로 지정하였기 때문에 연산을 진행할 값 중 NaN이 있으면 이를 skip하지 못하고 고려하게 되어 모두 NaN이 return되는 것입니다.

 

이는 cumsum 뿐 아니라 cumprod에도 동일하게 적용됩니다.

 

 

 

 

cumsum, cumprod 또한 groupby와 함께 사용할 수 있습니다.

 

 

import pandas as pd

dict_test = {
    'seq': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
    'qty': [200, 80, 150, 50, 10, 20, 180, 70, 100, 100]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

df_test_cumsum_with_groupby = df_test.groupby(by=['id'])[['qty']].apply(lambda df: df.cumsum(skipna=True))
print(df_test_cumsum_with_groupby)

df_test_cumsum_with_groupby = df_test_cumsum_with_groupby.reset_index(drop=False, inplace=False)
print(df_test_cumsum_with_groupby)

df_test.loc[:, 'qty_cumsum_groupby'] = df_test_cumsum_with_groupby.loc[:, 'qty']
print(df_test)




-- Result
   seq  id  qty
0    0   1  200
1    1   1   80
2    2   1  150
3    3   1   50
4    4   1   10
5    5   2   20
6    6   2  180
7    7   2   70
8    8   2  100
9    9   2  100


      qty
id       
1  0  200
   1  280
   2  430
   3  480
   4  490
2  5   20
   6  200
   7  270
   8  370
   9  470


   id  level_1  qty
0   1        0  200
1   1        1  280
2   1        2  430
3   1        3  480
4   1        4  490
5   2        5   20
6   2        6  200
7   2        7  270
8   2        8  370
9   2        9  470


   seq  id  qty  qty_cumsum_groupby
0    0   1  200                 200
1    1   1   80                 280
2    2   1  150                 430
3    3   1   50                 480
4    4   1   10                 490
5    5   2   20                  20
6    6   2  180                 200
7    7   2   70                 270
8    8   2  100                 370
9    9   2  100                 470

 

groupby와 cumsum을 동시에 사용한 예시입니다.

 

 

df_test.groupby(by=['id'])[['qty']].apply(lambda df: df.cumsum(skipna=True))

 

위 예시에서 보면 groupby의 lambda에 cumsum을 적용하여 groupby 별 cumsum을 적용하였습니다.

즉, id 컬럼의 값을 기준으로 동일한 id 값을 가진 행들의 qty에 대해서 cumsum이 적용됩니다.

 

 

   seq  id  qty  qty_cumsum_groupby
0    0   1  200                 200
1    1   1   80                 280
2    2   1  150                 430
3    3   1   50                 480
4    4   1   10                 490
5    5   2   20                  20
6    6   2  180                 200
7    7   2   70                 270
8    8   2  100                 370
9    9   2  100                 470

 

그래서 결과를 보면 id = 1인 행들에 대해서

id = 1의 1행 -> 200

id = 1의 2행 -> 200 + 80 = 280

id = 1의 3행 -> 200 + 80 + 150 = 430

id = 1의 4행 -> 200 + 80 + 150 + 50 = 480

id = 1의 5행 -> 200 + 80 + 150 + 50 + 10 = 490

 

id = 2의 1행 -> 20

id = 2의 2행 -> 20 + 180 = 200

id = 2의 3행 -> 20 + 180 + 70 = 270

id = 2의 4행 -> 20 + 180 + 70 + 100 = 370

id = 2의 5행 -> 20 + 180 + 70 + 100 + 100 = 470

 

이렇게 계산이 됩니다.

 

 

 

 

 

728x90
반응형
Comments