자기상관 :: 시계열 분석 - mindscale
Skip to content

자기상관

자기 상관은 시계열 데이터의 시점 간의 상관 관계를 의미합니다. 예를 들어 시점 1, 2, 3, 4, 5, 6이 있을 때 시점 간의 관계를 구하면 이를 자기 상관이라고 합니다.

ACF

자기 상관을 정의하는 방법 중 하나는 ACF, 즉 Auto-correlation Function입니다. 이는 시점 간 상관 관계를 보기 쉽게 표현한 것입니다.

ACF를 구하는 방식은 다음과 같습니다. 우선 한 시점 간격의 상관 관계를 구하고, 두 시점, 세 시점, 네 시점 간격의 상관 관계를 차례로 구합니다. 이 과정을 반복해 각 간격별로 상관 관계를 파악하는 것이 목표입니다.

다음 그림에서 다양한 ACF 예시들을 확인할 수 있습니다.

여기서 시작점은 1부터입니다. 이 경우, 첫 번째와 두 번째, 그리고 두 번째와 세 번째 사이에는 상관 관계가 거의 없습니다. 동일한 시점 간격 또한 마찬가지입니다. 여기서 점선은 통계적으로 유의하다고 결론짓기 위해 필요한 상관계수를 나타냅니다.

만약 상관 계수가 이 선을 넘어간다면, 통계적으로 유의하다고 판단할 수 있습니다. 하지만 우리가 확인할 수 있듯이, 이 선을 넘어가는 상관계수는 거의 없습니다.

여기서 한 가지 예외가 있는데, 20번의 시도 중에는 한 번 정도 극단적인 값이 나올 수 있다는 점입니다. 즉, 이는 통계적으로 유의하긴 하나, 이런 경우는 일반적으로 한 번 정도는 발생할 수 있으므로, 크게 신경 쓰지 않아도 됩니다.

결국, 이 경우에는 별다른 상관 관계를 찾아볼 수 없습니다.

무작위로 움직이는 시계열 데이터

무작위로 움직이는 시계열 데이터에서 특정 시점의 데이터는 그 전의 데이터에 영향을 받지 않습니다. 따라서, 이전의 값이 다음 데이터의 변화에 아무런 영향을 주지 않습니다. 이런 상황에서 ACF(AutoCorrelation Function)를 통해 해당 시계열 데이터가 무작위적으로 움직이는지 확인할 수 있습니다.

양의 자기 상관

다음으로, 만약 양의 상관 관계가 있다면 이는 어떤 것을 의미할까요? 일반적으로 이를 그림으로 나타낸다면, 양의 값으로 넘어가면 연속적으로 양의 값을 가지게 됩니다. 어떠한 이유로 음의 영역으로 돌아가면, 그 이후로 계속 음의 값을 유지하게 됩니다. 따라서 이전 시점이 음이었다면 다음 시점 역시 음이 됩니다.

이를 자기 상관을 통해 계산할 경우, 양이었으면 그 후에도 양, 음이었으면 그 후에도 음이라는 결과를 얻게 되며, 양에서 음으로 넘어가는 경우도 있지만 드뭅니다. 그렇다고 해서 음의 값이 없는 것은 아니지만, 어느 특정 시점에서의 상관 관계는 높고, 이전보다 멀어질 때마다 즉, 두 시점, 세 시점, 네 시점이 될수록 상관 관계가 서로 달라져 상관이 점차 줄어들게 됩니다.

따라서 보통 양의 상관 관계가 있을 경우, 천천히 감소하는 ACF 형태를 보입니다.

음의 자기 상관

만약 음의 상관 관계가 있다면, 그것은 앞서 움직인 방향과 그 다음 움직일 방향이 서로 반대라는 의미 입니다. 예를 들어, 앞선 움직임이 양수면 그 다음에는 음수의 움직임을 보여주게 됩니다.

시점별로 보면, 한 시점 전에는 음의 상관 관계가 있어 막대기가 밑으로 내려갑니다. 그러나 두 시점 전은 서로 반대로 움직였으므로 원래의 상태로 복귀하게 됩니다. 따라서 두 시점 후에는 양수가 됩니다.

세 번째 시점에서는 막대기가 다시 아래로 내려갑니다. 이러한 패턴이 반복되어, 마치 주기적으로 상승하고 하락하는 형태가 되는 것입니다. 이런 상향과 하향의 길이는 점점 줄어들게 됩니다.

