🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

VAR 다변량 시계열 분석 완벽 가이드 - 슬라이드 1/9
A

AI Generated

2025. 12. 3. · 14 Views

VAR 다변량 시계열 분석 완벽 가이드

여러 변수가 서로 영향을 주고받는 시계열 데이터를 분석하는 VAR 모델을 배웁니다. 경제 지표 예측부터 수요 예측까지, 실무에서 바로 활용할 수 있는 다변량 시계열 분석 기법을 초급자도 이해할 수 있도록 설명합니다.


목차

  1. VAR_모델의_기본_개념
  2. 정상성_검정과_차분
  3. 최적_시차_선택
  4. Granger_인과관계_검정
  5. 충격반응함수_IRF
  6. 분산분해_FEVD
  7. VAR_모델로_예측하기
  8. 모델_진단과_검증

1. VAR 모델의 기본 개념

어느 날 김개발 씨는 마케팅팀으로부터 급한 요청을 받았습니다. "광고비를 늘리면 매출이 얼마나 오를까요?

그리고 매출이 오르면 다시 광고비에 어떤 영향을 미칠까요?" 김개발 씨는 고민에 빠졌습니다. 두 변수가 서로 영향을 주고받는다니, 단순한 시계열 분석으로는 해결할 수 없어 보였습니다.

**VAR(Vector AutoRegression)**은 여러 개의 시계열 변수가 서로 영향을 주고받는 관계를 동시에 분석하는 모델입니다. 마치 가족 구성원들이 서로의 기분에 영향을 주듯이, 경제 변수들도 서로 연결되어 있습니다.

VAR 모델을 사용하면 이런 복잡한 상호작용을 하나의 시스템으로 파악할 수 있습니다.

다음 코드를 살펴봅시다.

import numpy as np
import pandas as pd
from statsmodels.tsa.api import VAR

# 샘플 데이터 생성: 광고비와 매출
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=100, freq='D')
ad_spend = np.cumsum(np.random.randn(100)) + 100
sales = 0.5 * ad_spend + np.cumsum(np.random.randn(100)) + 200

# 데이터프레임 생성
data = pd.DataFrame({'ad_spend': ad_spend, 'sales': sales}, index=dates)

# VAR 모델 생성 및 학습
model = VAR(data)
fitted_model = model.fit(maxlags=5, ic='aic')
print(fitted_model.summary())

김개발 씨는 입사 6개월 차 데이터 분석가입니다. 지금까지는 단일 변수의 시계열 분석만 다뤄왔는데, 이번에는 여러 변수가 얽혀 있는 복잡한 문제를 만났습니다.

선배 분석가 박시니어 씨가 다가와 상황을 듣더니 고개를 끄덕였습니다. "아, 이런 경우에는 VAR 모델을 써야 해요.

변수들이 서로 영향을 주고받을 때 쓰는 방법이에요." 그렇다면 VAR 모델이란 정확히 무엇일까요? 쉽게 비유하자면, VAR 모델은 마치 가족 회의와 같습니다.

아버지의 기분이 어머니에게 영향을 주고, 어머니의 기분이 자녀에게, 다시 자녀의 기분이 아버지에게 영향을 줍니다. 모든 구성원이 서로 연결되어 있는 것입니다.

VAR 모델도 마찬가지로 여러 변수가 서로 어떻게 영향을 주고받는지를 수학적으로 표현합니다. VAR 모델이 없던 시절에는 어땠을까요?

분석가들은 각 변수를 따로따로 분석해야 했습니다. 광고비 따로, 매출 따로 분석하다 보니 둘 사이의 피드백 효과를 놓치기 일쑤였습니다.

더 큰 문제는 예측의 정확도가 떨어진다는 것이었습니다. 변수 간의 상호작용을 무시했기 때문입니다.

바로 이런 문제를 해결하기 위해 VAR 모델이 등장했습니다. VAR을 사용하면 여러 변수를 하나의 시스템으로 볼 수 있습니다.

또한 Granger 인과관계를 검정하여 어떤 변수가 다른 변수에 실질적인 영향을 미치는지 파악할 수 있습니다. 무엇보다 미래 예측의 정확도가 크게 향상됩니다.

위의 코드를 살펴보겠습니다. 먼저 statsmodels 라이브러리에서 VAR 클래스를 임포트합니다.

그 다음 광고비와 매출 데이터를 담은 데이터프레임을 생성합니다. model.fit() 메서드에서 maxlags 파라미터는 과거 몇 개의 시점까지 고려할지를, **ic='aic'**는 최적의 시차를 자동으로 선택하게 합니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 이커머스 회사에서 광고비, 방문자 수, 매출, 재고량을 동시에 분석한다고 가정해봅시다.

