시계열 데이터(Time-Series Data)는 시간의 순서(Sequence)에 따라 관찰되는 데이터로, 주가, 기상, 판매 등 다양한 분야에서 사용되고있다. 이러한 데이터를 분석하는 것이 시계열 분석이고, 분석을 통해 데이터에서 숨겨진 패턴을 찾고, 미래의 값을 예측할 수 있다. 지금부터 시계열 분석을 위한 여러가지 가정과 그것을 검증하는 방법에 대해 알아보자.
정상성(Stationary)
시계열의 정상성은 평균, 분산, 공분산 등이 변하지 않는 특성을 의미한다. 즉, 정상성은 데이터 내의 패턴이 일관되게 유지된다는 것을 의미한다.
- 평균(average) : 시계열 데이터의 전체 합을 개별 관측치의 수로 나눈 값
- 분산(Variance) : 데이터의 편차(개별 관측치와 평균의 차이)의 제곱의 평균이다. 데이터의 분포를 나타낸다.
- 공분산(covariance) : 두 시계열 간의 관계를 나타내며, 한 시계열의 값이 증가할 때 다른 시계열의 값이 얼마나 변하는지를 나타낸다. 시계열 내에서는 시차(Time Lag)가 있는 두 시점의 관계를 보여준다.
- 시차(Time Lag) : 특정 시점의 데이터와 다른 시점의 데이터 간의 시간 간격 (예시 : 오늘의 주가와 어제의 주가)
정상성 검증을 위해 대표적으로 ADF-Test(Augmented Dickey-Fuller Test)를 사용해볼 수 있다. ADF Test는 시계열에 단위근이 있는지 검사하며, 단위근이 있다면 시계열이 비정상(non-stationary)하다고 판단한다.
- 단위근(Unit Root) : 단위근은 특정 시점에서의 관측치가 다음 시점의 값이 1:1로 영향을 주는 것을 의미한다. 단위근이 있는 시계열은 특정 추세 또는 패턴을 따르지 않으며 무작위 보행(random walk)의 형태를 가질 수 있다.
- 무작위 보행(Random Walk) : 연속적인 관측치 간의 관계가 무작위로 결정되는 시계열 모델을 의미한다. 각 관측치는 이전 관측치와 일정한 평균 또는 변화량이 아닌 오차항을 더한 값으로 구성된다. 무작위 보행의 미래 값은 현재 및 과거에 의해 예측할 수 없기 때문에, 분석이 복잡하고 예측이 어려워질 수 있다.
python에서 statsmodels library를 사용해 ADF Test를 수행할 수 있다. 아래에는 간단하게 예시를 만들어보았다.
from statsmodels.tsa.stattools import adfuller
import numpy as np
import matplotlib.pyplot as plt
# sample 생성(무작위)
np.random.seed(42)
data = np.random.randn(100).cumsum()
# 데이터 그래프
plt.plot(data)
plt.title('Random Walk (Non-stationary)')
plt.show()
# ADF Test
result = adfuller(data)
# 결과 출력
print(f'ADF Statistic: {result[0]}')
print(f'p-value: {result[1]}')
print(f'Used Lag: {result[2]}')
print(f'Number of Observations Used: {result[3]}')
print(f'Critical Values:')
for key, value in result[4].items():
print(f' {key}: {value}')
print(f'Regression Type (Used for the test): c (constant only, default)')
ADF Test의 귀무 가설(null hypothesis)은 단위근이 존재한다는 것이고, 대립 가설(alternative hypothesis)은 단위근이 존재하지 않음을 의미한다. 위의 그림은 표준 정규분포에서 100개의 누적 합계를 무작위로 뽑아낸 데이터를 보여주고 있다. 해당 데이터는 Random Walk한 형태를 보이고 있기 때문에, 단위근이 존재한다고 볼 수 있다. 또한 ADF-Test 결과를 해석해봐도 동일한 결과를 확인할 수 있는데, 아래에는 해석 방법에 대해 적어보았다.