우리는 이러한 정보를 이용해 자기상관함수(ACF)를 그려 상관 관계의 유무 및 데이터가 무작위로 움직이는지 판별할 수 있습니다.

추세가 존재하는 데이터

데이터에 추세가 존재하면 데이터는 증가하는 경향을 보이게 됩니다. 만약 음의 상관 관계가 있을 경우, 원래라면 하락하는 다음 시점이 되어야 하지만, 증가하는 추세에 따라 상승하게 됩니다. 이상적으로는 이런 특징을 통해 여러 시간대에 걸쳐 데이터의 패턴을 비교할 수 있습니다. 큰 차이는 있을 수 있지만, 그 패턴은 전반적으로 매우 비슷하게 나타납니다.

양의 상관 관계와 추세가 있는 경우를 보면, 비슷해 보일 수 있지만 둘은 사실 상이한 개념입니다. 양의 상관 관계는 어떤 데이터가 빠르게 하강하는 경향을 보이는 경우를 말하며, 추세가 있는 경우는 하강 속도가 비교적 완만한 경우를 말합니다.

애매한 개념일 수 있으니, 반복적으로 관찰하다보면 이해하게 될 것입니다. 수치적으로 정확히 나타낼 수는 없지만, 특정 패턴을 파악하는 데 도움이 됩니다.

예를 들어, 양의 상관 관계인 경우에는 한 시점과 다음 시점의 관계가 종종 곱하기 관계입니다. 즉, 한 단계 앞의 값이 0.8이었다면, 두 단계 뒤는 0.8 * 0.8 = 0.64, 세 단계 뒤는 0.8 * 0.8 = 0.4 같은 방식으로, 지수 함수처럼 빠르게 감소하게 됩니다.

반면에 추세가 있는 경우에는 값 자체가 증가하는 경향을 보이므로, 변동이 더 완만하게 진행되며, 이는 비교적 직선에 가깝게 떨어지는 것으로 보일 수 있습니다.

눈으로 구별하는 것이 쉽지 않을 수 있으니, 이럴 때는 별도의 검정을 진행하여 판단해야 합니다. 이제 이 개념을 실제 데이터를 활용해 살펴보도록 하겠습니다.

날씨

우리가 분석할 대상은 'washington_temp.xlsx'라는 데이터 파일입니다. 이는 pandasread_excel 함수를 통해 불러올 수 있습니다.

이 데이터의 구조를 살펴보면, 맨 위의 항목은 'Region'인데, 이는 각 기록이 어느 지역에서 나온 것인지를 나타냅니다. 그 다음으로는 'Country'가 나와 있어, 어느 나라에서의 기록인지 알 수 있습니다. 이 경우에는 미국입니다.

그 후 'State' 항목에서는 주를, 'City'에서는 도시를 확인할 수 있습니다.

그 다음으로는 날짜를 나타내는 'Month', 'Day', 'Year' 항목이 있습니다. 이 데이터는 미국식 날짜 표기법이므로, 월, 일, 연도 순서로 구성되어 있습니다.

마지막으로 'AvgTemperature' 항목에서는 해당 날짜의 평균 기온을 확인할 수 있습니다.

import pandas as pd
temp = pd.read_excel('washington_temp.xlsx')
temp.head()
Unnamed: 0 Region Country State City Month Day Year AvgTemperature
0 1687055 North America US Maryland Washington DC 1 1 1995 40.6
1 1687056 North America US Maryland Washington DC 1 2 1995 39.8
2 1687057 North America US Maryland Washington DC 1 3 1995 29.3
3 1687058 North America US Maryland Washington DC 1 4 1995 33.0
4 1687059 North America US Maryland Washington DC 1 5 1995 20.9

이제 이 세 개를 날짜 표기에 넣어야합니다. datetime 함수를 사용하여 연, 월, 일 순으로 year, months, day 칼럼을 합쳐 날짜로 변환하세요.

그렇게 하면 'date'라는 새로운 칼럼이 생성됩니다.

다음으로, 기존에 0번, 1번, 2번 등으로 표기되어있는 인덱스 번호를 우리가 만든 'date' 칼럼으로 대체하세요.

그렇게하면, 'date' 칼럼값이 행의 이름으로 사용됩니다. 이 경우, 행 이름은 굵은 글씨로 표시됩니다.