VAR 모델을 활용하면 광고비 증가가 방문자 수에, 방문자 수 증가가 매출에, 매출이 다시 재고 관리에 미치는 영향을 종합적으로 파악할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 분석가들이 흔히 하는 실수 중 하나는 정상성 검정을 생략하는 것입니다. VAR 모델은 데이터가 정상성을 만족해야 제대로 작동합니다.

비정상 시계열을 그대로 넣으면 가짜 회귀 문제가 발생할 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 눈이 반짝였습니다. "아, 그래서 변수들을 함께 분석해야 했군요!" VAR 모델을 제대로 이해하면 복잡한 비즈니스 문제도 데이터로 풀어낼 수 있습니다.

실전 팁

💡 - VAR 모델 적용 전 반드시 ADF 검정으로 정상성을 확인하세요

  • 변수 개수가 너무 많으면 과적합 위험이 있으니 3-5개 이내로 유지하세요

2. 정상성 검정과 차분

김개발 씨가 VAR 모델을 돌려보았는데, 결과가 이상합니다. 예측값이 점점 발산하더니 결국 말도 안 되는 숫자가 나왔습니다.

"왜 이러지?" 코드를 아무리 봐도 문제가 없어 보였습니다. 박시니어 씨가 데이터를 보더니 바로 문제점을 짚어냈습니다.

"데이터가 정상성을 만족하지 않네요."

**정상성(Stationarity)**이란 시계열 데이터의 통계적 특성이 시간에 따라 변하지 않는 성질입니다. 마치 호수의 수면처럼 출렁거리더라도 평균 수위는 일정하게 유지되는 것과 같습니다.

VAR 모델은 정상 시계열에서만 신뢰할 수 있는 결과를 내므로, 분석 전 반드시 정상성을 확인해야 합니다.

다음 코드를 살펴봅시다.

from statsmodels.tsa.stattools import adfuller
import pandas as pd

def check_stationarity(series, name):
    # ADF 검정 수행
    result = adfuller(series, autolag='AIC')
    print(f'{name}: ADF Statistic = {result[0]:.4f}, p-value = {result[1]:.4f}')
    if result[1] <= 0.05:
        print(f'  -> 정상 시계열입니다 (귀무가설 기각)')
    else:
        print(f'  -> 비정상 시계열입니다 (차분 필요)')

# 원본 데이터 검정
check_stationarity(data['sales'], 'Sales')

# 1차 차분 후 검정
data_diff = data.diff().dropna()
check_stationarity(data_diff['sales'], 'Sales (1차 차분)')

김개발 씨는 당황했습니다. 분명히 코드는 잘 작성했는데, 왜 결과가 이상하게 나오는 걸까요?

박시니어 씨가 차분히 설명해주었습니다. "VAR 모델을 포함한 대부분의 시계열 모델은 정상성이라는 조건을 만족해야 해요.

데이터가 이 조건을 만족하지 않으면 결과를 믿을 수 없어요." 그렇다면 정상성이란 무엇일까요? 쉽게 비유하자면, 정상 시계열은 마치 잔잔한 호수와 같습니다.

바람이 불면 잔물결이 일지만, 평균 수위는 항상 일정합니다. 반면 비정상 시계열은 댐에서 물이 빠지는 강과 같습니다.

시간이 지날수록 수위가 계속 낮아지죠. 이런 데이터로 미래를 예측하면 완전히 빗나간 결과가 나옵니다.

정상성을 무시하면 어떤 문제가 생길까요? 가장 흔한 문제는 **가짜 회귀(Spurious Regression)**입니다.

아무 관련 없는 두 변수가 마치 강한 상관관계가 있는 것처럼 보이는 현상입니다. 예를 들어 해적 수와 지구 온난화가 상관관계를 보일 수 있습니다.

물론 실제로는 아무 관련이 없죠. 이 문제를 해결하기 위해 **ADF 검정(Augmented Dickey-Fuller Test)**을 사용합니다.

ADF 검정의 귀무가설은 "데이터가 비정상 시계열이다"입니다. p-value가 0.05보다 작으면 귀무가설을 기각하고, 데이터가 정상 시계열이라고 판단합니다.

반대로 p-value가 크면 차분이 필요합니다. 코드를 살펴보겠습니다.

adfuller 함수는 ADF 검정 결과를 반환합니다. 반환값의 첫 번째 요소는 검정 통계량, 두 번째 요소는 p-value입니다.

