프로덕트 매니저/PM의 데이터

[시계열분석] Prophet 모델에 한국 공휴일 추가하기

KmkmKim 2023. 3. 5. 01:18

들어가면서

내가 처음 입사했을 때, 회사에서는 매월 주요 지표를 예측하는 과정은 기존에 엑셀의 회귀분석 기능을 활용했다. 나도 작년까지는 이런 회귀분석과 Cohort 추이 분석을 토대로 필요한 예측치를 산출하고는 했다. 다만, 시계열 분석을 공부하는 과정에서 이런 방식이 가지는 한계를 느낄 수 있었다. 그래서 시계열 공부를 시작했고, 그 과정에서 메타에서 제작한 prophet 모델을 알고 이를 실무에 도입하고 있다.

 

prophet 모델이 Excel에서 제공하는 회귀분석 기능과 비교해서 가지는 장점은 다음과 같다.

  • 계절성 및 휴일 패턴을 고려: Prophet 모델은 시계열 데이터에서 계절성 및 휴일 패턴을 고려하여 예측할 수 있습니다. 이는 Excel의 회귀분석에서는 별도로 처리해야 하므로 복잡하고 번거로울 수 있습니다.
  • 비선형적인 추세 모델링: Prophet 모델은 비선형적인 추세 모델링에 적합합니다. 따라서 Excel의 회귀분석보다 더 정확한 예측을 제공할 수 있습니다.
  • 데이터 전처리의 필요성 감소: Prophet 모델은 일반적으로 데이터 전처리가 필요하지 않습니다. 반면, Excel의 회귀분석에서는 데이터 전처리를 수행해야 합니다.
  • 여러 변수의 처리: Prophet 모델은 여러 변수를 처리하는 데 유용합니다. 예를 들어, 다른 변수의 영향을 받는 패턴을 예측할 때 유용합니다. Excel의 회귀분석은 일반적으로 단일 변수 예측에 사용됩니다.
  • 정확도와 예측 불확실성 분석: Prophet 모델은 예측의 정확도와 예측 불확실성을 분석할 수 있습니다. 이는 Excel의 회귀분석에서는 제공되지 않습니다.
  • 빠른 예측 시간: Prophet 모델은 대규모 데이터 집합에 대한 빠른 예측 시간을 제공합니다. 반면, Excel의 회귀분석은 대규모 데이터 집합에서 느릴 수 있습니다.

이런 이유에서 prophet을 활용한 보다 정확하고 유연한 시계열 분석을 하는 방향으로 개선하고 있다.

 

오늘 정리한 콘텐츠는 이런 Prophet 모델의 성능을 높여줄 수 있는 기초적인 작업이다. 바로 한국의 공휴일에 대한 내용을 맵핑하는 것이다. 비즈니스에서 지표를 보면, 영업일과 비영업일에 따라서 차이가 나는 경우가 많다. 예를 들면, B2C의 경우 오히려 금요일 저녁부터 주말에 높게 나타나는 지표가 있을 수 있다. 반면, B2B 서비스의 경우 상대적으로 영업일의 수치가 높게 나오는 경우가 많을 것이다.

 

이런 이유에서 시계열 모델에서 휴일에 대한 구분을 진행한다면, 보다 정확한 예측치를 산출할 수 있을 것이다. 

 

 

 

코드 작성하기

1.

import pandas as pd
import numpy as np
import plotly.graph_objs as go
import matplotlib.pyplot as plt

from datetime import datetime, timedelta
from prophet import Prophet #prophet 라이브러리 불러오기

from prophet.diagnostics import cross_validation
from prophet.plot import plot_cross_validation_metric
from prophet.diagnostics import performance_metrics

먼저, pandas, numpy, plotly.graph_objs, matplotlib.pyplot 등의 라이브러리를 불러온다. 그리고 datetime과 timedelta 라이브러리를 이용하여 날짜 및 시간 관련 함수를 사용할 수 있도록 불러온다.

 

또한 Prophet 라이브러리에서 모델링 및 성능 평가에 필요한 함수들을 불러온다. 그리고, Prophet 모델링을 위한 Prophet() 함수를 불러온다. 이 함수는 Prophet 모델을 생성할 때 사용되며, 모델 생성 후에는 이 함수를 이용하여 모델의 하이퍼파라미터를 설정하고, 예측을 수행할 수 있다.