temp['date'] = pd.to_datetime(temp[['Year', 'Month', 'Day']])
temp.set_index('date', inplace=True)
temp.head()
Unnamed: 0 Region Country State City Month Day Year AvgTemperature
date
1995-01-01 1687055 North America US Maryland Washington DC 1 1 1995 40.6
1995-01-02 1687056 North America US Maryland Washington DC 1 2 1995 39.8
1995-01-03 1687057 North America US Maryland Washington DC 1 3 1995 29.3
1995-01-04 1687058 North America US Maryland Washington DC 1 4 1995 33.0
1995-01-05 1687059 North America US Maryland Washington DC 1 5 1995 20.9

이제 평균 기온을 바탕으로 그림을 그려볼 것입니다. resample 함수를 사용해 'M' 옵션으로 설정하면, 월별로 통계를 구할 수 있습니다. 다음으로 일 평균 기온에 대해 다시 평균을 구하여 월 평균 기온을 계산하게 됩니다. 이 결과를 'monthly'라는 이름으로 정하고 그래프를 그리게 되면, monthly의 결과가 오락가락하는 것을 확인할 수 있습니다.

기온은 여름에는 높고 겨울에는 낮습니다. 이 그래프를 보면 12개월 전의 기온이 현재에 반복되는 것을 볼 수 있습니다. 예를 들어, 12개월 전에 춥다면 12개월 후에도 춥습니다. 이는 겨울이 돌아왔기 때문입니다. 또한, 12개월 전에 더웠다면 12개월 후에도 더우며, 이는 여름이 돌아왔기 때문입니다. 따라서 이런 계절적 특성 때문에, 12개월 간격으로 데이터 패턴이 유사한 현상이 발생합니다. 즉, 12개월 간격에 자기상관이 존재하게 됩니다.

monthly = temp.resample('M').AvgTemperature.mean()
monthly.plot()
<Axes: xlabel='date'>

acf 함수는 자기 상관을 계산해줍니다. 앞서 슬라이드에서의 ACF는 1부터 시작했지만, 여기서는 0부터 시작합니다.

0은 자기 자신과의 관계를 나타내므로 항상 1이 됩니다. 예를 들어, 99년 여름과 99년 여름의 상관계수는 당연히 1이 됩니다. 왜냐하면 그 값은 언제나 동일하기 때문입니다.

중요한 것은 이 다음인데, 한 달 전의 상관계수는 0.83이 됩니다. 즉, 한 달 전이 더웠다면 이번 달도 덥고, 한 달 전이 추웠다면 이번 달도 춥습니다. 이는 지난달의 기후가 여름이었을 경우 이번 달도 여름이거나 초기 가을이며, 여전히 더우리라는 의미입니다. 마찬가지로, 지난달이 겨울이었다면 이번 달도 겨울이거나 초기 봄이기 때문에 여전히 춥습니다. 따라서 한 달 단위의 상관계수는 0.83으로 굉장히 높게 나타납니다.

상관계수가 2달 후에는 0.47, 3달 후에는 0.06처럼 급격하게 떨어지는 것을 볼 수 있습니다. 이후에는 다시 마이너스로 상승하게 되는데, 6개월이 지나면 -0.92, 거의 -1에 가까운 자기상관을 가지게 됩니다. 이는 6개월 전의 날씨와 현재의 날씨가 반대되기 때문입니다. 예를 들어, 6개월 전이 더웠다면 현재는 추울 것이며, 반대로 6개월 전이 추웠다면 현재는 더울 것입니다. 이는 계절 변화에 따른 것입니다.

여기서 더 나아가, 12개월이 지나면 상관계수는 다시 양수가 되며, 이는 1년 전의 날씨와 현재의 날씨가 비슷하기 때문입니다. 즉, 1년 전이 추웠다면 현재도 추우며, 1년 전이 더웠다면 현재도 더울 것입니다.

from statsmodels.tsa.stattools import acf
acf(monthly)
array([ 1.        ,  0.83115217,  0.47467309,  0.00663062, -0.46502868,
       -0.80701026, -0.92863316, -0.79835449, -0.45824698,  0.00253566,
        0.46149188,  0.79540151,  0.91171097,  0.7838514 ,  0.44951811,
       -0.00177934, -0.44842724, -0.77479739, -0.88350088, -0.76231924,
       -0.43924376,  0.00369917,  0.43941603,  0.76217074,  0.87282319])