p-value가 0.05보다 크면 diff() 메서드로 1차 차분을 수행합니다. 차분이란 현재 값에서 이전 값을 빼는 연산입니다.

실무에서는 어떻게 적용할까요? 주가 데이터를 분석한다고 가정해봅시다.

주가 자체는 대부분 비정상 시계열입니다. 계속 오르거나 내리는 추세가 있기 때문입니다.

하지만 **수익률(로그 차분)**은 정상 시계열에 가깝습니다. 그래서 금융 분석에서는 주가 대신 수익률을 사용하는 경우가 많습니다.

주의할 점이 있습니다. 차분을 너무 많이 하면 데이터의 정보가 손실됩니다.

보통 1차 차분이면 충분하고, 2차 차분 이상이 필요한 경우는 드뭅니다. 또한 차분 후에는 첫 번째 행이 NaN이 되므로 **dropna()**로 제거해야 합니다.

김개발 씨는 데이터를 차분한 후 다시 VAR 모델을 돌려보았습니다. 이번에는 결과가 합리적으로 나왔습니다.

실전 팁

💡 - ADF 검정의 p-value가 0.05보다 크면 차분을 적용하세요

  • 차분 후에는 반드시 dropna()로 결측값을 제거하세요

3. 최적 시차 선택

김개발 씨는 새로운 고민에 빠졌습니다. VAR 모델을 만들 때 과거 몇 개의 시점까지 고려해야 할까요?

1일 전? 7일 전?

30일 전? 시차를 너무 적게 잡으면 정보를 놓칠 것 같고, 너무 많이 잡으면 과적합이 될 것 같았습니다.

**시차(Lag)**는 VAR 모델에서 과거 몇 개의 시점을 고려할지 결정하는 핵심 파라미터입니다. 마치 날씨 예측에서 어제만 볼지, 지난 일주일을 볼지 결정하는 것과 같습니다.

적절한 시차 선택은 모델의 성능을 크게 좌우하며, AIC, BIC 같은 정보 기준을 활용하여 최적의 시차를 찾을 수 있습니다.

다음 코드를 살펴봅시다.

from statsmodels.tsa.api import VAR

# 차분된 데이터로 VAR 모델 생성
model = VAR(data_diff)

# 최적 시차 자동 선택
lag_order_results = model.select_order(maxlags=15)
print(lag_order_results.summary())

# AIC 기준 최적 시차
optimal_lag_aic = lag_order_results.aic
# BIC 기준 최적 시차
optimal_lag_bic = lag_order_results.bic

print(f'\nAIC 기준 최적 시차: {optimal_lag_aic}')
print(f'BIC 기준 최적 시차: {optimal_lag_bic}')

# 최적 시차로 모델 학습
fitted = model.fit(optimal_lag_aic)

김개발 씨는 VAR 모델의 기본 원리는 이해했지만, 막상 적용하려니 막막했습니다. "시차를 몇으로 해야 하죠?" 박시니어 씨가 답했습니다.

"좋은 질문이에요. 시차 선택은 VAR 모델에서 가장 중요한 결정 중 하나예요." 시차란 무엇일까요?

쉽게 비유하자면, 시차는 마치 요리사의 레시피 기억과 같습니다. 어떤 요리사는 어제 만든 음식만 기억하고, 어떤 요리사는 지난 일주일간의 요리를 모두 기억합니다.

더 많이 기억할수록 패턴을 파악하기 쉽지만, 너무 오래된 정보는 오히려 방해가 될 수 있습니다. 시차가 너무 적으면 어떻게 될까요?

중요한 패턴을 놓칠 수 있습니다. 예를 들어 주간 패턴이 있는 데이터에서 시차를 3일로 설정하면, 7일 주기의 패턴을 전혀 잡아내지 못합니다.

이를 **과소적합(Underfitting)**이라고 합니다. 반대로 시차가 너무 많으면 어떻게 될까요?

모델이 학습 데이터에만 지나치게 맞춰지는 **과적합(Overfitting)**이 발생합니다. 학습 데이터에서는 완벽해 보이지만, 새로운 데이터에서는 형편없는 예측을 하게 됩니다.

이런 문제를 해결하기 위해 **정보 기준(Information Criteria)**을 사용합니다. **AIC(Akaike Information Criterion)**와 **BIC(Bayesian Information Criterion)**가 대표적입니다.

둘 다 모델의 적합도와 복잡도를 동시에 고려합니다. 값이 작을수록 좋은 모델입니다.