- ADF Statistic : Test 통계량으로, 이 값이 특정 임계값보다 작으면 시계열이 Stationary 하다고 판단한다. 여기서 임계값이란 특정 유의 수준(significance level)에서의 임계값을 의미한다. 유의 수준이란 통계적 검정에서 귀무 가설을 잘못 기각할 위험을 수용할 수 있는 최대 확률로, 보통 0.05로 설정된다. 즉, 정해진 유의 수준에서 ADF Statistic이 해당 유의 수준의 임계값보다 작으면 귀무 가설을 기각한다. 여기서 주의해야 할 점은, 유의 수준 자체가 임계값이 아니라는 점이다.
- p-value : 귀무 가설 하에 현재 데이터에서 관측된 통계량이 나타날 확률이다. p-value가 유의 수준보다 작으면 귀무 가설을 기각한다.
- Used Lag : 사용된 지연 시차(lags)의 수를 의미한다. 지연 시차란, 특정 시점의 값과 그 이전 시점의 값 사이의 시간 차이를 의미한다.
- Number of Observations Used : 사용된 관측치의 수
- Critical Values : 다양한 유의 수준에서의 임계값으로, dict 형태로 return
- Regression Type : 테스트에 사용된 회귀의 결과로, default는 c로 사용한다. 시계열의 특성에 따라 적절한 유형을 선택할 수 있다.
- c : 상수만 포함
- ct : 상수와 선형 추세 포함
- ctt : 상수와 선형추세, 2차 추세 포함
- nc : 상수나 추세 없음
정상성은 시계열 분석에서 중요한 가정 중 하나로, 모델의 성능과 예측의 정확성이 큰 영향을 미친다. 비정상 시계열의 경우, 분석을 위해 정상성으로 변환해야 하는 경우가 많다. 이제부터는 정상성으로 변환하는 대표적인 방법들에 대해 알아보자.
차분(Difference)

- 차분은 연속된 관측치 간의 차이를 계산하는 과정으로, 비정상 시계열을 정상 시계열로 변환하는 데 사용할 수 있음
- 차분을 통해 평균이 시간에 따라 변하는 비정상 시계열을 정상화하며, 계절성을 제거하는 데도 사용할 수 있음
- 2차 차분, 3차 차분 등도 이어지며, k차 차분은 k번 연속으로 차분을 취한 것을 의미
- 시계열에서 2차 차분 이상의 차분을 하는 경우는 거의 없음
import numpy as np
import matplotlib.pyplot as plt
# 원본 데이터
data = np.arange(1, 101) * 2 + np.random.randn(100) * 10
# 1차 차분
differenced_data = np.diff(data, n=1)
plt.figure(figsize=(15,5))
plt.subplot(1, 3, 1)
plt.plot(data)
plt.title('Original Data')
plt.subplot(1, 3, 2)
plt.plot(differenced_data)
plt.title('Differenced Data')
plt.show()

차분을 하기 전과 후를 비교해보면, 명확한 추세(Trend)를 가지고 있던 데이터가 차분 이후 평균이 일정하게 유지되고 있음을 확인할 수 있다. 차분은 추세를 제거하고, 시계열의 정상성을 확보하는 데 중요한 역할을 수행한다. 이렇게 정상성을 확보한 후에는, 분산의 안정화나 다른 특성을 조정하기 위해 로그 변환과 같은 추가적인 변환이 이용될 수 있다. 다음으로는 로그 변환에 대해 알아보자.
로그 변환(Log Transformation)
- 로그 변환은 개별 관측치에 대해 자연 로그를 취하는 변환으로, 관측치 간의 상대적인 관계를 유지하면서 데이터의 scale을 변경
- 시간에 따라 변화하는 분산을 줄일 수 있으며, 이상치(outlier)의 영향을 줄여 모델의 성능을 제고할 수 있음
- 지수적 trend를 보이는 데이터를 선형화하는데 효과적이며, 이를 통해 분석을 보다 간단하고 효율적으로 수행할 수 있음
import numpy as np
import matplotlib.pyplot as plt
# 원본 데이터 생성 (분산이 시간에 따라 증가)
np.random.seed(42)
time = np.linspace(0, 10, 100)
data = np.random.randn(100) * (0.5 * time + 1) + 5
# 로그 변환
log_transformed_data = np.log(data)
plt.figure(figsize=(15,5))
# 원본 데이터 그래프
plt.subplot(1, 2, 1)
plt.plot(data)
plt.title('Original Data (Increasing Variance)')
plt.ylim(-8, 12)
# 로그 변환된 데이터 그래프
plt.subplot(1, 2, 2)
plt.plot(log_transformed_data)
plt.title('Log Transformed Data (Stabilized Variance)')
plt.ylim(-8, 12)
plt.show()

