티스토리 뷰
3.2 비트코인 시세 예측하기¶
이번엔 비트코인 시세 예측이다. 비트코인 시세처럼 연속적인 시간에 따라 다르게 측정되는 데이터를 시계열 데이터라 하며, 이를 분석하는 것을 '시계열 데이터 분석' 이라고 한다.
step.1 탐색: 시간 정보가 포함된 데이터 살펴보기¶
우선 비트코인 데이터를 탐색하자. 아래 결과와 같이 날짜와 가격에 대한 2개의 피처로 구성되어 있다. 이러한 데이터셋을 시계열 데이터셋이라고 한다.
- market price 데이터셋의 기본 정보 구하기
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 데이터 출처 : # Data Source : https://www.blockchain.com/ko/charts/market-price?timespan=60days
file_path = '/home/jaeyoon89/python-data-analysis/data/market-price.csv'
bitcoin_df = pd.read_csv(file_path, names = ['day','price'])
print(bitcoin_df.shape)
print(bitcoin_df.info())
bitcoin_df.tail()
(365, 2) <class 'pandas.core.frame.DataFrame'> RangeIndex: 365 entries, 0 to 364 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 day 365 non-null object 1 price 365 non-null float64 dtypes: float64(1), object(1) memory usage: 5.8+ KB None
day | price | |
---|---|---|
360 | 2018-08-22 00:00:00 | 6575.229167 |
361 | 2018-08-23 00:00:00 | 6434.881667 |
362 | 2018-08-24 00:00:00 | 6543.645714 |
363 | 2018-08-25 00:00:00 | 6719.429231 |
364 | 2018-08-26 00:00:00 | 6673.274167 |
그리고 아래의 코드는 시계열 정보를 데이터 프레임의 index로 설정하여 가격의 추이를 시각화한 것이다.
bitcoin_df['day'] = pd.to_datetime(bitcoin_df['day'])
bitcoin_df.index = bitcoin_df['day']
bitcoin_df.set_index('day', inplace=True)
bitcoin_df.plot()
plt.show()
step.2 예측: 파이썬 라이브러리를 활용해 시세 예측하기¶
이번 절에서 첫 번째로 사용할 시계열 예측 분석 방법은 ARIMA 분석 방법이다. ARIMA는 전통적인 시계열 예측 방법으로 크게 두 가지 개념을 포함하고 있다. 첫 번째는 AR모델로 자기 자신의 과거를 정보로 사용하는 것이다. 두 번째 개념은 MA모델로 '이전 항에서의 오차를 이용하여 현재 항의 상태를 추론하겠다' 라는 방법이다. 그리고 이 둘을 합친 것을 ARMA 모델이라고 하며, 조금 더 나아간 ARIMA 모델은 ARMA 모델에 추세 변동의 경향성까지 반영한 방법이다.
- ARIMA 모델 활용하기 : 모델 학습
from statsmodels.tsa.arima_model import ARIMA
import statsmodels.api as sm
model = ARIMA(bitcoin_df.price.values, order=(2,1,2))
model_fit = model.fit(trend='c', full_output=True, disp=True)
print(model_fit.summary())
/home/jaeyoon89/.local/lib/python3.6/site-packages/statsmodels/tsa/arima_model.py:472: FutureWarning: statsmodels.tsa.arima_model.ARMA and statsmodels.tsa.arima_model.ARIMA have been deprecated in favor of statsmodels.tsa.arima.model.ARIMA (note the . between arima and model) and statsmodels.tsa.SARIMAX. These will be removed after the 0.12 release. statsmodels.tsa.arima.model.ARIMA makes use of the statespace framework and is both well tested and maintained. To silence this warning and continue using ARMA and ARIMA until they are removed, use: import warnings warnings.filterwarnings('ignore', 'statsmodels.tsa.arima_model.ARMA', FutureWarning) warnings.filterwarnings('ignore', 'statsmodels.tsa.arima_model.ARIMA', FutureWarning) warnings.warn(ARIMA_DEPRECATION_WARN, FutureWarning)
ARIMA Model Results ============================================================================== Dep. Variable: D.y No. Observations: 364 Model: ARIMA(2, 1, 2) Log Likelihood -2787.553 Method: css-mle S.D. of innovations 512.415 Date: Wed, 14 Apr 2021 AIC 5587.107 Time: 23:13:52 BIC 5610.490 Sample: 1 HQIC 5596.400 ============================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------ const 6.3192 27.794 0.227 0.820 -48.156 60.795 ar.L1.D.y -0.3788 1.827 -0.207 0.836 -3.959 3.201 ar.L2.D.y 0.1586 1.191 0.133 0.894 -2.175 2.492 ma.L1.D.y 0.4569 1.822 0.251 0.802 -3.115 4.028 ma.L2.D.y -0.1941 1.337 -0.145 0.885 -2.816 2.427 Roots ============================================================================= Real Imaginary Modulus Frequency ----------------------------------------------------------------------------- AR.1 -1.5864 +0.0000j 1.5864 0.5000 AR.2 3.9753 +0.0000j 3.9753 0.0000 MA.1 -1.3797 +0.0000j 1.3797 0.5000 MA.2 3.7332 +0.0000j 3.7332 0.0000 -----------------------------------------------------------------------------
/home/jaeyoon89/.local/lib/python3.6/site-packages/statsmodels/tsa/arima_model.py:472: FutureWarning: statsmodels.tsa.arima_model.ARMA and statsmodels.tsa.arima_model.ARIMA have been deprecated in favor of statsmodels.tsa.arima.model.ARIMA (note the . between arima and model) and statsmodels.tsa.SARIMAX. These will be removed after the 0.12 release. statsmodels.tsa.arima.model.ARIMA makes use of the statespace framework and is both well tested and maintained. To silence this warning and continue using ARMA and ARIMA until they are removed, use: import warnings warnings.filterwarnings('ignore', 'statsmodels.tsa.arima_model.ARMA', FutureWarning) warnings.filterwarnings('ignore', 'statsmodels.tsa.arima_model.ARIMA', FutureWarning) warnings.warn(ARIMA_DEPRECATION_WARN, FutureWarning)
다음으로 ARIMA 모델의 학습 결과를 알아보자. 아래의 실행 결과 중 첫 번째 그래프는 학습한 모델에 학습 데이터셋을 넣었을 때의 시계열 예측 결과이다.
- ARIMA 모델 활용하기 : 모델의 성능 & 예측 결과 시각화
fig = model_fit.plot_predict()
residuals = pd.DataFrame(model_fit.resid)
residuals.plot()
<AxesSubplot:>
두 번째 그래프는 실제값과 예측값 사이의 오차 변동을 나타내는 그래프이다. 만약 이 그래프 폭이 일정하다면 좋은 예측 모델을 학습시킨 것이라고 생각할 수 있다. 하지만 실행 결과에서는 오차 변동이 매우 불안정하다. 다음으로 ARIMA 모델을 평가해보자. 모델을 평가하기 위해서는 테스트 전용 데이터가 필요한데 이번 예제에서는 5일 동안의 미래를 테스트 데이터로 사용하자.
- ARIMA 모델 활용하기 : 실제 데이터와의 비교
forecast_data = model_fit.forecast(steps=5)
test_file_path = '/home/jaeyoon89/python-data-analysis/data/market-price-test.csv'
bitcoin_test_df = pd.read_csv(test_file_path, names=['ds', 'y'])
pred_y = forecast_data[0].tolist()
test_y = bitcoin_test_df.y.values
pred_y_lower = []
pred_y_upper = []
for lower_upper in forecast_data[2]:
lower = lower_upper[0]
upper = lower_upper[1]
pred_y_lower.append(lower)
pred_y_upper.append(upper)
그리고 다음 코드는 이를 그래프로 시각화한 것이다. 파란색은 모델이 예상한 최고 가격, 빨간색은 모델이 예측한 하한가 그래프이고, 초록색은 실제 5일 간의 가격 그래프, 노란색은 모델이 예측한 5일간의 가격 그래프를 나타낸 것이다.
plt.plot(pred_y, color="gold")
plt.plot(pred_y_lower, color="red")
plt.plot(pred_y_upper, color="blue")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa1dc736a90>]
아래는 상한가와 하한가를 제외한 뒤 그래프를 살펴보았다. 그래프의 상승 경향을 살펴보면 그다지 좋지 않은 예측을 한 것으로 보인다. 하지만 5일동안 상승할 것이다 라는 아주 큰 트렌드는 예측할 수 있었다.
plt.plot(pred_y, color="gold")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa1e41034a8>]
from sklearn.metrics import mean_squared_error, r2_score
from math import sqrt
rmse = sqrt(mean_squared_error(pred_y, test_y))
print(rmse)
270.85258806066304
이번엔 ARIMA보다 조금 더 정확한 트렌드 예측 분석을 제공하는 라이브러리 'FacebookProphet'을 사용하자. fbprophet 라이브러리를 사용하기 위해선 데이터 프레임의 피처를 'ds'와 'y'로 변경해야 한다. 그리고 Prophet이라는 클래스를 선언한 뒤, fit() 함수로 모델을 학습시킨다. 모델 생성에 포함되는 파라미터의 의미는 다음과 같다.
- seasonality_mode : 연간,월간,주간,일간 등의 트렌드성을 반영하는 것을 의미하는 파라미터이다.
- changepoint_prior_scale : 트렌드가 변경되는 문맥을 반영하는 파라미터이다. 수치가 높을수록 모델은 과적합에 가까워진다.
- Facebook Prophet 활용하기
from fbprophet import Prophet
bitcoin_df = pd.read_csv(file_path, names=['ds', 'y'])
prophet = Prophet(seasonality_mode='multiplicative',
yearly_seasonality=True,
weekly_seasonality=True, daily_seasonality=True,
changepoint_prior_scale=0.5)
prophet.fit(bitcoin_df)
<fbprophet.forecaster.Prophet at 0x7fa1dc9821d0>
다음 코드 중 make_future_dataframe(periods-5,freq='d'), prophet.predict(future_data) 를 실행하면 학습 데이터셋 기반의 5일 단위 예측 데이터를 얻을 수 있다.
future_data = prophet.make_future_dataframe(periods=5, freq='d')
forecast_data = prophet.predict(future_data)
forecast_data.tail(5)
ds | trend | yhat_lower | yhat_upper | trend_lower | trend_upper | daily | daily_lower | daily_upper | multiplicative_terms | ... | weekly | weekly_lower | weekly_upper | yearly | yearly_lower | yearly_upper | additive_terms | additive_terms_lower | additive_terms_upper | yhat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
365 | 2018-08-27 | 595.073916 | 6244.684071 | 7541.386994 | 595.073916 | 595.073916 | 10.907431 | 10.907431 | 10.907431 | 10.544862 | ... | -0.007796 | -0.007796 | -0.007796 | -0.354773 | -0.354773 | -0.354773 | 0.0 | 0.0 | 0.0 | 6870.046369 |
366 | 2018-08-28 | 598.125014 | 6283.276467 | 7646.269844 | 598.125014 | 598.125014 | 10.907431 | 10.907431 | 10.907431 | 10.633309 | ... | 0.026363 | 0.026363 | 0.026363 | -0.300485 | -0.300485 | -0.300485 | 0.0 | 0.0 | 0.0 | 6958.173352 |
367 | 2018-08-29 | 601.176113 | 6245.853845 | 7709.353527 | 601.176113 | 601.176113 | 10.907431 | 10.907431 | 10.907431 | 10.588920 | ... | -0.055193 | -0.055193 | -0.055193 | -0.263318 | -0.263318 | -0.263318 | 0.0 | 0.0 | 0.0 | 6966.981989 |
368 | 2018-08-30 | 604.227211 | 6274.519297 | 7786.241714 | 595.135376 | 610.384660 | 10.907431 | 10.907431 | 10.907431 | 10.631542 | ... | -0.029402 | -0.029402 | -0.029402 | -0.246487 | -0.246487 | -0.246487 | 0.0 | 0.0 | 0.0 | 7028.094432 |
369 | 2018-08-31 | 607.278310 | 6245.421765 | 7895.588102 | 585.171677 | 621.710651 | 10.907431 | 10.907431 | 10.907431 | 10.694373 | ... | 0.039476 | 0.039476 | 0.039476 | -0.252535 | -0.252535 | -0.252535 | 0.0 | 0.0 | 0.0 | 7101.738804 |
5 rows × 22 columns
forecast_data[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(5)
ds | yhat | yhat_lower | yhat_upper | |
---|---|---|---|---|
365 | 2018-08-27 | 6870.046369 | 6244.684071 | 7541.386994 |
366 | 2018-08-28 | 6958.173352 | 6283.276467 | 7646.269844 |
367 | 2018-08-29 | 6966.981989 | 6245.853845 | 7709.353527 |
368 | 2018-08-30 | 7028.094432 | 6274.519297 | 7786.241714 |
369 | 2018-08-31 | 7101.738804 | 6245.421765 | 7895.588102 |
다음은 fbprophet 모델의 학습 결과를 시각화한 결과이다. 그래프의 검은점은 실제 가격을 파란 선은 예측 가격을 나타낸 것이다.
fig1 = prophet.plot(forecast_data)
그리고 다음의 그래프는 fbprophet에서 제공하는 트렌드 정보 시각화 그래프이다. 앞서 seasonality_mode 파라미터를 설정해놓은 경우에만 이 시각화가 가능하다.
fig2 = prophet.plot_components(forecast_data)
/home/jaeyoon89/.local/lib/python3.6/site-packages/fbprophet/plot.py:422: UserWarning: FixedFormatter should only be used together with FixedLocator ax.set_yticklabels(yticklabels) /home/jaeyoon89/.local/lib/python3.6/site-packages/fbprophet/plot.py:422: UserWarning: FixedFormatter should only be used together with FixedLocator ax.set_yticklabels(yticklabels) /home/jaeyoon89/.local/lib/python3.6/site-packages/fbprophet/plot.py:422: UserWarning: FixedFormatter should only be used together with FixedLocator ax.set_yticklabels(yticklabels)
ARIMA 모델을 평가한 것과 동일한 방법으로 테스트 데이터셋을 평가해보자. 아래 코드 실행 결과 ARIMA 모델보다는 prophet 모델이 실제 price값에 더 근접한 예측을 하는 것처럼 보인다.
- Facebook Prophet 활용하기 : 실제 데이터와의 비교
bitcoin_test_df = pd.read_csv(test_file_path, names=['ds','y'])
pred_y = forecast_data.yhat.values[-5:]
test_y = bitcoin_test_df.y.values
pred_y_lower = forecast_data.yhat_lower.values[-5:]
pred_y_upper = forecast_data.yhat_upper.values[-5:]
plt.plot(pred_y, color="gold")
plt.plot(pred_y_lower, color="red")
plt.plot(pred_y_upper, color="blue")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa1dc847080>]
다음으로 이 모델의 Test RMSE를 ARIMA 모델과 비교해 보자.
plt.plot(pred_y, color="gold")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa1dc6f4f28>]
rmse = sqrt(mean_squared_error(pred_y, test_y))
print(rmse)
105.41853842279897
step.3 활용: 더 나은 결과를 위한 방법¶
이번 분석 단계에서는 모델의 성능을 조금 더 향상시킬 수 있는 방법들에 대해 알아보자. 첫 번째로 고려해볼 방법은 상한값 혹은 하한값을 지정해 주는 것이다. 바닥과 천장이 없는 주가 데이터의 경우에는 의미가 없을 수 있지만 일반적인 시계열 데이터에서는 상한값 혹은 하한값을 설정해 주는 것이 모델의 성능을 높여줄 수 있는 방법 중 하나이다.
- 상한가 및 하한가 설정하기
bitcoin_df = pd.read_csv(file_path, names=['ds','y'])
bitcoin_df['cap'] = 20000
prophet = Prophet(seasonality_mode='multiplicative',
growth='logistic',
yearly_seasonality=True,
weekly_seasonality=True, daily_seasonality=True,
changepoint_prior_scale=0.5)
prophet.fit(bitcoin_df)
<fbprophet.forecaster.Prophet at 0x7fa2193723c8>
future_data = prophet.make_future_dataframe(periods=5, freq='d')
future_data['cap'] = 20000
forecast_data = prophet.predict(future_data)
fig = prophet.plot(forecast_data)
이번엔 아래의 코드로 예측값과 실제값을 비교해보자.
- 예측과 실제 비교 그래프
bitcoin_test_df = pd.read_csv(test_file_path, names=['ds','y'])
pred_y = forecast_data.yhat.values[-5:]
test_y = bitcoin_test_df.y.values
pred_y_lower = forecast_data.yhat_lower.values[-5:]
pred_y_upper = forecast_data.yhat_upper.values[-5:]
plt.plot(pred_y, color="gold")
plt.plot(pred_y_lower, color="red")
plt.plot(pred_y_upper, color="blue")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa21920f470>]
위와 같이 형편없는 결과를 예측했다는 것을 알 수 있다. 상한가라는 개념이 큰 의미가 없는 비트코인 데이터의 경우에는 상한선을 설정한 것이 오히려 독이 되었다고 볼 수 있다.
이제 모델의 성능을 향상시키는 다른 방법 중 하나인 이상치 제거 기법을 살펴보자. 이상치란 평균적인 수치에 비해 지나치게 높거나 낮은 수치의 데이터를 의미한다. fbprophet 모델이 이상치를 제거한 데이터로 학습하려면 이상치에 해당하는 데이터를 None으로 설정해 주면 된다. 아래 코드에선 18000 이상을 이상치라 설정하였다.
- 이상치 제거하기
bitcoin_df = pd.read_csv(file_path, names=['ds', 'y'])
bitcoin_df.loc[bitcoin_df['y'] > 18000, 'y'] = None
prophet = Prophet(seasonality_mode='multiplicative',
yearly_seasonality=True,
weekly_seasonality=True, daily_seasonality=True,
changepoint_prior_scale=0.5)
prophet.fit(bitcoin_df)
future_data = prophet.make_future_dataframe(periods=5, freq='d')
forecast_data = prophet.predict(future_data)
fig = prophet.plot(forecast_data)
마찬가지 방법으로 예측값과 실제값을 그래프로 나타내보자.
bitcoin_test_df = pd.read_csv(test_file_path, names = ['ds', 'y'])
pred_y = forecast_data.yhat.values[-5:]
test_y = bitcoin_test_df.y.values
pred_y_lower = forecast_data.yhat_lower.values[-5:]
pred_y_upper = forecast_data.yhat_upper.values[-5:]
plt.plot(pred_y, color="gold")
plt.plot(pred_y_lower, color="red")
plt.plot(pred_y_upper, color="blue")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa1dcad5748>]
plt.plot(pred_y, color="gold")
plt.plot(test_y, color="green")
[<matplotlib.lines.Line2D at 0x7fa1dcc2d198>]
rmse = sqrt(mean_squared_error(pred_y, test_y))
print(rmse)
171.83159828333595
이를 살펴본 결과, 이상치를 제거하는 것이 정확도(RMSE) 면에서는 떨어지지만 '트렌드'를 예측하는 측면에서는 이전 모델보다 낫다고 할 수 있다.
출처 : 이것이 데이터 분석이다.
'이것이 데이터분석이다 with 파이썬' 카테고리의 다른 글
이것이 데이터 분석이다 with 파이썬 ch4-1(타이타닉 생존자 가려내기) (0) | 2021.04.16 |
---|---|
이것이 데이터 분석이다 with 파이썬 ch3-3(미래에 볼 영화의 평점 예측하기) (0) | 2021.04.15 |
이것이 데이터 분석이다 with 파이썬 ch3-1(프로야구 선수의 다음 해 연봉 예측하기) (0) | 2021.04.12 |
이것이 데이터 분석이다 with 파이썬 ch1-2(국가별 음주 데이터 분석하기) (0) | 2021.04.11 |
이것이 데이터 분석이다 with 파이썬 ch1-1(chipotle 주문 데이터 분석하기) (0) | 2021.04.09 |