일반적으로 BIC가 더 단순한 모델을 선호하고, AIC는 조금 더 복잡한 모델을 허용합니다. 코드를 살펴보겠습니다.

select_order() 메서드는 다양한 시차에 대해 정보 기준을 계산합니다. maxlags=15는 최대 15개 시차까지 검토하라는 의미입니다.

결과로 각 시차별 AIC, BIC, FPE, HQIC 값을 확인할 수 있습니다. 실무에서는 어떤 기준을 선택해야 할까요?

예측이 주 목적이라면 AIC를, 모델 해석이 중요하다면 BIC를 권장합니다. BIC는 더 단순한 모델을 선택하므로 해석하기 쉽습니다.

확신이 없다면 두 기준이 동시에 추천하는 시차를 선택하는 것도 좋은 방법입니다. 김개발 씨는 select_order 함수를 실행해보았습니다.

AIC와 BIC 모두 시차 3을 추천했습니다. 확신을 가지고 진행할 수 있게 되었습니다.

실전 팁

💡 - 예측 목적이면 AIC, 해석 목적이면 BIC를 우선 고려하세요

  • maxlags는 데이터 길이의 10-20% 이내로 설정하는 것이 안전합니다

4. Granger 인과관계 검정

마케팅팀에서 또 질문이 왔습니다. "광고비가 매출에 정말 영향을 미치나요?

아니면 매출이 좋아서 광고비를 늘린 건가요?" 김개발 씨는 VAR 모델을 돌렸지만, 이 질문에 어떻게 답해야 할지 몰랐습니다. 단순한 상관관계가 아니라 인과관계를 증명해야 했습니다.

Granger 인과관계는 한 변수의 과거 값이 다른 변수의 미래 값을 예측하는 데 도움이 되는지 검정하는 방법입니다. 마치 먹구름이 비의 원인인지 확인하는 것처럼, 시간적 선후관계를 바탕으로 인과성을 추론합니다.

단, 이것은 진정한 철학적 인과관계가 아니라 '예측 인과관계'임을 명심해야 합니다.

다음 코드를 살펴봅시다.

from statsmodels.tsa.stattools import grangercausalitytests

# Granger 인과관계 검정
# 귀무가설: X가 Y를 Granger-cause 하지 않는다
print("광고비 -> 매출 Granger 인과관계 검정:")
gc_result_1 = grangercausalitytests(
    data_diff[['sales', 'ad_spend']],
    maxlag=5,
    verbose=True
)

print("\n매출 -> 광고비 Granger 인과관계 검정:")
gc_result_2 = grangercausalitytests(
    data_diff[['ad_spend', 'sales']],
    maxlag=5,
    verbose=True
)

김개발 씨는 고민에 빠졌습니다. 광고비와 매출 사이에 상관관계가 있다는 건 알겠는데, 누가 원인이고 누가 결과일까요?

박시니어 씨가 힌트를 주었습니다. "Granger 인과관계 검정을 해보세요.

완벽한 인과관계는 아니지만, 예측에 도움이 되는지는 알 수 있어요." Granger 인과관계란 무엇일까요? 쉽게 비유하자면, 마치 일기예보와 같습니다.

먹구름이 끼면 비가 올 확률이 높습니다. 먹구름의 과거 정보가 비의 미래를 예측하는 데 도움이 되죠.

이때 "먹구름이 비를 Granger-cause 한다"고 말합니다. 물론 먹구름이 비의 진정한 원인인지는 별개의 문제입니다.

Granger 인과관계의 핵심 아이디어는 이렇습니다. X가 Y를 Granger-cause 한다는 것은, Y를 예측할 때 X의 과거 값을 포함하면 예측력이 향상된다는 의미입니다.

귀무가설은 "X의 과거 값이 Y 예측에 도움이 되지 않는다"입니다. p-value가 0.05보다 작으면 귀무가설을 기각하고, X가 Y를 Granger-cause 한다고 결론 내립니다.

중요한 점은 양방향으로 검정해야 한다는 것입니다. 광고비가 매출을 Granger-cause 하는지, 매출이 광고비를 Granger-cause 하는지 모두 확인해야 합니다.

둘 다 유의하면 피드백 관계가 존재하는 것입니다. 이런 경우 VAR 모델이 특히 적합합니다.

코드를 살펴보겠습니다. grangercausalitytests 함수의 첫 번째 인자는 2열 데이터프레임입니다.

중요한 점은 첫 번째 열이 결과 변수, 두 번째 열이 원인 변수라는 것입니다. 즉 ['sales', 'ad_spend']는 "광고비가 매출을 Granger-cause 하는가?"를 검정합니다.