로그 변환 이전과 이후를 비교해보면, 분산이 시간에 따라 증가하는 패턴을 보이던 이전과 달리 분산이 시간에 따라 덜 변하는 모습을 확인할 수 있다. 이처럼 차분과 로그 변환은 비정상 시계열을 정상화하는데 사용되며, 두 가지 기법을 함께 사용할 수도 있다. 지금까지 정상성에 대해 알아보았으니, 다른 특성들에 대해서도 알아보자.
정규성(Normality)
정규성은 관측된 데이터가 정규 분포를 따르고 있는지를 확인하는 특성이다. 많은 통계적 방법들은 데이터가 정규 분포를 따른다는 가정하에 작동하므로, 해당 가정이 만족되지 않으면 모델의 예측값이 왜곡될 수 있다. 또한, 시계열 분석에서는 오차항의 정규성을 가정하는 경우가 많기 때문에 이를 확인하는 과정이 중요하다. 정규성을 예측하기 위한 방법으로 Box-Cox 변환과 QQ-Plot을 사용할 수 있는데, 간단한 예제와 함께 알아보자.
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
# 비정규분포 데이터 생성
data_non_normal = np.random.exponential(scale=2, size=1000)
# Box-Cox 변환 적용(data, lambda)
transformed_data, _ = stats.boxcox(data_non_normal)
# QQ-Plot으로 원본 데이터와 변환된 데이터 비교
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
stats.probplot(data_non_normal, dist="norm", plot=plt)
plt.title('QQ-Plot of Non-Normal Data')
plt.subplot(1, 2, 2)
stats.probplot(transformed_data, dist="norm", plot=plt)
plt.title('QQ-Plot of Transformed Data')
plt.show()

- QQ-Plot : QQ-Plot은 데이터가 정규 분포를 따르는지 여부를 확인할 수 있는 방법이다. 관측 데이터의 분포와 정규 분포의 분포를 직선상에서 비교하며, 관측치가 직선에 가깝게 배열되어 있을수록 데이터가 정규분포에 가깝다고 해석할 수 있다.
- Box-Cox 변환 : 왼쪽의 그림처럼 데이터가 정규 분포를 따르지 않을 경우, Box-Cox 변환을 사용하여 데이터를 정규 분포에 가깝게 변환할 수 있다. 오른쪽의 그림을 보면 변환된 데이터가 정규 분포에 가깝게 변환된 것을 확인할 수 있다.
자기상관(AutoCorrelation)
자기상관은 시계열 데이터에서 이전 관측치와 이후 관측치 간의 상관 관계를 나타낸다. 시차(lag)가 0일 때 자기상관은 1이며, 시차가 증가하면 자기상관은 감소한다. 자기상관을 측정하기 위해 ACF와 PACF 함수를 사용할 수 있다. 두 가지 함수의 개념을 정리해보고, 예제를 통해 알아보자.
- ACF(Autocorrelation Function) : ACF는 특정 시차에서의 자기상관을 계산한다. 시차 k에서 자기상관은 시간 t의 값과 시간 t-k의 값 사이의 상관 관계를 나타낸다. ACF는 모든 시차에 대한 자기상관을 그래프로 나타내어, 시계열 데이터의 전반적인 패턴 및 계절성을 파악하는데 사용할 수 있다.
- PACF(Partial Autocorrelation Function) : PACF는 특정 시차의 자기상관을 계산할 때, 그보다 작은 시차들의 영향을 제거한 상관 관계를 나타낸다. 예를 들어, 시차 3의 PACF는 시차 1,2의 영향을 제거한 후 계산된다. 특히, AR(Autoregressive) 모델의 차수를 결정하는 데 유용하게 사용할 수 있다.
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as plt
import numpy as np
# 임의의 AR(1) 시계열 데이터 생성
np.random.seed(42)
data = [np.random.randn()]
for i in range(1, 1000):
data.append(0.6 * data[-1] + np.random.randn())
data = np.array(data)
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
# ACF 그리기
plot_acf(data, lags=40, ax=axes[0])
axes[0].set_title('Autocorrelation Function (ACF)')
# PACF 그리기
plot_pacf(data, lags=40, ax=axes[1])
axes[1].set_title('Partial Autocorrelation Function (PACF)')
plt.tight_layout()
plt.show()