마지막으로, cross_validation, plot_cross_validation_metric, performance_metrics 함수를 이용하여 Prophet 모델의 성능 평가를 수행할 수 있다. cross_validation 함수는 Prophet 모델의 교차 검증을 수행하며, plot_cross_validation_metric 함수를 이용하여 교차 검증 결과를 시각화할 수 있다. performance_metrics 함수는 교차 검증 결과를 통해 모델의 성능 지표를 계산합니다. 이를 통해 모델의 성능을 평가하고, 개선할 수 있다.

 

2.

#데이터 로드
join_raw = pd.read_csv('../join_data.csv')
join_raw['register_month'] = pd.to_datetime(join_raw['register_month'], format='%Y-%m-%d')
join_raw['register_day'] = pd.to_datetime(join_raw['register_day'], format='%Y-%m-%d')

df_train = join_raw[['register_day', 'user_total']]
df_train = df_train.rename(columns={'register_day': 'ds', 'user_total':'y'})

다음은 데이터를 불러와야한다. 특히 데이터를 부르고 해당 데이터의 값이 String으로 저장된 경우, 시계열 분석을 위해서 이를 DataFrame으로 변환하는 작업을 거쳐야한다.

 

3. 

import holidays
date_list=pd.date_range(start='20210101', end='20231231', freq='D')

# 한국 휴일 객체 생성 
kr_holidays = holidays.KR()
holiday_df = pd.DataFrame(columns=['ds','holiday'])
holiday_df['ds'] = sorted(date_list)
holiday_df['holiday'] = holiday_df.ds.apply(lambda x: 'holiday' if x in kr_holidays else 'non-holiday')

이제 오늘 콘텐츠의 핵심이다. 한국의 공휴일 정보를 이용하여 2021년 1월 1일부터 2023년 12월 31일까지의 모든 날짜에 대한 공휴일 여부를 데이터프레임으로 생성한다.

 

holidays 라이브러리를 사용하여 한국의 공휴일 정보를 불러온 후, pd.date_range 함수를 사용하여 2021년 1월 1일부터 2023년 12월 31일까지의 모든 날짜 리스트를 생성합니다. 그리고 pd.DataFrame 함수를 사용하여 공휴일 여부를 저장할 빈 데이터프레임을 생성합니다.

holiday_df['ds']에는 생성된 모든 날짜 리스트를 저장하며, holiday_df['holiday']에는 해당 날짜가 공휴일인지 아닌지를 저장합니다. 여기서 lambda 함수를 사용하여 날짜가 한국의 공휴일인 경우 'holiday'를, 아닌 경우에는 'non-holiday'를 저장합니다. 이렇게 생성된 holiday_df 데이터프레임을 이후에 Prophet 모델 등에서 독립변수로 활용하여 시계열 예측 분석을 수행할 수 있습니다.

 

4.

# Prophet 객체 생성
n = Prophet(holidays=holiday_df, weekly_seasonality=True, daily_seasonality=True)

 

 

Prophet() 함수를 호출하여 모델 객체를 생성하고, 이를 n 변수에 할당한다. 함수의 인자로 holidays, weekly_seasonality, daily_seasonality를 전달한다.

  • holidays는 Prophet 모델에서 공휴일 정보를 활용하기 위한 인자로, holiday_df 변수에 저장된 공휴일 정보를 전달하여 해당 날짜가 공휴일인지 아닌지를 반영하여 예측 결과를 출력한다.
  • weekly_seasonality와 daily_seasonality는 각각 주별과 일별 계절성을 고려하여 모델링을 할 지 여부를 설정하는 인자입니다. 이 값이 True인 경우, Prophet 모델에서 해당 계절성 요소를 반영하여 예측 결과를 출력한다.

따라서, 위 코드는 Prophet 모델 객체를 생성하고, 이를 n 변수에 할당하면서 공휴일 정보와 주별, 일별 계절성을 고려하여 모델링을 수행할 것을 설정하는 코드다.

 

5.