실무에서 주의할 점이 있습니다. Granger 인과관계는 예측 인과관계일 뿐, 진정한 인과관계가 아닙니다.

제3의 변수가 둘 다에 영향을 미칠 수 있습니다. 예를 들어 계절 요인이 광고비와 매출 모두에 영향을 줄 수 있습니다.

따라서 "광고비가 매출의 원인이다"라고 단정짓기보다, "광고비 정보가 매출 예측에 유용하다"고 해석하는 것이 정확합니다. 김개발 씨는 검정 결과를 마케팅팀에 보고했습니다.

"광고비가 매출을 Granger-cause 하지만, 매출도 광고비를 Granger-cause 합니다. 서로 영향을 주고받는 관계입니다."

실전 팁

💡 - 열 순서가 중요합니다. 첫 번째 열이 결과, 두 번째 열이 원인입니다

  • Granger 인과관계를 진정한 인과관계로 오해하지 마세요

5. 충격반응함수 IRF

경영진 회의에서 질문이 나왔습니다. "광고비를 갑자기 10% 늘리면 매출에 어떤 영향이 있을까요?

효과가 바로 나타나나요, 아니면 시간이 걸리나요?" 김개발 씨는 단순한 예측을 넘어, 충격의 동적 효과를 분석해야 했습니다.

**충격반응함수(Impulse Response Function, IRF)**는 한 변수에 충격이 가해졌을 때 다른 변수들이 시간에 따라 어떻게 반응하는지 보여줍니다. 마치 연못에 돌을 던졌을 때 파문이 퍼져나가는 모습과 같습니다.

IRF를 통해 충격의 크기, 지속 기간, 방향을 파악할 수 있습니다.

다음 코드를 살펴봅시다.

import matplotlib.pyplot as plt

# VAR 모델 학습 (이미 fitted 객체가 있다고 가정)
fitted = model.fit(3)

# 충격반응함수 계산 (10기간 동안)
irf = fitted.irf(10)

# 시각화
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
irf.plot(orth=False, subplot_params={'axes': axes})
plt.tight_layout()
plt.savefig('irf_plot.png')
plt.close()

# 특정 반응 출력
print("광고비 충격에 대한 매출 반응:")
print(irf.irfs[:, 1, 0])  # sales에 대한 ad_spend 충격 반응

김개발 씨는 새로운 도전에 직면했습니다. 단순히 "광고비가 매출에 영향을 준다"는 것을 넘어, 얼마나, 얼마 동안 영향을 주는지 알아야 했습니다.

박시니어 씨가 조언했습니다. "충격반응함수를 분석해보세요.

VAR 모델의 꽃이라고 할 수 있어요." 충격반응함수란 무엇일까요? 쉽게 비유하자면, 마치 연못에 돌을 던지는 것과 같습니다.

돌이 물에 닿는 순간 물보라가 튀고, 파문이 동심원을 그리며 퍼져나갑니다. 처음에는 파문이 크지만, 시간이 지나면 점점 작아지다가 결국 잔잔해집니다.

충격반응함수는 이 파문의 크기와 지속 시간을 수치로 보여줍니다. 왜 충격반응함수가 중요할까요?

정책 결정에 직접적인 도움을 주기 때문입니다. 예를 들어 광고비를 10% 늘렸을 때 매출이 즉시 5% 오르고 2주 후에 최대 15%까지 오른 뒤, 4주 후에 원래 수준으로 돌아온다면, 이 정보는 광고 전략 수립에 결정적입니다.

코드를 살펴보겠습니다. **fitted.irf(10)**은 10기간 동안의 충격반응을 계산합니다.

orth=False는 직교화되지 않은 충격반응을 의미하며, orth=True로 설정하면 직교화된 충격반응을 얻을 수 있습니다. 직교화는 변수 간 동시적 상관관계를 고려한 분석입니다.

irfs 배열의 구조는 **[기간, 반응변수, 충격변수]**입니다. 예를 들어 **irf.irfs[:, 1, 0]**은 "변수 0(ad_spend)의 충격에 대한 변수 1(sales)의 반응"을 의미합니다.

양수면 같은 방향으로, 음수면 반대 방향으로 반응한다는 뜻입니다. 실무에서 IRF는 어떻게 활용될까요?

중앙은행에서 금리를 인상했을 때 GDP와 물가가 어떻게 반응하는지 분석하는 데 널리 사용됩니다. 마케팅에서는 프로모션 효과가 얼마나 지속되는지, 재고 관리에서는 수요 충격이 공급망에 어떤 영향을 미치는지 분석할 수 있습니다.