AR(1) 시계열 데이터에 대한 ACF와 PACF를 표시하였다. AR(1)이란 자기회귀(Autoregressive) 모델의 첫 번째 차수를 나타내며, 현재 시점의 값이 바로 이전 시점의 값에 의존한다는 것을 의미한다. AR(자기회귀), I(차분), MA(이동 평균)으로 구성되는 모델을 ARIMA 모델이라고 하는데, 차후 프로젝트에서 모형을 사용하면서 간단하게 설명하도록 하겠다. 위 코드에서는 이전 시점의 값에 0.6을 곱한 후 정규분포에서 생성된 임의의 잡음(noise)을 더해서 시계열 데이터를 생성하였다.
ACF 그래프를 보면 y축에 자기상관계수, x축에 시차가 표시되고 있다. 시차 0에서의 자기상관계수는 1이며, 시차가 증가함에 따라 자기상관계수는 점차 감소한다. AR(1) 모델에서는 자기상관이 점차적으로 감소하지만, 시차가 길어지더라도 약간의 상관 관계가 지속될 수 있다. PACF 그래프는 각 시차에서의 부분 자기상관을 보여주고 있다. AR(1) 데이터에서는 시차 1에서의 부분 자기상관계수가 상대적으로 크며, 그 이후의 시차에서는 거의 0에 가까워진다. 이는 현재 값이 바로 이전 값에만 의존하고 더 이전의 값에는 의존하지 않는 AR(1)의 특성을 보여주고 있다. 두 가지 그래프는 ARIMA, SARIMA와 같은 시계열 모델을 적합시키기기 전에 데이터의 자기상관 패턴을 파악하는데 사용할 수 있으며, 그래프의 형태를 기반으로 모델(AR, MA, ARMA 등)의 차수를 결정하여 최적의 모형을 도출할 수 있다.
등분산성(Homoscedasticity)
등분산성은 데이터의 분산이 시간이나 예측변수의 값에 따라 일정하게 유지되는 것을 의미한다. 반대의 개념으로, 이분산성(Heteroscedasticity)은 분산이 시간이다 예측변수의 값에 따라 변화하는 경우를 나타낸다. 시계열 분석에서는 잔차의 등분산성 가정이 위반되면, 표준 오차가 왜곡되어 신뢰 구간과 가설 감정이 부정확해질 수 있다. 또한, 잔차의 변동성이 시간에 따라 달라지는 경우 모델은 데이터의 중요한 패턴을 놓치고 있을 수도 있기 때문에 일관적 예측을 위해서 사용된 데이터의 등분산성을 만족해야 한다. 분산성의 유무를 검정하는 데는 여러가지 통계적 방법이 있으며, 예제로는 Bartlett Test와 Levene Test를 사용해보았다.
- Bartlett Test : 두 개 이상의 표본에서 등분산성을 검정하는 데 사용된다. 정규성을 가정하기 때문에, 데이터가 정규분포를 따를 떄 가장 효과적이다. 각 집단의 분단을 비교하고, 전체 분산과 집단 내 분산 간의 차이를 검정 통계량으로 사용한다. 즉, 정규분포를 따르는 집단에 대해 분산의 동일성을 검정하는 방식이다.
- 귀무가설 : 모든 그룹의 분산이 동일하다. (등분산성 가정)
- 대립가설 : 적어도 한 그룹의 분산이 다른 그룹과 다르다. (등분산성 가정 위반)
- Levene Test : Bartlett Test와 유사하지만, 정규성 가정을 하지않는다. 따라서, 정규분포를 따르지 않는 데이터에 더 적합하다. 각 집단의 평균 또는 중앙값과 관측치 간의 차이를 기반으로 분산을 계산하고, 이러한 분산값들이 동일한지 검증한다. 중앙값을 사용하는 경우, outlier에 주의해야 한다.
- 귀무가설 : 모든 그룹의 분산이 동일하다. (등분산성 가정)
- 대립가설 : 적어도 한 그룹의 분산이 다른 그룹과 다르다. (등분산성 가정 위반)
from scipy.stats import bartlett, levene
import numpy as np
# 임의의 등분산성을 가진 데이터 생성
np.random.seed(42)
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(0, 1, 1000)
# 임의의 이분산성을 가진 데이터 생성
data3 = np.concatenate([np.random.normal(0, 1, 500), np.random.normal(0, 3, 500)])
# Bartlett 검정
bartlett_stat, bartlett_p_value = bartlett(data1, data2)
print(f"Bartlett Test: statistic = {bartlett_stat}, p-value = {bartlett_p_value}")
# Levene 검정
levene_stat, levene_p_value = levene(data1, data3)
print(f"Levene Test: statistic = {levene_stat}, p-value = {levene_p_value}")
Bartlett Test: statistic = 0.34002826698353783, p-value = 0.5598129061342484
Levene Test: statistic = 0.16575791865118406, p-value = 0.6839533428776543
Bartlett Test의 p-value가 0.5598로, 일반적인 유의 수준 0.05보다 크기 때문에 귀무가설을 기각할 수 없다. 따라서 모든 그룹의 분산이 동일하다고 판단할 수 있다. Levene Test 역시 p-value가 0.6839로, 유의 수준 0.05보다 크기 때문에 귀무 가설을 기각할 수 없다. 따라서, 두 그룹의 분산이 동일하다고 판단할 수 있다. 두 검정 모두 등분산성 가정이 유지되었다는 결론을 내리므로, 시계열 분석에 해당 데이터를 사용하는데 등분산성을 가정하는 방법을 사용해도 문제가 없음을 확인할 수 있다.
독립성(Independence)
시계열 분석에서 잔차의 독립성이란, 시간에 따른 연속된 관측치 사이에 어떠한 상관 관계도 없음을 의미한다. 만약 잔차에 독립성이 없다면, 모델은 시계열 데이터의 중요한 정보나 패턴을 제대로 포착하지 못한 것으로 볼 수 있으며, 이는 예측의 정확성을 저하시킬 수 있다. Durbin-Watson Test로 연속된 잔차(Residual)간의 상관관계를 확인해보자.
import numpy as np
from statsmodels.stats.stattools import durbin_watson
# 임의의 잔차 데이터 생성
np.random.seed(42)
residuals = np.random.randn(100)
# Durbin-Watson 통계량 계산
dw_stat = durbin_watson(residuals)
print(f'Durbin-Watson statistic: {dw_stat}')
Durbin-Watson statistic: 2.0118176103841634
Durbin-Watson 통계량 값은 일반적으로 0과 4 사이에 있다. 해당 데이터의 통계량은 값이 2 근처에 있기 때문에 잔차가 독립적이라고 판단할 수 있으며, 이런 경우에는 문제가 없다고 판단한다.
- 2에 가까운 값 : 잔차간에 자기상관이 없음
- 0에 가까운 값 : 양의 자기상관이 있음
- 4에 가까운 값 : 음의 자기상관이 있음
위에서는 임의로 잔차 데이터를 생성하였지만, Durbin-Watson Test는 회귀 분석/시계열 모델의 잔차에 대해 수행되는 Test이므로, 실제로 사용할 때는 모델에 학습을 진행한 후의 잔차를 사용해야 한다. 만약 잔차가 독립적이지 않다면, 시계열 데이터의 중요한 패턴이나 구조가 누락되었을 가능성이 존재한다. 이러한 문제를 해결하기 위해 위에서 소개했던 차분, 로그 변환, Box-Cox 변환 등 다양한 방법을 시도해볼 수 있다.
시계열 분석은 시간 순서에 따른 데이터 패턴을 이해하고 예측하는 과정이다. 이번 포스팅에서는 시계열 분석에서의 주요 가정과 관련된 중요한 개념들에 대해 알아보았다.
- 정상성(Stationary): 시계열 데이터의 통계적 특성이 시간에 따라 일정한지 확인하는 과정으로, 모델의 안정성과 예측 성능을 높이는 핵심 요소이다.
- 자기상관(Autocorrelation): 이전 관측치와 이후 관측치 간의 상관 관계를 분석한다. 자기상관의 이해는 시계열 데이터의 내재된 패턴과 구조를 파악하는 데 중요한 역할을 수행한다.
- 정규성(Normality): 잔차의 정규 분포 여부를 평가한다. 정규성은 통계적 추론의 정확도를 높이는 데 필요할 수 있다.
- 등분산성(Homoscedasticity): 잔차의 등분산성은 모델의 일관적 예측과 잔차의 변동성을 평가하는 데 사용된다.
- 독립성(Independence): 잔차의 독립성은 모델이 데이터의 중요한 정보를 놓치지 않았는지 평가하는 데 사용된다.
시계열 분석은 위에서 소개한 요소들 뿐만 아니라 다양한 요소들이 복합적으로 작용하고 있으며, 데이터의 특성과 어떤 요소를 활용하냐에 따라 모델의 성능과 안정성에 직접적으로 영향을 미친다. 이러한 가정들을 충족시키기 위해 적절한 통계적 방법 및 변환을 적용하면, 우리가 만드는 모델의 예측 능력을 크게 향상시킬 수 있다. 오늘 내용을 정리하면서 시계열 분석의 의미 및 주요 요인에 대해서도 추가로 포스팅해야 겠다는 생각이 들어서, 차후 정리해서 소개하도록 하겠다.
'Data Science' 카테고리의 다른 글
| [NLP] Tokenizer (0) | 2023.09.20 |
|---|---|
| [KNIME] python 설정 (0) | 2023.09.18 |
| [KNIME] 설치방법 (0) | 2023.09.16 |
| [NLP] Transfer Learning (0) | 2023.09.13 |
| [시계열 분석] 시계열의 의미와 요인 (2) | 2023.08.23 |