공부 -> 개념 숙지 완료 -> 포스팅 순으로 블로그를 작성했어야 했는데, 공부와 포스팅을 동시에 하다 보니 중간중간 틀린 내용들과 부족한 내용들이 나오기도 했고, 개념들이 여기저기 흩어지는 현상이 발생했다. 스터디에 진행할 발표도 함께 준비할 겸, 깔끔하게 개념들을 정리하고 넘어가고 싶다. 그래서 시계열 데이터의 특성과 정상성에 대해 지금까지 공부한 내용들을 정리해보기로 했다.
시계열 데이터 (Time Series Data)
일정한 시간 간격으로 측정된 데이터, x축을 시간, y축을 값으로 설정하여 그래프로 나타낼 수 있는 모든 데이터
EX) 출생률, 주식 가격, GDP 등등..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import pandas as pd
import matplotlib.pyplot as plt
# Load the built-in AirPassenger dataset
df = pd.read_csv('https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
header=0,
index_col=0)
# Plot the AirPassenger dataset
df.plot()
# Set the x-axis label
plt.xlabel('Year')
# Set the y-axis label
plt.ylabel('Passenger')
# Set the title
plt.title('AirPassenger dataset')
# Show the plot
plt.show()
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import numpy as np
import math
import matplotlib.pyplot as plt
n=1000 #시뮬레이션 횟수
T=3 #연단위 만기
M = 720 #이산화 시간 구간의 개수
dt = T/M #연단위 시간 길이
r= 0.03 #은행 이자율
sigma1=0.2691 #니케이지수 변동성
S_0=1000 #액면가
S1=np.zeros((M+1,n))
S1[0]=S_0
for t in range(1,M+1):
S1[t]=S1[t-1]*np.exp((r-0.5*sigma1**2)*dt+sigma1*math.sqrt(dt)*np.random.standard_normal(n))
x=[i for i in range(0,M+1)]
y=[S1[t] for t in range(0,M+1)]
plt.plot(x,y) #시뮬레이션 그래프는 하나만 그려봤습니다.
|
cs |
시계열 데이터가 중요한 이유
과거의 시계열 데이터들을 토대로 미래를 예측할 수 있기 때문에 시계열 데이터는 중요하게 여겨진다. 시계열을 예측하는 모델로는 분해 모델, 지수 평활, ARIMA 모델 등이 있다. 시계열 데이터의 특징에 따라 적절한 모델을 선택할 수 있다.
시계열 패턴 4요소 - Trend(추세성), Cycle(주기성), Seasonality(계절성), Irregular or Noise (불규칙 요인, 오차)
추세 (Trend) - 데이터가 장기적으로 증가하거나 감소하는 흐름
주기 (Cycle) - 고정된 기간(Period)이 아닌 불규칙적인 기간 동안 감소하거나 증가하는 흐름
계절성 (Seasonality) - 1년 중, 특정 월들에만 데이터가 증가하거나 감소하는 흐름
오차 - 예측값과 실제값의 차이
주기와 계절성 차이
1. 발생하는 기간 : 계절성은 1년 안에서 규칙적으로, 주기는 불규칙하지만 2~10년 등 매우 다양
2. 영향 : 계절성의 변동폭보다, 주기의 변동폭이 훨씬 크다.
Trend는 장기적으로 증가하거나 감소하는 흐름을 의미하고, Cycle은 불규칙적이며 장기적인 기간동안 증가하고 감소하는 흐름을 의미하므로, 겹치는 부분이 존재한다.
따라서 일반적으로 시계열 데이터를 분해할 때, 추세-주기(일반적으로 Trend)성분, 계절성(Seasonality) 성분, 나머지(Remainder) 성분으로 분해한다.
일반적으로 Trend나 Seasonality가 존재하는 시계열 데이터를 비정상성 데이터, 그렇지 않은 데이터를 정상성 데이터라고 분류한다. 단, Cycle만 존재하는 시계열 데이터는 정상성 데이터로 분류한다. Cycle의 period는 불규칙하기 때문에 주기의 시작과 끝을 예측할 수 없기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
# load dataset
df = pd.read_csv('https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv',
header=0,
index_col=0)
df.index = pd.to_datetime(df.index)
# decompose the time series
result = seasonal_decompose(df, model='multiplicative')
# plot the decomposition
result.plot()
plt.show()
|
cs |
정상성 (Stationary) 시계열 데이터
평균이 유지되고 분산이 시간에 의존하지 않기 때문에 예측하는 것이 쉽다.
예시 : 백색소음 (평균이 0, 표준편차가 1인 랜덤생성함수)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import numpy as np
import matplotlib.pyplot as plt
# Set the number of data points
n = 100
# Generate the white noise data
data = np.random.normal(0, 1, n)
# Plot the data
plt.plot(data)
plt.title('White Noise Data')
plt.xlabel('Time')
plt.ylabel('Value')
plt.show()
|
cs |
비정상성 (Non-Stationary) 시계열 데이터
추세, 계절성이 있어 시간에 따라 평균과 분산이 달라진다.
비정상성 데이터의 예시는 예시1과 예시 2에서 보여주었으므로 생략한다.
정상성 - 비정상성 데이터 구분하는 법 (내 기준)
0부터 T까지의 데이터와 T+1의 데이터가 상관이 있으면 비정상성 데이터, 그렇지 않으면 정상성 데이터이다.
이는 자기상관함수와 연관된 부분인데, 이렇게 말로 이해하는 것이 나는 더 편했다.
비정상성 데이터를 정상성 데이터로 변환하는 방법
1. 차분 - 추세, 계절성을 제거하고 싶을 때 사용하는 방법.
2. 로그 - 데이터의 분산이 일정하지 않은 경우, 분산을 일정하게 만들기 위해 사용.
추세와 계절성도 없애고 분산도 일정하게 만들고 싶으면 로그를 씌우고 차분을 진행.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import pandas as pd
import numpy as np
#create sample data
date_rng = pd.date_range(start='1/1/2020', end='1/10/2020', freq='D')
data = {'date': date_rng, 'data': np.random.normal(100,10,size=(len(date_rng)))}
df = pd.DataFrame(data)
#apply log transformation
df['log_data'] = np.log(df['data'])
# plot
df.plot(x ='date', y=['data','log_data'], kind = 'line')
plt.show()
|
cs |
비정상성 데이터를 정상성 데이터로 변환하는 이유
0부터 T까지의 데이터가 T+1의 데이터와 상관이 없으면 정상성 데이터, 그렇지 않으면 비정상성 데이터라고 위에서 쉽게 구분했다. 이렇게 되면 알고 있는 데이터로 추정하기 쉬운 비정상성 데이터가 예측 모델로 더 좋다고 생각하기 쉽다.
하지만, T+1부터 T+N까지 긴 범위를 예측한다고 생각하면 말이 다르다.
비정상성 데이터는 평균도, 분산도 일정하지 않기 때문에 긴 시간범위의 데이터를 예측하기는 아주 힘들다. 특히 추세가 있는 그래프는 주기가 일정하지 않기 때문에 더더욱 힘들 것이다.
그에 반해 정상성 데이터는 평균과 분산이 일정하다는 확신이 있기 때문에 긴 시간범위에 대해 예측하는 것이 훨씬 쉽다. 우리는 보통 예측할 때 바로 다음 상황만을 예측하진 않는다. 미래를 길게 보고 예측하기 때문에, 비정상성 데이터를 정상성 데이터로 변환하여 사용한다.
정리하고 넘어갈 점 - 왜 굳이 정상성?
이러한 이유 말고도 굳이 정상성 데이터로 변환하는 이유는, 우리가 사용할 모델이 ARIMA 모델이기 때문이다.
ARIMA 모델은 넓게 보면 미래를 예측하는 여러 방법론 중 하나이고, 머신 러닝에서 유용하게 사용되는 모델이기 때문에, 코딩을 하는 과정에서 평균과 분산이 일정하다는 점은 큰 이점으로 작용할 것이다. 또한 머신러닝이란 결국 데이터들을 바탕으로 평균과 분산을 수정해나가면서 일종의 회귀선을 만드는 과정을 의미하는데, 이때 효과적인 회귀선을 만드려면 좋은 데이터들을 집어넣는 것이 중요하다.
이렇듯 ARIMA 모델이 평균과 분산이 일정한 정상성 데이터에서 잘 작동하게끔 이루어져있기 때문에, 차분과 로그 등의 작업을 거쳐서 정상성 데이터로 억지로 변환시키는 것이다.
사실 현대에 와서는 ARIMA 모델의 성능이 좋지 않은 편이라고 한다. 아는 게 별로 없는 내가 알기로도, 미래를 예측하는 방법 중 Marcov Chain Monte Carlo(MCMC)라는 아주 좋은 방법론이 존재하는 것으로 알고 있다.
(MCMC 간략히 설명 : 엄청나게 많이 시뮬레이션을 돌린 후, 가능한 시나리오들을 모두 정리하여 미래를 예측하는 기법)
하고 싶은 말은, "계절성 뚜렷하면 예측하기 쉬운 것 아닌가?" "정상성 데이터 평균 기준으로 위아래로 랜덤하게 변하는데 예측하기 더 어려워 보이는데?" 라는 의문은 불필요하다는 것이다. 사람이 직관적으로 보거나, 앞서 언급한 MCMC 외에도 Prophet, 로지스틱 선형회귀 같은 방법론을 사용하면 아주 쉽게 예측 가능할 것이다. 단, 우리는 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
# Load the stock price data for Apple Inc.
df = pd.read_csv('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=AAPL&apikey=R1A10JZ8TS3877SK&datatype=csv&start_date=2010-01-01')
# Convert the 'Date' column to a datetime index
df.index = pd.to_datetime(df['timestamp'])
# Plot the 'Close' column
plt.plot(df['close'])
plt.xlabel('Date')
plt.ylabel('Closing price (USD)')
plt.title('Apple Inc. Stock Price')
plt.show()
# Calculate the logged data
df['log'] = np.log(df['close'])
# Plot the logged data
plt.plot(df['log'])
plt.xlabel('Date')
plt.ylabel('Logged data')
plt.title('Apple Inc. Stock Price Logged Data')
plt.show()
# Calculate the differenced data of logged data
df['logdiff'] = df['log'].diff()
# Plot the differenced data
plt.plot(df['logdiff'])
plt.xlabel('Date')
plt.ylabel('Differenced data of Logged')
plt.title('Apple Inc. Stock Price Differenced Logged Data')
plt.show()
# Calculate the differenced data
df['diff'] = df['close'].diff()
# Plot the differenced data
plt.plot(df['diff'])
plt.xlabel('Date')
plt.ylabel('Differenced data')
plt.title('Apple Inc. Stock Price Differenced Data')
plt.show()
|
cs |
확인해보면 알겠지만 차분을 씌우면 평균이 0처럼 보이고, 로그를 씌우면 그래프의 y축 범위가 아주 작아져서 분산이 작아진다.
다음에는 자기상관함수까지 포스팅을 끝내고 정상성을 검정하는 방법 3가지에 대해서 총정리를 할 예정이다.
참고자료
https://otexts.com/fppkr/index.html
https://assaeunji.github.io/statistics/2021-08-08-stationarity/
https://aliencoder.tistory.com/3
https://yamalab.tistory.com/112
'시계열 데이터' 카테고리의 다른 글
시계열 데이터 - AR,MA,ARIMA 모델 (2) | 2023.01.21 |
---|---|
시계열 데이터 - 자기상관함수 ACF와 PACF (2) | 2023.01.15 |
시계열 데이터 - 정상성 분석하기, ADF test (0) | 2023.01.02 |
시계열 데이터 - 시계열 분해와 차분 (0) | 2022.12.30 |
시계열 데이터 - 시작 전 (0) | 2022.12.28 |