ACF 함수를 이용해서 플롯을 그리면 다음과 같은 결과가 나타납니다. 첫 번째 데이터는 0이므로 무시하셔도 괜찮습니다.

from statsmodels.graphics.tsaplots import plot_acf
plot_acf(monthly);

맥주 생산량

다음으로 저희가 살펴볼 데이터는 맥주 생산량에 관한 것입니다. beer.xlsx 파일을 불러오시면 됩니다. parse_dataTrue로 설정하면, 자동적으로 날짜 데이터를 처리해줍니다. 기존에 있던 quarter라는 날짜 열은 각 분기의 첫 날짜, 즉 1월 1일, 4월 1일, 7월 1일, 10월 1일로 구성되어 있습니다. 이는 1년을 4분기로 나눈 각 분기의 첫 날짜를 의미합니다.

다음으로, index_col을 이용해서 quarter를 인덱스로 지정할 수 있습니다. 일반적으로 데이터를 불러올 때는 자동으로 번호가 부여되지만, 이 경우에는 번호 대신 쿼터 칼럼을 데이터의 이름으로 사용합니다.

beer = pd.read_excel('beer.xlsx', parse_dates=True, index_col='quarter')
beer.head()
production
quarter
1956-01-01 284
1956-04-01 213
1956-07-01 227
1956-10-01 308
1957-01-01 262

이 프로덕션은 맥주 생산량을 나타냅니다. 이를 그래프로 그려보면, 날씨의 패턴과 비슷하게 나타나게 됩니다. 보통 여름에는 맥주 판매량이 늘고, 겨울에는 추운 탓에 맥주 소비량이 줄어듭니다.

생산 패턴이 날씨와 비슷하게 나타나지만, 고유의 추세가 있습니다. 일정한 상승세를 보였다가 1975년을 기점으로 약간 감소하는 경향을 보입니다. 이는 사람들이 맥주 소비를 줄이고 다른 음료를 선호하기 시작했다는 것을 시사합니다.

그러나 이제 추세가 있다는 것을 알 수 있습니다. 이는 앞서 다룬 날씨 데이터와는 다릅니다. 날씨 데이터에서는 명확한 추세가 나타나지 않습니다. 물론 기후 변화로 인해 약간의 상승 경향은 있지만, 눈에 띄는 변화는 아닙니다. 굉장히 미세한 증가 추세이므로, 이를 통해 추세를 파악하기는 어렵습니다. 그러나 맥주 생산량을 보면, 눈에 띄는 추세의 존재를 확인할 수 있습니다.

beer.production.plot()
<Axes: xlabel='quarter'>

그래서 ACF 그래프를 보면, 정확하게는 12달 간격이 아니라 대략 4달 간격으로 패턴이 보여집니다. 이것은 분기인데, 4분기가 1년을 이루므로, 1년 간격으로 계절성이 나타나는 것을 볼 수 있습니다. 이는 그래프가 전반적으로 상승하면서 완만하게 내려가는 추세를 가지고 있음을 의미합니다.

이를 통해, 추세가 있다고 예상할 수 있습니다. 하지만 이것만으로는 추세가 있다고 단정하기 어렵고, 다른 요인들을 함께 고려할 때 추세를 유추해 볼 수 있습니다.

plot_acf(beer.production);

데이터의 계절성을 명확하게 보기 위해서는 우선 추세를 제거해야 합니다. 이를 위해 시계열 데이터에서 특정 시점의 데이터 간 차이를 구하는 '차분'을 사용할 수 있습니다.

예를 들어, 일정하게 증가하는 데이터에 대한 차분을 하게 될 경우, 차이는 일정하게 나올 것입니다. 이 차이를 그래프로 그릴 경우, 결과적으로 수평선을 이루는 그래프가 생성됩니다.

조금 더 복잡한 예를 들어보면, 일정하게 증가하는 추세에서 일부 데이터가 조금 더 늘어나고, 일부 데이터는 조금 더 줄어드는 경우가 있습니다. 이런 데이터에 차분을 적용하면, 일정하게 올라가는 부분은 배제되므로 원래의 증가 추세는 사라지고, 데이터의 증가와 감소만이 드러나게 됩니다.