# 모델 학습
n.fit(df_train)

#2023년 3월을 포함한 미래의 날짜값 생성(예측할 기간 설정)
future_n = n.make_future_dataframe(periods=31, freq='d')

#예측값 생성
forecast_n = n.predict(future_n)

n.fit(df_train)은 Prophet 모델 객체인 n에 df_train 데이터를 학습시키는 코드입니다. df_train은 Prophet 모델 학습에 사용되는 시계열 데이터를 저장한 Pandas DataFrame 객체다.

n.make_future_dataframe(periods=31, freq='d')는 periods 인자로 전달된 값(31)만큼의 미래 날짜 값을 생성하는 코드입니다. freq 인자는 미래 날짜값을 생성할 때 각 날짜 사이의 간격을 의미합니다. 'd'는 일(day) 단위로 간격을 두겠다는 것을 의미합니다. 즉, 31일의 미래 날짜 값을 생성하고, 이를 future_n 변수에 할당한다.

n.predict(future_n)은 Prophet 모델 객체 n과 future_n을 활용하여 미래의 예측값을 생성하는 코드입니다. forecast_n 변수에 예측 결과가 저장된다.

따라서, 위 코드는 Prophet 모델 객체 n에 df_train을 학습시킨 후, 31일의 미래 날짜 값을 생성하고 이를 활용하여 forecast_n 변수에 예측 결과를 저장하는 코드다.

 

6.

# 성능 평가
df_n_cv = cross_validation(n, initial='300 days', period='60 days', horizon = '30 days')
df_n_p = performance_metrics(df_n_cv)
fig3 = plot_cross_validation_metric(df_n_cv, metric='mape')

cross_validation(n, initial='300 days', period='60 days', horizon = '30 days')는 학습된 Prophet 모델 객체 n에 대해 교차 검증(cross-validation)을 수행하여, 이를 통해 모델의 예측 성능을 평가하는 코드다.

 

initial 인자는 처음 300일 동안의 데이터를 훈련 데이터로 사용하겠다는 것을 의미하며, period 인자는 검증 데이터와 훈련 데이터 간의 간격을 설정한다. 여기서는 60일 간격으로 검증 데이터와 훈련 데이터를 구분한다. horizon 인자는 예측 기간을 의미하며, 여기서는 30일을 예측하도록 설정한다.

performance_metrics(df_n_cv)는 cross_validation 함수를 통해 생성된 교차 검증 결과인 df_n_cv를 활용하여, 모델의 성능을 측정하는 함수다. df_n_cv는 예측 결과와 실제 값의 차이(mae, mse, rmse, mape, mdape)와 관련된 정보가 담긴 DataFrame 객체다. performance_metrics 함수는 이러한 정보를 바탕으로 모델의 성능 지표(mean absolute percentage error, mean absolute scaled error)를 반환한다.

plot_cross_validation_metric(df_n_cv, metric='mape')은 cross_validation 함수를 통해 생성된 교차 검증 결과인 df_n_cv를 활용하여, 모델의 성능을 시각화하는 함수다. metric 인자를 통해 어떤 성능 지표(mae, mse, rmse, mape, mdape)를 사용할 것인지 설정한다. plot_cross_validation_metric 함수는 예측 결과와 실제 값의 차이(mape)를 바탕으로 교차 검증 결과를 시각화한다.

따라서, 위 코드는 Prophet 모델의 성능을 교차 검증을 통해 평가하고, 이를 바탕으로 성능 지표와 시각화 결과를 생성하는 코드다.

 

성능평가를 진행한 결과, 아래와 같이 결과물이 나타난다.

[TO-BE]

horizon     30 days 00:00:00
mse              5585.983818
rmse                74.73944
mae                48.337485
mape                0.198669
mdape               0.101449
smape               0.201137
coverage            0.708333
Name: 27, dtype: object

[AS-IS]

horizon     30 days 00:00:00
mse              6290.790264
rmse               79.314502
mae                51.748078
mape                0.227036
mdape               0.178369
smape               0.183599
coverage            0.708333
Name: 27, dtype: object

 

공휴일에 대한 맵핑 이후에 예측의 정확도가 조금 향상한 것을 확인할 수 있었다.