[Python pandas] 그룹별로 전 분기 대비, 전년 동분기 대비 변동률 구하기 (Percentage change between the current and a prior element by Group)
Python 분석과 프로그래밍/Python 데이터 전처리 2020. 12. 26. 19:50pandas의 Series나 DataFrame 자료구조로 저장된 시계열 데이터에 대해서 이전 값 대비 현재 값의 변동율(change percentage)을 구하고 싶을 때 pandas 의 pct_change() 메소드를 사용하면 매우 편리하게 계산할 수 있습니다.
이번 포스팅에서는 Python pandas 패키지의 pct_change() 메소드를 사용하여
pandas Series에서
- (1) 이전 원소 대비 변동률
(Percentage change between the current and a prior element)
- (2) 이전 2개 원소 대비 변동률
(Percentage change between the current and 2 periods prior element)
- (3) 결측값을 이전 원소 값으로 대체 후 이전 원소 대비 변동률
(Percentage change between the current and a prior element after filling the missing values using the 'forward fill' method)
pandas DataFrame에서
- (4) 그룹별 이전 분기 대비 변동률
(Percentage change between the current and a prior quarter by Group)
- (5) 그룹별 전년 동분기 대비 변동률
(Percentage change between the current and a year before by Group)
* pandas의 pct_change() 메소드는 Series와 DataFrame 자료구조 모두에서 동일하게 사용 가능합니다.
-- pandas Series 에서
(1) 이전 원소 대비 변동률 (Percentage change between the current and a prior element) |
pandas의 pct_change() 메소드는 기본 설정이 이전 원소 대비 현재 원소의 변동 비율(percentage change)을 계산해줍니다. 아래 pandas Series의 경우,
첫번째 값은 이전 값이 없으므로 NaN
두번째 값의 첫번째 값 대비 변동률 = (20-10)/10 = 1.0
세번째 값의 두번째 값 대비 변동률 = (50-20)/20 = 1.5
네번째 값의 세번째 값 대비 변동률 = (55-50)/50 = 0.1
다섯번째 값의 네번째 값 대비 변동률 = (70-55)/55 = 0.27
In [1]: import pandas as pd In [2]: s = pd.Series([10, 20, 50, 55, 70]) s.pct_change() Out[2]: 0 NaN 1 1.000000 2 1.500000 3 0.100000 4 0.272727 dtype: float64 |
(2) 이전 2개 원소 대비 변동률 (Percentage change between the current and 2 periods prior element) |
변동률을 구할 때 이전 값의 이동 기간을 periods 매개변수를 사용하면 자유롭게 설정해줄 수 있습니다. 가령, 위의 s Series 예에서 이전 2개 원소 대비 변동률은 s.pct_change(periods=2) 로 해주면 됩니다.
첫번째와 두번째 값은 이전 2개 원소 값이 없으므로 NaN
세번째값의 이전 2개 원소 값 대비 변동률 = (50-10)/10 = 4.0
네번째값의 이전 2개 원소 값 대비 변동률 = (55-20)/20 = 1.75
다섯번째값의 이전 2개 원소 값 대비 변동률 = (70-50)/50 = 0.4
In [3]: s = pd.Series([10, 20, 50, 55, 70]) s.pct_change(periods=2) Out[3]: 0 NaN 1 NaN 2 4.00 3 1.75 4 0.40 dtype: float64 |
(3) 결측값을 이전 원소 값으로 대체 후 이전 원소 대비 변동률 (Percentage change between the current and a prior element |
만약 데이터셋 안에 결측값(missing value)가 있다면 pct_change() 메소드에 pandas의 결측값 처리 매개변수를 그대로 차용하여 결측값을 처리한 후에 이전 원소 대비 변동률을 구할 수 있습니다.
결측값을 처리하는 방법으로는,
fill_method='ffill' or 'pad' : 이전 값으로 결측값을 대체하여 채우기 (앞방향으로 채워나가기)
fill_method='bfill' or 'backfill' : 이후 값으로 결측값을 대체하여 채우기 (뒤방향에서 채워나가기)
In [4]: s2 = pd.Series([10, 20, 50, None, 70]) s2.pct_change(fill_method='ffill') Out[4]: 0 NaN 1 1.0 2 1.5 3 0.0 4 0.4 dtype: float64
|
-- pandas DataFrame 에서
(4) 그룹별 이전 분기 대비 변동률 (Percentage change between the current and a prior quarter by Group) |
예제로 사용할 '제품(product)' 그룹을 가진 연도(year)/ 분기(quarter) 기간 별 판매량(sales) 칼럼으로 구성된 DataFrame을 만들어보겠습니다.
In [5]: # input data sale = pd.DataFrame( {'product': ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'], 'year': [2018, 2018, 2018, 2018, 2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020, 2018, 2018, 2018, 2018, 2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020], 'quarter': [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], 'sales': [5, 6, 6, 8, 10, 20, 30, 40, 12, 25, 38, 50, 60, 65, 80, 95, 100, 125, 130, 140, 110, 130, 132, 144]}) sale Out[5]: product year quarter sales 0 a 2018 1 5 1 a 2018 2 6 2 a 2018 3 6 3 a 2018 4 8 4 a 2019 1 10 5 a 2019 2 20 6 a 2019 3 30 7 a 2019 4 40 8 a 2020 1 12 9 a 2020 2 25 10 a 2020 3 38 11 a 2020 4 50 12 b 2018 1 60 13 b 2018 2 65 14 b 2018 3 80 15 b 2018 4 95 16 b 2019 1 100 17 b 2019 2 125 18 b 2019 3 130 19 b 2019 4 140 20 b 2020 1 110 21 b 2020 2 130 22 b 2020 3 132 23 b 2020 4 144
|
이제 '제품(product)' 그룹별로 '이전 분기 대비 현재 분기의 변동율(change percentage between the current and the prior quarter)' 을 구해보겠습니다.
물론 이번 예제 데이터는 년(year)/ 분기(quarter) 를 기준으로 이미 정렬이 되어 있기는 합니다만, 정렬이 안되어 있는 경우도 있을 수 있으므로 명확하게 시간 기준으로 정렬될 수 있도록 sort_values(by=['year', 'quarter'] 로 명시적으로 먼저 정렬을 해주었습니다. 다음으로 groupby(['product']) 메소드로 '제품(product)' 별로 그룹을 분할(split) 하여 그룹별로 이후 연산이 이루어지도록 하였습니다. 마지막으로 sales.pct_change() 메소드로 '판매량(sales)' 칼럼에 대해 '이전대비 변동률(pct_change)'을 '제품' 그룹별로 구해주었습니다.
In [6]: sale['sales_pct_change_by_1q'] = sale.\ sort_values(['year', 'quarter']).\ groupby(['product']).\ sales.pct_change() sale Out[6]: product year quarter sales pct_change_by_1q 0 a 2018 1 NaN 1 a 2018 2 6 0.200000 2 a 2018 3 6 0.000000 3 a 2018 4 8 0.333333 4 a 2019 1 10 0.250000 5 a 2019 2 20 1.000000 6 a 2019 3 30 0.500000 7 a 2019 4 40 0.333333 8 a 2020 1 12 -0.700000 9 a 2020 2 25 1.083333 10 a 2020 3 38 0.520000 11 a 2020 4 50 0.315789 12 b 2018 1 60 NaN 13 b 2018 2 65 0.083333 14 b 2018 3 80 0.230769 15 b 2018 4 95 0.187500 16 b 2019 1 100 0.052632 17 b 2019 2 125 0.250000 18 b 2019 3 130 0.040000 19 b 2019 4 140 0.076923 20 b 2020 1 110 -0.214286 21 b 2020 2 130 0.181818 22 b 2020 3 132 0.015385 23 b 2020 4 144 0.090909
|
(5) 그룹별 전년 동분기 대비 변동률 (Percentage change between the current and a year before by Group) |
만약 이전 분기가 아니라 '전년 동일 분기' 대비 변동률을 구하고 싶다면 pct_change(periods=4) 처럼 periods=4 매개변수를 설정해주어서 4분기 이전 (즉, 전년 동일 분기)의 값 대비 변동률을 구해주면 됩니다. (만약 월 단위로 데이터가 집계되어 있다면 pct_change(periods=12) 로 해주면 됩니다.)
In [7]: sale['pct_change_by_1y'] = sale.sort_values(['year', 'quarter']).\ groupby(['product']).\ sales.pct_change(periods=4) In [8]: sale.sort_values(by=['product', 'quarter', 'year']) Out[8]: product year quarter sales pct_change_by_1q pct_change_by_1y 0 a 2018 1 5 NaN NaN 4 a 2019 1 10 0.250000 1.000000 8 a 2020 1 12 -0.700000 0.200000 1 a 2018 2 6 0.200000 NaN 5 a 2019 2 20 1.000000 2.333333 9 a 2020 2 25 1.083333 0.250000 2 a 2018 3 6 0.000000 NaN 6 a 2019 3 30 0.500000 4.000000 10 a 2020 3 38 0.520000 0.266667 3 a 2018 4 8 0.333333 NaN 7 a 2019 4 40 0.333333 4.000000 11 a 2020 4 50 0.315789 0.250000 12 b 2018 1 60 NaN NaN 16 b 2019 1 100 0.052632 0.666667 20 b 2020 1 110 -0.214286 0.100000 13 b 2018 2 65 0.083333 NaN 17 b 2019 2 125 0.250000 0.923077 21 b 2020 2 130 0.181818 0.040000 14 b 2018 3 80 0.230769 NaN 18 b 2019 3 130 0.040000 0.625000 22 b 2020 3 132 0.015385 0.015385 15 b 2018 4 95 0.187500 NaN 19 b 2019 4 140 0.076923 0.473684 23 b 2020 4 144 0.090909 0.028571
|
또는 아래 방법처럼 분기(quarter)/ 년(year) 를 기준으로 먼저 정렬을 해놓고, 그 다음에 제품/분기 그룹(groupby(['product', 'quarter']) 별로 판매량의 변동률(sales.pct_change())를 구해도 결과는 같습니다.
# or equvalently sale['pct_change_by_1y'] = sale.sort_values(by=['quarter', 'year']).\ groupby(['product', 'quarter']).\ sales.pct_change() sale.sort_values(by=['product', 'quarter', 'year']) |
이번 포스팅이 많은 도움이 되었기를 바랍니다.
행복한 데이터 과학자 되세요! :-)