비정상 시계열 데이터에서는 시계열 데이터의 패턴이 계속 변화합니다. 이런 추세 때문에 데이터가 전반적으로 증가하거나 또는 감소하는 비정상성이 발생하는 경우가 많습니다. 여기서 '정상'이란 시계열 데이터의 패턴이 일정하다는 뜻입니다. 즉, 데이터의 패턴이 계속 변화하면 그것은 비정상적인 상황입니다.

따라서 분석시, 데이터에 추세를 제거함으로써 이러한 비정상성을 제거할 수 있습니다. diff 함수를 사용하여 차분을 하면 추세를 제거할 수 있습니다. 증가 추세를 보이던 데이터를 차분하면 오르락 내리락하는 패턴만 남게 됩니다.

beer.production.diff().plot()
<Axes: xlabel='quarter'>

차분한 상태에서 ACF를 다시 그려보면, 계절성이 더 뚜렷하게 나타나는 것을 볼 수 있습니다. 다시 ACF를 확인하면 이전보다 높게 척도가 구현되던 부분이 모두 사라지고 계절성이 강조되었습니다.

여기서 '0, 1, 2, 3, 4'는 분기를 나타냅니다. 두 분기 지난 시점은 여름과 겨울이 바뀌는 지점이므로 관계가 역전되고, 네 분기 지난 시점은 1년이 경과한 지점이므로 같은 계절이 반복되므로 관계가 양성입니다. 이를 통해 고도의 자기상관을 확인할 수 있습니다.

다음으로, 코드에 dropna라는 표현이 보입니다. NA는 'Not Available', 즉 사용할 수 없다는 뜻이며, dropna은 불필요한 부분을 제거하라는 명령어입니다. 차분을 할 때 첫 번째 데이터는 앞선 데이터와 비교할 수 없으므로 계산에서 제외됩니다. 이제 이 후 처리를 위해 첫 번째 데이터를 제거하고, ACF를 계산하는 명령을 실행하면 됩니다.

plot_acf(beer.production.diff().dropna());

다음으로, 계절성 차분에 대해 설명하겠습니다. 계절성 차분은 데이터에서 계절성을 제거하기 위해 사용되는 기법이며, diff에 4를 입력함으로써 4개의 간격으로 차분할 수 있습니다. 예를 들어, 이번 달 봄 데이터와 지난해 봄 데이터를 비교하는 것입니다. 이는 작년 동기 대비를 비교하는 것으로써, 계절적인 요인이 있는 매출이나 실적 등을 고려할 때 사용됩니다.

우리가 아이스크림 혹은 맥주 회사라 가정해봅시다. 당연히 여름에 맥주 판매량이 증가할 것입니다. 이런 상황에서 봄과 여름을 비교하는 것보다, 작년 여름과 비교하는 것이 더 공정한 비교라 할 수 있습니다. 따라서 작년 여름과의 차이를 빼고, 겨울에는 작년 겨울과의 차이를 빼는 것입니다. 이를 계절성 차분이라고 합니다.

분기별 데이터인 경우엔 4를, 월별 데이터인 경우엔 12를 입력하시면 되며, 이를 통해 계절성을 제거할 수 있습니다. 그림으로 보면, 데이터는 여전히 변동성은 보이지만, 4개월 간격의 계절적 패턴이 아닌 무작위로 오르락 내리락하는 모습을 볼 수 있습니다. 또한, 차분을 통해 거의 일정하게 추세도 제거됩니다. 이는 데이터가 꾸준히 상승하는 추세를 보여도 차분을 통해 모든 추세가 사라지게 되는 원리를 반영합니다.

beer.production.diff(4).plot()
<Axes: xlabel='quarter'>

그러면, 계절성을 차분한 후에 다시 ACF를 그려보면, 대부분의 경우 연관성을 나타내지 않습니다. 약간의 연관성이 보일 수는 있지만, 그것이 큰 의미를 가지기는 어렵습니다.

그래서 별도의 특별한 관계는 거의 없습니다.

ACF에서 보였던 관계는 원래 계절성과 추세 때문에 생긴 것이었습니다. 이것들을 제거하면, 남은 움직임은 대부분 무작위적일 것입니다.

그러나 '무작위적'이라는 표현은 아무 이유 없이 변하는 것이 아니라, 외부 충격으로 인한 변화를 의미합니다. 예를 들어, 이전에 많이 팔리면 그 후에도 많이 팔린다거나, 반대의 경우는 아닙니다.

plot_acf(beer.production.diff(4).dropna());