본문 바로가기
시계열 데이터

시계열 데이터 - 파이썬 auto_arima 및 ARIMA 모델 정리

by Tiabet 2023. 2. 13.

오늘은 파이썬의 auto_arima 함수를 사용해보고 ARIMA  함수 내용을 마치고자 한다.

 

이전의 코로나 확진자 수를 예측해보는 코드를 짜면서 든 생각이, "그럼 코로나 확진자 말고 다른 데이터들도 다 이렇게 ARIMA 모델로 예측할 수 있는데, 그러면 어떤 데이터가 들어오더라도 사용자에게 일정 수준의 예측치를 제공해줄 수 있겠구나!" 하는 것이었다. 그래서 어떤 데이터가 들어오던 ARIMA 모델로 단기간의 미래를 예측해주는 그런 코드를 짜보기로 했다. 이걸 쉽게 가능하게 해준 것이 바로 auto_arima 함수였다.

auto_arima 응용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from statsmodels.tsa.arima.model import ARIMA
import warnings
import itertools
 
warnings.filterwarnings("ignore"# to suppress warnings
 
= d = q = range(05# parameter combinations for ARIMA model
pdq = list(itertools.product(p, d, q))
 
best_AIC = float("inf"# Initialize the best AIC score as infinity
best_params = None # Initialize the best parameters as None
 
for param in pdq:
    try:
        model = ARIMA(train_data, order=param)
    
        res = model.fit()
        if res.aic < best_AIC:
                best_AIC = res.aic
                best_params = (param)
    except:
        continue
 
print("Best ARIMA parameters: ", best_params)
 
cs

위 코드는 이전 코로나 확진자 수 예측하기에서 최적의 계수를 찾기 위해 짰던 코드였다.

https://tiabet0929.tistory.com/12

 

시계열 데이터 - 코로나 확진자 수 ARIMA 모델로 예측하기 (2)

저번 포스팅에 이어 코로나 확진자 수를 ARIMA 모델을 사용해 예측해본 결과를 마저 기록해보도록 하겠다. https://tiabet0929.tistory.com/10

tiabet0929.tistory.com

보면 알겠지만 for 문을 돌리면서 AIC 가 가장 적게 나오는 계수를 선택하는 방식이다. ACF, PACF 들을 보면서 계수를 선택하는 것이 깔끔하진 하지만, 사실 일일이 보면서 선택하는 것은 실수할 우려도 있고 무엇보다 귀찮다.. 그래서 이런 방식을 채택하게 된 것인데, auto_arima 가 작동하는 원리도 이와 같았다. 

1
2
3
4
5
6
7
8
9
from pmdarima import auto_arima
 
model = auto_arima(train_data, seasonal=True, suppress_warnings=True, error_action="ignore", stepwise=True,
                   trace=True, random_state=42)
 
print(model.order, model.seasonal_order)
 
model_order = model.order
model_seasonal_order = model.seasonal_order
cs

auto_arima 는 pmdarima 라는 패키지에 있으므로 이를 호출하면 된다.

 

seasonal 변수는 계절성이 있으면 True, 없으면 False 라고 하는게 맞는데, 없어도 True라고 해놓으면 알아서 계절성 변수들을 0으로 처리하기 때문에 문제가 없다. 그 뒤의 변수들은 정석적인 것이므로 저렇게 해놓고 넘어갔다. 출력 결과는 아래와 같다. 

Performing stepwise search to minimize aic
 ARIMA(2,1,2)(0,0,0)[0] intercept   : AIC=inf, Time=0.57 sec
 ARIMA(0,1,0)(0,0,0)[0] intercept   : AIC=22648.759, Time=0.03 sec
 ARIMA(1,1,0)(0,0,0)[0] intercept   : AIC=22577.884, Time=0.05 sec
 ARIMA(0,1,1)(0,0,0)[0] intercept   : AIC=22481.226, Time=0.11 sec
 ARIMA(0,1,0)(0,0,0)[0]             : AIC=22646.760, Time=0.04 sec
 ARIMA(1,1,1)(0,0,0)[0] intercept   : AIC=22456.702, Time=0.16 sec
 ARIMA(2,1,1)(0,0,0)[0] intercept   : AIC=22436.498, Time=0.22 sec
 ARIMA(2,1,0)(0,0,0)[0] intercept   : AIC=22486.609, Time=0.09 sec
 ARIMA(3,1,1)(0,0,0)[0] intercept   : AIC=22438.172, Time=0.31 sec
 ARIMA(1,1,2)(0,0,0)[0] intercept   : AIC=22427.162, Time=0.32 sec
 ARIMA(0,1,2)(0,0,0)[0] intercept   : AIC=22443.764, Time=0.16 sec
 ARIMA(1,1,3)(0,0,0)[0] intercept   : AIC=22317.805, Time=0.37 sec
 ARIMA(0,1,3)(0,0,0)[0] intercept   : AIC=22429.977, Time=0.24 sec
 ARIMA(2,1,3)(0,0,0)[0] intercept   : AIC=22237.507, Time=0.89 sec
 ARIMA(3,1,3)(0,0,0)[0] intercept   : AIC=22236.967, Time=1.30 sec
 ARIMA(3,1,2)(0,0,0)[0] intercept   : AIC=inf, Time=0.92 sec
 ARIMA(4,1,3)(0,0,0)[0] intercept   : AIC=22154.381, Time=1.39 sec
 ARIMA(4,1,2)(0,0,0)[0] intercept   : AIC=22162.547, Time=0.94 sec
 ARIMA(5,1,3)(0,0,0)[0] intercept   : AIC=22144.029, Time=1.84 sec
 ARIMA(5,1,2)(0,0,0)[0] intercept   : AIC=22167.169, Time=0.96 sec
 ARIMA(5,1,4)(0,0,0)[0] intercept   : AIC=22047.071, Time=1.64 sec
 ARIMA(4,1,4)(0,0,0)[0] intercept   : AIC=22103.739, Time=0.88 sec
 ARIMA(5,1,5)(0,0,0)[0] intercept   : AIC=22040.825, Time=2.26 sec
 ARIMA(4,1,5)(0,0,0)[0] intercept   : AIC=22064.729, Time=2.05 sec
 ARIMA(5,1,5)(0,0,0)[0]             : AIC=22037.477, Time=1.96 sec
 ARIMA(4,1,5)(0,0,0)[0]             : AIC=22064.121, Time=1.98 sec
 ARIMA(5,1,4)(0,0,0)[0]             : AIC=22045.078, Time=1.59 sec
 ARIMA(4,1,4)(0,0,0)[0]             : AIC=22101.742, Time=0.75 sec

Best model:  ARIMA(5,1,5)(0,0,0)[0]          
Total fit time: 24.040 seconds
(5, 1, 5) (0, 0, 0, 0)

처음에 (2,1,2) 모델을 적용한 뒤 거의 모든 계수들을 돌려보면서 최적값을 찾는 것을 확인할 수 있다. 아마 비계절성 계수만 돌려서 시간이 조금 단축된 것이 아닐까 싶다.  

 

실행결과

데이터는 전체 데이터를 사용했다. auto_arima 함수를 사용하는 데에 의의가 있다고 생각했지 딱히 좋은 결과를 얻으려고는 안 했기 때문에 1년치 데이터를 사용하는 것은 생략했다. 역시 초반부의 데이터 때문에 그렇게 좋게 예측하지는 못하는 모습이다. 신뢰구간도 한 번 확인해보자.

 

1
2
3
4
5
6
7
8
pred = model_fit.predict(n_periods=len(test_data), return_conf_int=True)
np.asarray(pred[1])
 
plt.plot(test_data, label='Observed')
plt.plot(pred[0], 'r', label='Predicted')
plt.fill_between(test_data.index, pred[1][:,0], pred[1][:,1], color='gray',alpha=0.2)
plt.legend()
plt.show()
cs

다행히 신뢰구간에는 들어오는 모습이다. 이렇게 auto_arima를 사용해서 ACF, PACF를 보지 않고, 또 반복문 코드를 쓰지 않고 최적의 계수를 찾아보았다. 내 생각엔 계수가 (5, 1, 5) 인 것은 P와 Q의 max가 5로 설정되어 있기 때문인 것 같아서 max를 더 늘릴 수 있겠으나, 실행 결과를 보면 계수가 커진다고 해서 AIC가 급격하게 줄어들지가 않았다. 그래서 더 하는 게 의미가 없을 걸로 생각된다.

 

auto_arima 의 차이점

이건 공부하면서 auto_arima와 ARIMA 함수의 차이점에 대해 발견한 것들이 있어 몇 개 기록해보겠다. 차이점들의 원인은 ARIMA 함수는 statsmodels 패키지에, auto_arima 함수는 pmdarima 패키지에 있기 때문에 발생하는 것 같다. (원래 파이썬의 statsmodels 패키지에  auto_arima 함수가 없던 것을 보고 다른 사람이 pmdarima 패키지를 만들면서 R의 autoARIMA 함수를 보고 auto_arima 함수를 만들었다고 한다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
from statsmodels.tsa.arima.model import ARIMA
 
 
model = ARIMA(train_data, order=(4,3,3))
model_fit = model.fit()
 
predictions = model_fit.predict(start=test_data.index[0], end=test_data.index[-1], typ='levels')
 
plt.plot(train_data,label='train data', color = 'blue')
plt.plot(test_data,label='test data', color = 'green')
plt.plot(predictions,label='ARIMA model', color = 'red')
plt.legend()
plt.show()
cs

처음엔 여기서 ARIMA 를 auto_arima로만 바꿔버리고 실행했더니 여러 오류가 떠서 다른 자료를 참고했어야 했다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pmdarima import auto_arima
 
model = auto_arima(train_data, seasonal=True, suppress_warnings=True, error_action="ignore", stepwise=True,
                   trace=True, random_state=42)
 
model_fit = model.fit(train_data)
 
predictions = model_fit.predict(n_periods=len(test_data))
 
 
plt.plot(train_data, label = 'Train', color = 'blue')
plt.plot(test_data, label = 'Real', color = 'green')
plt.plot(predictions, label='Predictions', color = 'red')
plt.legend()
plt.xlabel('Date')
plt.ylabel('Weekly Patients Numbers')
plt.title('Weekly Predictions')
plt.show()
cs

자세히 보면 fit 함수의 파라미터 변수에 차이가 있음을 알 수 있다. ARIMA 함수의 fit 함수는 아무것도 요구하지 않는데, auto_arima 함수의 fit 함수는 사용한 데이터를 한 번 더 받아줘야 한다. fit 함수의 기능은 데이터에 맞게 ARIMA 모델을 말그대로 fit 해주는 것이다. (최대우도법, MLE를 사용한다고 한다. 통계시간에 아주 중요한 내용으로 배운다.)

 

또 하나 다른 점은 predict를 할 때이다. fit() 의 결과물인 model_fit 에서 predict를 사용하는데, ARIMA 함수의 predict 함수는 파라미터로 시작점, 끝점을 요구한다. 하지만 auto_arima의 predict 함수는 단순히 예측할 길이만을 요구한다. (시작점, 끝점을 파라미터로 받지 못 함.) 그래서 ARIMA 함수의 결과 그래프를 그릴 때 그냥 predict의 시작점을 0, 끝점을 미래 한 시점으로 설정하면 그래프가 처음부터 쭉 그려지지만, auto_arima는 그냥 미래만 그릴 수 있다. 즉 Output에 차이가 있다.

 

참고로 ARIMA 모델의 predict 함수도 시작점, 끝점 말고 단 하나의 파라미터만 입력할 수 있다. 하지만 그러면 입력한 시점의 예측치를 알려줄 뿐, 엄연히 auto_arima의 predict와는 다르다.

auto_arima의 update 함수

자료들을 참고하다가 auto_arima에 유용한 함수가 있어 사용해보았다. 바로 update 라는 함수인데, pmdarima의 공식 소개 홈페이지에선 데이터의 Refresh를 굉장히 중요하다고 소개하고 있다. 이 Refresh를 하는 데에 update라는 함수가 사용된다.

 

실제 회사에서 일하면서 ARIMA 함수를 사용하고 있다고 생각해보자. 회사에선 매일매일 데이터를 수집하고 있기 때문에 업데이트가 활발하게 이루어질 것이다. update 함수가 업데이트된 데이터를 불러오고 모델을 다시 돌리는 과정을 생략시켜주는 것은 아니다. 다만 이런 상황에선 유용하게 쓰일 수 있다.

이전에 ARIMA 모델 포스팅에서 해봤던 애플 주식을 ARIMA 모델로 예측한 결과다. 보다시피 직선으로 예측하기 때문에 딱봐도 좋지 않은 예측치임을 알 수 있다. 이는 선형회귀의 특성상 비계절성 계수 PDQ 가 0, 1 이런 식으로 나오면 어쩔 수 없이 직선이 나오기 때문에, 이전 포스팅들에서도 내가 ARIMA 모델의 한계라고 지적한 바가 있었다. 하지만 만약 update 함수로 데이터가 들어올 때마다 모델을 업데이트해줄 수 있다면? 훨씬 더 좋은 결과를 가져올 수 있을 것이다. 

 

1
2
3
4
5
6
7
8
9
pred_data = []
 
for new_data in test_data:
    pred = model.predict(n_periods=1)
    pred_data.append(pred[0])
    
    model.update(new_data)
   
print(pred_data)
cs

update 함수는 이런 식으로 사용할 수 있다. predict 함수의 예측 기간을 1로 설정하면 바로 다음날의 예측결과만 pred에 저장된다. 이 pred를 pred_data라는 array에다가 저장시켜줘서 한 번 성능을 테스트해본 것이다.

 

1
2
3
4
5
6
7
8
9
pred_data=pd.DataFrame(pred_data,columns=['Predictions'],index=test_data.index)
 
print(pred_data)
 
plt.plot(pred_data,label='prediction data')
plt.plot(test_data,label='test data')
plt.title("using update function")
plt.legend()
plt.show()
cs

실행 결과. 오른쪽이 update 함수의 결과다.

 

마무리

이것으로 ARIMA 함수에 대한 모든 것을 정리해보았다. 꼬박 1달 반이 넘게 걸렸는데 꾸준히 공부했다면 더 빨리 끝냈을 수도 있었을 것 같다. 이번 학기에 시계열 분석 수업을 듣기 때문에, 앞으로 시계열 데이터 카테고리는 학기 중에 공부하면서 업데이트가 될 것 같다. 지금은 데이터 수집을 위한 웹 크롤링을 공부하고 있고, 아마 학교 공부를 겸하면서 xgboost 와 lgbm 모델에 대해 공부를 할 것 같다. 

 

참고자료

 https://assaeunji.github.io/data%20analysis/2021-09-25-arimastock/

 

시계열 분석 시리즈 (4): Python auto_arima로 삼성 주가 제대로 예측하기

ARIMA 예측이 일직선으로 되신다구요? 이 글을 보면 어느 정도 그 이유와 해결 방법을 찾을 수 있습니다. 이번 포스팅에서는 시계열 분석 시리즈 (3): auto_arima를 잘 쓰기 위한 배경 지식에 이어 실

assaeunji.github.io

http://alkaline-ml.com/pmdarima/index.html

 

pmdarima: ARIMA estimators for Python — pmdarima 2.0.2 documentation

Donations We’re a team of volunteers who maintain and contribute to this project because we want to see Python’s scientific and ML community continue to flourish. If the project has helped your or your team, please consider donating to the project!

alkaline-ml.com