주의할 점도 있습니다. 충격의 순서가 결과에 영향을 줄 수 있습니다.

특히 직교화된 IRF를 사용할 때 Cholesky 분해의 변수 순서가 중요합니다. 경제 이론에 따라 순서를 신중하게 결정해야 합니다.

김개발 씨는 IRF 그래프를 경영진에게 보여주었습니다. "광고비 충격 후 매출이 2주 후에 최대치에 도달하고, 6주 후에 효과가 사라집니다."

실전 팁

💡 - 신뢰구간을 함께 표시하여 결과의 불확실성을 보여주세요

  • 변수 순서가 직교화된 IRF 결과에 영향을 미치므로 신중하게 결정하세요

6. 분산분해 FEVD

경영진이 추가 질문을 던졌습니다. "매출 변동의 원인이 뭐죠?

광고 때문인가요, 아니면 다른 요인이 더 큰가요?" 김개발 씨는 매출 변동 중 얼마나 많은 부분이 광고비에 의해 설명되는지 정량화해야 했습니다.

**분산분해(Forecast Error Variance Decomposition, FEVD)**는 한 변수의 예측 오차 분산이 각 변수의 충격에 의해 얼마나 설명되는지 분석합니다. 마치 파이 차트처럼 전체 변동성을 각 요인별로 나누어 보여줍니다.

이를 통해 어떤 변수가 시스템에서 가장 중요한 역할을 하는지 파악할 수 있습니다.

다음 코드를 살펴봅시다.

# 분산분해 계산 (10기간)
fevd = fitted.fevd(10)

# 분산분해 요약 출력
print(fevd.summary())

# 시각화
fevd.plot(figsize=(10, 8))
plt.tight_layout()
plt.savefig('fevd_plot.png')
plt.close()

# 특정 기간의 분산분해 확인
print("\n10기간 후 매출 분산분해:")
print(f"  광고비로 인한 비중: {fevd.decomp[9, 1, 0]:.2%}")
print(f"  매출 자체로 인한 비중: {fevd.decomp[9, 1, 1]:.2%}")

김개발 씨는 IRF로 충격의 동적 효과는 파악했지만, 경영진은 더 직관적인 숫자를 원했습니다. "결국 매출 변동의 몇 퍼센트가 광고 때문인가요?" 박시니어 씨가 답했습니다.

"분산분해를 해보세요. 비율로 딱 떨어지게 나와요." 분산분해란 무엇일까요?

쉽게 비유하자면, 마치 가계부 분석과 같습니다. 이번 달 지출 변동의 원인을 분석할 때, 식비 때문에 30%, 교통비 때문에 20%, 문화생활 때문에 50% 변동했다고 말할 수 있습니다.

분산분해도 마찬가지로 변수의 변동을 각 원인별로 쪼개어 보여줍니다. 분산분해가 IRF와 다른 점은 무엇일까요?

IRF는 충격의 방향과 크기를 보여주는 반면, FEVD는 상대적 중요도를 보여줍니다. IRF에서 광고비 충격이 매출에 긍정적 영향을 준다는 것을 알았다면, FEVD에서는 그 영향이 전체 변동의 몇 퍼센트를 차지하는지 알 수 있습니다.

코드를 살펴보겠습니다. **fitted.fevd(10)**은 10기간 동안의 분산분해를 계산합니다.

decomp 배열의 구조는 **[기간, 반응변수, 충격변수]**입니다. 값은 0과 1 사이의 비율이며, 각 행의 합은 항상 1(100%)입니다.

분산분해 결과를 어떻게 해석해야 할까요? 예를 들어 "10기간 후 매출 분산의 40%가 광고비 충격으로 설명된다"는 결과가 나왔다면, 광고비가 매출에 상당한 영향을 미친다고 해석할 수 있습니다.

반대로 "매출 분산의 90%가 매출 자체 충격으로 설명된다"면, 외부 요인보다 내생적 요인이 더 중요하다는 의미입니다. 실무에서 FEVD는 예산 배분에 활용됩니다.

마케팅 예산을 어디에 집중해야 할지 결정할 때, FEVD 결과를 참고할 수 있습니다. 광고비보다 제품 품질이 매출 변동에 더 큰 영향을 미친다면, R&D에 더 투자하는 것이 합리적일 수 있습니다.

주의할 점이 있습니다. IRF와 마찬가지로 변수 순서가 결과에 영향을 미칩니다.

