[시계열분석] 주식데이터 주가예측 (ARIMA, auto_arima)
* 라이브러리 정의
import pandas as pd
import datetime
import matplotlib.pyplot as plt
# 시각화 라이브러리 및 마이너스 기호설정
import platform
from matplotlib import font_manager, rc
# 마이너스 기호 및 한글 설정
# - 마이너스 기호
plt.rcParams["axes.unicode_minus"] = False
# - os별 한글설정
if platform.system() == "Windows":
path = "c:/Windows/Fonts/malgun.ttf"
font_name = font_manager.FontProperties(fname=path).get_name()
rc("font", family=font_name)
### mac인경우
elif platform.system() == "Darwin":
rc("font", family="Applegothic")
### 리눅스인 경우
elif platform.system() == "Linux":
path = "usr/share/fonts/NanumGothic.ttf"
font_name = font_manager.FontProperties(fname=path).get_name()
rc("font", family=font_name)
else:
print("이거뭐임?")
* 10년 치 주가 정보 수집하기
- 증권사 : yahoo finance
- 수집 증권 : 구글 주식 수집
- 수집기간 : 2012년 10월 31일 ~ 2022년 10월 31일까지 데이터
- 증권사 제공 라이브러리 : yfinance 라이브러리를 제공
- 라이브러리 설치 필요 : pip install yfinance
import yfinance as yf
start = datetime.datetime(2012, 10, 31)
end = datetime.datetime(2022, 10, 31)
goog_data = yf.download("GOOG", start=start, end=end)
goog_data
* 시작 및 종료 기간 변수 설정
- 날짜 타입으로 넣어야 한다.
start = pd.to_datetime("2012-10-31")
end = pd.to_datetime("2022-10-31")
- index : 날짜타입
- Open : 시작가
- Hight : 상한가
- Low : 하한가
- Close : 종가
- Adj Close : 수정종가(분할, 배당, 배분, 신주 등이
발생한 경우 조정이 이루어짐)
- Volume : 거래량
* 전처리 전 데이터 확인하기
goog_data.info()
goog_data.describe()
* 데이터 전처리
- 50일치에 대한 주식 흐름(이동) 확인하기
#1
data = goog_data["Adj Close"]
data
#2
# 기간설정 : 50일
interval = 50
# 이동평균 계산하기
# - 주식 가격의 흐름을 유연하게 보기 좋게 하기 위해서 사용
# - 실제 가격 흐름과 이동평균값과 차이가 보이는 부분 : 변동성이 있는 부분
rolmean = data.rolling(interval).mean()
rolmean
# 이동표준편차 계산하기
rolstd = data.rolling(interval).std()
rolstd
#1 에서는 모든 데이터를 보여주지만 처리 후 #2에서는 50개의 데이터만 보여주고 나머지는 NaN으로 표현된다.
* 시각화 하기
# 원본, 이동평균, 이동표준편차 시각화
plt.figure(figsize=(10, 6))
plt.title("실제, 이동평균, 이동표준편차 시각화")
### 실제 주식가격 그리기
plt.plot(data, color="blue", label="실제 원본 주가")
# 50일 간격의 이동평균 그리기
plt.plot(rolmean, color="red", label=f"이동평균 {interval}일 기준")
# 50일 간격의 이동표준편차 그리기
plt.plot(rolstd, color="black", label=f"이동표준편차 {interval}일 기준")
plt.xlabel("Date")
plt.ylabel("data")
plt.grid()
plt.legend()
plt.show()
<시각화 해석>
* 비정상성 : 평균이 일정하지 않고 오르락내리락하는 불규칙 형태를 의미함
- 시계열 분석 시에는 비정상성을 정상성으로 만들어서 분석을 진행한다
=> 정상성으로 만들기 위해 차수(d)라는 개념이 적용됨(아래서 설명)
=> 표준화(정규화) 시키는 개념과 유사
- 계절성을 나타내지 않는 것으로 보이며, 특징적 패턴을 보이고 있지 않음(특정 주기성이 없음)
* 시계열 데이터 분석 모델 => ARIMA모델
<시계열 분석>
- 시계열 분석에서 주로 사용되는 모델은 ARIMA모델로, 오랫동안 사용되어 온 통계학적 기술통계 모델임
- 시계열 분석은 일반적으로 예측분석 중에서도 시간을 독립변수(x)로 사용하고, 다른 데이터를 종속변수(y)로 사용하여 예측하는 분석 방법임
- ARIMA(Autoregressive(AR) Intergrated(I) Moving Average(MA))
: 시계열 분석(예측)에서 가장 널리 사용되는 모델 중 하나
: 시계열 분석은 현 시점까지의 데이터를 이용해서 앞으로 어떤 패턴의 차트를 그릴지 예측하는 분석기법임.
* AR(Autoregressive) : "자기 상관"이라고 한다.
- 이전의 값이 이후의 값에 영향을 미치고 있는 상황(관계)
* MA(Moving Average) : "이동평균" 이라고 한다
- 특정 변수의 평균값이 지속적으로 증가하거나 감소하는 주세(추이)
<정상성(stationary)과 비정상성(Non-stationary)>
* 정상성
- 평균과 분산이 일정한 형태
* 비 정상성
- 평균과 분산이 일어나지 않는 상태
- 시간에 따라 평균 수준이 다른다,
- 특징적 패턴(trand)이나 계절성(seasonality)에 영향을 받는 형태
- 예시 데이터 형태 : 겨울의 난방비 증가, 여름에 아이스트림 판매량 등등..
- 비정상 데이터는 예측의 범위가 너무 다양하고 많기에 고려해야 할 특성들이 많음
- 이에, 비정상성 데이터를 정상성으로 반환하여 분석을 진행하게 됨
- 정상성으로 분석을 진행하며, 예측범위가 일정하게 줄어들고, 성능이 개선되는 효과를 발휘함
<비정상성을 정상성으로 반환하는 방법들>
- 평균의 정상화를 위한 차분 사용(** 주로 사용하는 항법)
- 분산의 안정화를 위한 로그 변환 사용
- 제곱/제곱근 변환 사용
- 이외 등등
* 차분 : 비정상성을 정상성으로 만들기 위해 관측값들의 차이를 계산하여 사용하게 됨.
* 시계열 정상성 확인하기 => ADF테스트
<ADF 테스트; Augmented Dickey-Fuller Test>
- 시계열 데이터의 정상성 여부를 통계적인 정량 방법으로 검증하는 방법
- 귀무가설과 대립가설에 따라 결정됨
- 귀무가설 : 기존 연구이론
- 대립가설 : 신규 연구리론
- 귀무가설과 대립가설의 보편적 기준 => p-value<0.05 , 005(증감가능)
: p-value < 0.05 이면, 귀무가설 가각, 대립가설 채택
: p-value > 0.05 이면, 귀무가설 채택 (연구방향을 수정해야 함)
- 시계열 분석에서는 정상성과 비정상성 데이터의 형태를 구분하는 용도로 사용
- ADF테스트 라이브러리 : statsmodels 패키지의 adfuller 라이브러리 사용
-> 설치 필요 : pip install statsmodels
# ADF라이브러리
from statsmodels.tsa.stattools import adfuller
* 원본(수정종가) 데이터를 이용해서 ADF테스트하기
result = adfuller(data.values)
result
결과 :
(-0.7728513783029752,
0.826966999646946,
24,
2492,
{'1%': -3.432976825339513,
'5%': -2.862700515844509,
'10%': -2.5673877878037974},
8123.380716155494)
* p-value 추출하기
- ADF결과의 1번째 값이 => p-value 값임
print(f"p-value : {result[1]}")
결과 : p-value : 0.826966999646946
<해석>
- p-value < 0.05을 만족하지 않으므로, 귀무가설을 기각할 수 없음 즉, 유의미하지 않음.
(* p-value의 값이 0.827로 0.05보다 크므로 이러면 안 된다. 통념상 위와 같이 쓴다.)
- 따라서 구글 주식 데이터는 "비정상성" 데이터임
===> 비정상성을 띄기 때문에 정상성으로 만들기 위한 "차분" 처리가 필요함.
* 1d(1차) 차분 계산하기
dff1 = data.diff().dropna()
dff1
- 1d(1차) : 1칸씩 이동하면서 이전과 현채의 차이값을 사용함
- 사용함수 : diff()
- 차분을 계산하게 되면, 최초 또는 차분의 이동 거리에 따라서
Nan이 발생 ==> Nan은 제거하고 사용함.
* 시각화
plt.figure(figsize=(15, 5))
plt.title("1d diff")
plt.plot(dff1)
* 차분 결과 데이터를 이용하여 정상성 여부 확인하기
- ADF테스트하여 p-value < 0.05 확인하기
result = adfuller(dff1.values)
print("p-value : %f" %result[1])
결과 : p-value : 0.000000
<해석>
- p-value < 0.05을 만족하므로, 유의미함.
- 즉, 귀무가설을 기각하고 대립가설을 채택
- 시계열 분석에서는 차분 처리를 통해 정상성 데이터로 변환되었으며, 이후 ARIMA분석을 통해 진행이 가능한 것으로 증명되었음.
- 사용된 차분은 1차 차분을 수행하여 증명하였음.
<차분의 차수 사용>
- 1차 차분 : 일반적으로 시계열 곡선이 특정한 트렌드(패턴이 있음)를 가지고 있을 때 사용
- 2차 차분 : 시간에 따라 들쑥날쑥한 트렌드를 가지고 있을 때 사용(랜덤 한 들쑥날쑥)
* ARIMA모델의 모수(하이퍼파라미터) 찾기
- ARIMA모델에서 사용되는 중요한 3개의 하이퍼파라미터
=> p, d, q
- ARIMA(AR, MA, ARMA) 모델을 사용하기 위해서는
=> AR(자기 회귀모형, p), 차분(d), MA(이동평균모형, q) 값을 결정해야 함
- 결정방법
1. ACF plot과 PACF plot을 통해 모수(하이퍼파라미터)를 결정할 수 있음
- 현재값이 => 과거 값과 어떤 관계(relationship)가 있는지를 보여주는 그래프로 확인
2. pmdarima라이브러리의 ndiffs, auto_arima(*주로 사용 grid_search_cv랑 동일한 공식으로 작동) 함수를 사용하여
모수(하이퍼파라미터)를 결정할 수 있음
- 주로 auto_arima 함수를 사용함
* (방법-1) ACF plot과 PACF plot을 통해 모수(하이퍼파라미터)를 결정
# 사용라이브러리
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
* 실제 원본데이터를 이용해서 => ACF 및 PACF 시각화하기
plot_acf(data)
plot_pacf(data)
plt.show()
파란색 박스에 들어가지 않으면 자기 상관이 없다.
<해석>
* ACF plot
- ACF plot에서 막대그래프가 천천히 감소되는 것으로 보임
- 이는 주식 데이터가 주기에 따라 일정하지 않은 비정상 데이터로 판단한다.
* PACF plot
- 첫 값을 제외한 1개 이후 파란 박스에 들어가면서 막대그래프가 끊기는 것으로 보임
- 이는 자기 회기모형(AR)의 결괏값이 1개 이후인, 즉 p는 1인 값을 활용하는 것이 적절하다는 의미
- 이동평균(MA)의 값은 AR과의 차이값이 0이 되도록 하는 것이 일반적임.
=> 따라서, MA는 1이 적절함을 의미함.
* 1차 차분 데이터로 ACF 및 PACF 시각화하기
plot_acf(dff1)
plot_pacf(dff1)
plt.show()
<결론>
- AR(p) = 1 / d = 1 / MA(q) = 1 이 적절
- 이때, MA(q) 값은 AR - MA값을 사용하기도 한다. (q=0)
- MA값은 AR - MA를 사용
* (방법-2) pmdarima라이브러리의 ndiffs, auto_arima 함수를 사용하여 모수(하이퍼파라미터)를 결정
# 사용라이브러리
# - 라이브러리 설치해야함 : pip install pmdarima
import pmdarima as pm
from pmdarima.arima import ndiffs
* ndiffs 방법 : 차수를 결정하는 함수
n_diffs = ndiffs(data, alpha=0.05, test="adf", max_d=6)
print(f"결정된 차수 : {n_diffs}")
결과 : 결정된 차수 : 1
- data : 원본데이터
- alpha : 차분 횟수를 결정하는 데 사용할 p-value(유의수준) 기준
- test : 차분 횟수를 결정하는데 사용할 테스트 방법 지정
: 주로 adf테스트 방법을 사용(kpss 테스트 방법도 있으나, 거의 사용 안 함)
- max_d : 최대 차분 횟수를 제한함(이 범위 내에서 가장 적절한 차수를 결정)
* auto_arima 함수 사용 : p, d, q 값들을 모두 추출해 준다.
model = pm.auto_arima(
y=data,
d=1,
start_p=0, max_p=3,
start_q=0, max_q=3,
m=1, seasonal=False,
stepwise=True,
trace=True
)
- y : 데이터 원본
- d : 차분의 차수, 이를 지정하지 않으면 실행 시간이 매우 길어짐(기본값 : None)
- start_p(기본값 2), max_p(기본값 5): AR(p)를 찾기 위한 범위(start_p에서 max_p까지 수행)
- start_q(기본값 2), max_q(기본값 5): MA(q)를 찾기 위한 범위(start_q에서 max_q까지 수행)
- m : 계절적 특성이 있을 때 사용하는 매개변수(기본값 : 1) -> 차수를 의미함
- seasonal : 계절적 특성이 있을 때 사용(기본값 : True)
: 계절적 특성이 있을 때(True) -> m의 값은 계절적 특성의 범위 차수 지정(보통 : 3)
: 계절적 특성이 없을 때(False) -> m은 1을 보통사용
- stepwise : 최적의 모수를 찾기 위한 알고리즘을 사용할지 여부(기본값 : True)
(최적의 모수 알고리즘 : 힌트만-칸다카르 알고리즘이 적용됨)
- trace: 결과 출력 여부(기본값 : False)
결과 :
Performing stepwise search to minimize aic
ARIMA(0,1,0)(0,0,0)[0] intercept : AIC=8231.556, Time=0.05 sec
ARIMA(1,1,0)(0,0,0)[0] intercept : AIC=8221.824, Time=0.08 sec
ARIMA(0,1,1)(0,0,0)[0] intercept : AIC=8221.860, Time=0.13 sec
ARIMA(0,1,0)(0,0,0)[0] : AIC=8231.191, Time=0.04 sec
ARIMA(2,1,0)(0,0,0)[0] intercept : AIC=8223.799, Time=0.12 sec
ARIMA(1,1,1)(0,0,0)[0] intercept : AIC=8223.097, Time=0.35 sec
ARIMA(2,1,1)(0,0,0)[0] intercept : AIC=8225.820, Time=0.18 sec
ARIMA(1,1,0)(0,0,0)[0] : AIC=8221.686, Time=0.05 sec *AIC 값이 가장 낮은 값이 선정됨
ARIMA(2,1,0)(0,0,0)[0] : AIC=8223.648, Time=0.08 sec
ARIMA(1,1,1)(0,0,0)[0] : AIC=8222.890, Time=0.19 sec
ARIMA(0,1,1)(0,0,0)[0] : AIC=8221.739, Time=0.07 sec
ARIMA(2,1,1)(0,0,0)[0] : AIC=8224.664, Time=0.34 sec
Best model: ARIMA(1,1,0)(0,0,0)[0]
Total fit time: 1.683 seconds
ARIMA(1,1,0)(0,0,0)[0]
▲ ▲ ▲
각각의 값은 (p, d, q)
<해석>
- auto_arima를 사용한 결과에서 최적의 모델은 ARIMA(1, 1, 0) 모형으로 결정됨
- 모델이 설명력(결정력)이 좋을수록 AIC값이 작아지고, 모델의 복잡도가 높아질수록 AIC값이 커짐
- AIC값은 작을수록 좋음