pandas의 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
        after filling the missing values using the 'forward fill' method
)


만약 데이터셋 안에 결측값(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'])

 



이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)



728x90
반응형
Posted by Rfriend
,