또한 분산분해는 예측 오차의 분산을 분해하는 것이지, 변수 수준의 분산을 분해하는 것이 아닙니다. 이 차이를 명확히 이해해야 합니다.

김개발 씨는 FEVD 결과를 정리했습니다. "매출 변동의 35%가 광고비로 설명됩니다.

나머지 65%는 매출 자체의 관성이나 기타 요인입니다."

실전 팁

💡 - 단기와 장기의 분산분해 결과가 다를 수 있으니 여러 기간을 확인하세요

  • 변수 순서에 따라 결과가 달라지므로 민감도 분석을 수행하세요

7. VAR 모델로 예측하기

드디어 가장 중요한 질문이 왔습니다. "다음 달 매출은 얼마나 될까요?" 김개발 씨는 지금까지 배운 모든 것을 종합하여 실제 예측을 수행해야 했습니다.

VAR 모델의 진정한 가치는 바로 이 예측에 있었습니다.

VAR 모델의 **예측(Forecasting)**은 학습된 계수를 바탕으로 미래 값을 추정하는 과정입니다. 마치 날씨 예보처럼 과거 패턴을 바탕으로 미래를 예측합니다.

VAR의 장점은 여러 변수를 동시에 예측하며, 변수 간 상호작용까지 고려한다는 것입니다.

다음 코드를 살펴봅시다.

# 예측 수행 (10기간 앞)
forecast_steps = 10
forecast = fitted.forecast(data_diff.values[-fitted.k_ar:], steps=forecast_steps)

# 예측 결과를 데이터프레임으로 변환
forecast_dates = pd.date_range(
    start=data_diff.index[-1] + pd.Timedelta(days=1),
    periods=forecast_steps,
    freq='D'
)
forecast_df = pd.DataFrame(
    forecast,
    index=forecast_dates,
    columns=data_diff.columns
)

# 차분을 원래 스케일로 복원
last_values = data.iloc[-1]
forecast_original = forecast_df.cumsum() + last_values.values

print("원래 스케일 예측값:")
print(forecast_original)

김개발 씨는 드디어 최종 단계에 도달했습니다. 모델을 만들고 분석한 것은 모두 이 순간을 위한 것이었습니다.

박시니어 씨가 말했습니다. "이제 예측을 해봅시다.

하지만 몇 가지 주의할 점이 있어요." VAR 예측의 기본 원리는 무엇일까요? 쉽게 비유하자면, 마치 바둑 기사의 수 읽기와 같습니다.

현재 상태에서 다음 수를 두면 상대가 어떻게 반응할지, 그러면 내가 또 어떻게 둘지를 연쇄적으로 계산합니다. VAR 모델도 현재 값을 바탕으로 다음 시점을 예측하고, 그 예측값을 바탕으로 그 다음 시점을 예측합니다.

예측 코드에서 중요한 부분을 살펴보겠습니다. forecast 메서드의 첫 번째 인자는 초기값입니다.

모델의 시차가 3이라면 최근 3개 시점의 데이터가 필요합니다. **data_diff.values[-fitted.k_ar:]**는 마지막 k_ar개 행을 가져옵니다.

steps는 몇 기간 앞을 예측할지 지정합니다. 차분 데이터로 모델을 학습했다면 역변환이 필요합니다.

차분 데이터의 예측값은 변화량입니다. 실제 값을 얻으려면 **cumsum()**으로 누적합을 구하고, 마지막 실제 값을 더해야 합니다.

이 과정을 빠뜨리면 예측값이 0 근처에서 움직이는 이상한 결과가 나옵니다. 예측의 불확실성도 고려해야 합니다.

점 예측만으로는 부족합니다. 예측 구간을 함께 제시해야 의사결정에 도움이 됩니다.

statsmodels의 forecast_interval 메서드를 사용하면 신뢰구간을 얻을 수 있습니다. 실무에서 예측을 활용할 때 주의점이 있습니다.

예측 기간이 길어질수록 불확실성이 커집니다. 일반적으로 단기 예측이 더 정확합니다.

또한 과거 패턴이 미래에도 유지된다는 가정 하에 예측하므로, 구조적 변화가 있으면 예측이 빗나갈 수 있습니다. 모델 검증도 중요합니다.

전체 데이터 중 일부를 테스트 세트로 남겨두고, 모델의 예측 성능을 평가해야 합니다. RMSE, MAE 같은 지표로 예측 오차를 측정하고, 다른 모델과 비교해보세요.

김개발 씨는 예측 결과를 그래프로 만들어 보고서에 포함했습니다. "다음 10일간의 예측입니다.

신뢰구간도 함께 표시했습니다."

실전 팁

💡 - 차분 데이터 예측 후 반드시 원래 스케일로 복원하세요

  • 예측 기간은 학습 데이터의 10-20%를 넘지 않는 것이 좋습니다

8. 모델 진단과 검증

보고서를 제출하기 직전, 박시니어 씨가 물었습니다. "모델 진단은 했어요?" 김개발 씨는 멈칫했습니다.

모델이 잘 작동하는지 어떻게 확인할 수 있을까요? 예측만 했다고 끝이 아니었습니다.

모델의 신뢰성을 검증해야 했습니다.

**모델 진단(Model Diagnostics)**은 VAR 모델이 데이터를 잘 설명하는지 확인하는 과정입니다. 마치 자동차 정기 검사처럼, 모델이 제대로 작동하는지 여러 항목을 점검합니다.

잔차의 정상성, 자기상관, 정규성 등을 검정하여 모델의 신뢰성을 확보합니다.

다음 코드를 살펴봅시다.

from statsmodels.stats.stattools import durbin_watson
from statsmodels.stats.diagnostic import acorr_ljungbox

# 잔차 추출
residuals = fitted.resid

# Durbin-Watson 검정 (자기상관)
print("Durbin-Watson 통계량 (2에 가까울수록 좋음):")
for col in residuals.columns:
    dw = durbin_watson(residuals[col])
    print(f"  {col}: {dw:.4f}")

# Ljung-Box 검정 (잔차 자기상관)
print("\nLjung-Box 검정 (p-value > 0.05면 자기상관 없음):")
for col in residuals.columns:
    lb_result = acorr_ljungbox(residuals[col], lags=[10], return_df=True)
    print(f"  {col}: p-value = {lb_result['lb_pvalue'].values[0]:.4f}")

# 안정성 검정
print(f"\n모델 안정성: {fitted.is_stable()}")

김개발 씨는 자신만만하게 예측 결과를 준비했지만, 박시니어 씨의 질문에 당황했습니다. 모델이 정말 믿을 만한지 어떻게 알 수 있을까요?

박시니어 씨가 설명했습니다. "모델을 맹신하면 안 돼요.

반드시 진단을 해야 해요. 잔차가 이상하면 모델을 수정해야 합니다." 잔차란 무엇일까요?

쉽게 비유하자면, 잔차는 마치 시험 채점 후 틀린 문제와 같습니다. 모델이 예측한 값과 실제 값의 차이입니다.

좋은 모델의 잔차는 패턴이 없어야 합니다. 잔차에 패턴이 있다면, 모델이 데이터의 어떤 부분을 제대로 잡아내지 못했다는 의미입니다.

Durbin-Watson 검정은 잔차의 자기상관을 검정합니다. 통계량이 2에 가까우면 자기상관이 없고, 0에 가까우면 양의 자기상관, 4에 가까우면 음의 자기상관이 있습니다.

1.5에서 2.5 사이면 대체로 괜찮다고 볼 수 있습니다. Ljung-Box 검정은 여러 시차에 걸친 자기상관을 한 번에 검정합니다.

귀무가설은 "자기상관이 없다"입니다. p-value가 0.05보다 크면 귀무가설을 기각할 수 없으므로, 잔차에 유의한 자기상관이 없다고 판단합니다.

p-value가 작으면 모델에 문제가 있을 수 있습니다. 안정성 검정도 매우 중요합니다.

VAR 모델이 안정적이지 않으면 예측이 시간이 지남에 따라 폭발합니다. is_stable() 메서드가 False를 반환하면, 모델의 모든 고유값의 절댓값이 1보다 작아야 한다는 조건을 만족하지 못한 것입니다.

이 경우 차분을 다시 하거나 모델을 재검토해야 합니다. 진단 결과가 좋지 않으면 어떻게 해야 할까요?

먼저 시차를 조정해보세요. 시차가 너무 적으면 잔차에 자기상관이 남고, 너무 많으면 과적합됩니다.

다음으로 변수를 추가하거나 제거해보세요. 중요한 변수가 빠져 있거나, 불필요한 변수가 포함되어 있을 수 있습니다.

김개발 씨는 진단을 수행한 결과, 모든 검정을 통과했습니다. 이제 자신 있게 보고서를 제출할 수 있었습니다.

실전 팁

💡 - Durbin-Watson 통계량은 1.5~2.5 범위가 이상적입니다

  • is_stable()이 False면 차분이나 변수 선택을 재검토하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#VAR#TimeSeries#Statsmodels#Forecasting#Data Science

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.