단순한 예측 기법
이번 시간에는 단순하지만 효과적인 시계열 데이터 예측 기법들을 살펴보도록 하겠습니다.
맥주 생산량 데이터
실습에 사용할 데이터는 맥주 생산량입니다. 우선, 판다스를 임포트하고, read_excel
함수를 통해 'beer.xlsx' 파일을 열어봅시다. parse_dates
옵션을 True
로 설정하면, 날짜는 자동으로 처리됩니다. 그 다음, 'quarter' 컬럼을 인덱스로 지정합니다. 인덱스는 각 행의 번호가 됩니다.
맥주 데이터는 1956년 1월 1일부터 2010년 4월 1일까지 있습니다. 이 데이터를 바탕으로 미래의 맥주 생산량을 예측하기 위해서는, 이어지는 날짜들을 생성해야 합니다.
import pandas as pd
beer = pd.read_excel('beer.xlsx', parse_dates=True, index_col='quarter')
beer
production | |
---|---|
quarter | |
1956-01-01 | 284 |
1956-04-01 | 213 |
1956-07-01 | 227 |
1956-10-01 | 308 |
1957-01-01 | 262 |
... | ... |
2009-04-01 | 398 |
2009-07-01 | 419 |
2009-10-01 | 488 |
2010-01-01 | 414 |
2010-04-01 | 374 |
218 rows × 1 columns
date_range
함수를 사용하면 날짜를 만들 수 있습니다. 4월 1일부터 날짜를 생성해야 하기 때문에, 인덱스에서 -1을 하면 마지막 쿼터를 참조하게 됩니다.
-1은 맨 뒤를 의미하며, -2, -3 등은 각각 그 다음 항목을 참조합니다. 이렇게 뒤에서부터 번호를 붙일 때에는 -1, -2, -3, -4 등으로 표현됩니다.
마지막 쿼터를 참조하고, 20을 지정하면 다음 20개 분기 시점을 의미합니다. 즉, 4월 1일 이후 20개의 분기 데이터를 생성합니다.
한 해를 4개의 분기로 나누므로 20개 분기는 5년치 예측에 해당하게 됩니다.
다음으로, freq
에 등장하는 문자열은 빈도를 나타냅니다. 'Q'는 분기, 'A'는 연, 'M'는 월을 의미합니다.
따라서 이 함수를 통해 특정 단위의 날짜를 생성합니다.
예를 들어, future
에 생성된 날짜를 보면 2010년 6월 30일부터 시작해서 9월 30일, 12월 31일 등으로 생성된 것을 확인할 수 있습니다.
future = pd.date_range(beer.index[-1], periods=20, freq='Q')
future
DatetimeIndex(['2010-06-30', '2010-09-30', '2010-12-31', '2011-03-31', '2011-06-30', '2011-09-30', '2011-12-31', '2012-03-31', '2012-06-30', '2012-09-30', '2012-12-31', '2013-03-31', '2013-06-30', '2013-09-30', '2013-12-31', '2014-03-31', '2014-06-30', '2014-09-30', '2014-12-31', '2015-03-31'], dtype='datetime64[ns]', freq='Q-DEC')
평균 기법
분기 단위로 날짜를 생성하였으니, 이제 예측을 해보겠습니다. 가장 간단한 예측 기법으로 평균 기법을 사용할 것입니다. 평균 기법은 과거 시점부터 최근 시점까지 전체 기간의 평균을 내는 방법입니다. 이 평균값으로 예측을 진행합니다.
평균 기법은 어떠한 추세가 있거나 다양한 요인이 있더라도 결국 평균적인 경향을 따라갈 것이라는 가정하에 유용합니다. 그래서 이름이 평균 기법이지만, 실질적으로는 과거부터 지금까지의 평균 값을 이용해서 예측을 진행하는 것입니다.
우리가 예를 들어 어떤 자산의 가격을 예측한다고 가정해봅시다. 자산 가격에는 기초 가치가 존재합니다. 대부분의 자산은 기초 가치 가까운 가격으로 거래됩니다. 따라서 가격을 평균하여, 이 값을 기초 가치라고 간주합니다. 가격은 평균적으로 가치 주변에 위치하게 될 것이라는 가정입니다.
따라서, 기초 가치가 일정하고 오차가 무작위적인 상황에서는 이와 같은 평균 기법을 사용할 수 있습니다.
평균 기법은 매우 단순한데, 여러 데이터 포인트의 production
을 평균내면 됩니다.
그 다음으로 새로운 데이터 프레임을 생성합니다. 파이썬에서 딕셔너리를 사용하여 데이터의 이름과 내용을 지정합니다.
데이터 프레임을 생성하면 그 위에 이름이 입력되며, production
밑에 어떤 데이터를 입력할지 설정할 수 있습니다. 우리에게 필요한 것은 이 평균 값입니다.
그리고 인덱스에는 future
를 사용하면, 우리의 future
값이 인덱스로 추가됩니다. 평균은 하나지만, future
가 20개의 값이므로 평균 값이 20번 반복하여 채워집니다.
pred = pd.DataFrame(
{'production': beer.production.mean()},
index=future)
pred.head()
production | |
---|---|
2010-06-30 | 415.37156 |
2010-09-30 | 415.37156 |
2010-12-31 | 415.37156 |
2011-03-31 | 415.37156 |
2011-06-30 | 415.37156 |
따라서 실제로 어떻게 진행하는지 보면, 평균 값이 415.37156인 데이터를 가지고 작업하게 됩니다. 이 평균 값을 기반으로 데이터를 모두 채워나가게 됩니다.
이를 플롯하여 시각화해줍니다. 실제 값은 파란 색, 평균 값은 주황색 선으로 그려주게 됩니다.
beer.production.plot()
pred.production.plot()
<Axes: xlabel='quarter'>
단순 기법
다음으로 소개할 방법은 '단순 기법'입니다. 이 방법은 가장 간단합니다. 마지막 값으로 예측을 하는 것이 단순 기법의 핵심입니다. 예를 들어, 데이터가 특정 범위에서 변동하다가 마지막 값에 도달했다고 가정하면, 이 후에도 마지막 값으로부터 무작위로 변동할 것으로 가정하는 것입니다. 즉, 이전에 무슨 일이 발생했든 간에, 마지막으로 관측된 결과에서 새롭게 출발할 것으로 예측하는 방법입니다. 이 방법은 평균 기법보다 더 간단하기 때문에 '단순 기법'이라 부릅니다.
'단순 기법'은 '랜덤 워크' 형태의 데이터에 주로 사용됩니다. '랜덤 워크'는 이전 시점의 상태를 기반으로 다음 시점의 값을 결정하지만, 이 과정에서 일정한 '오차'가 발생합니다. 이 '오차'는 외부 요인으로 발생하며, 이러한 오차가 시간이 지남에 따라 계속 누적되게 됩니다. 따라서 현재의 가치는 누적된 오차의 합계에 영향을 받게 되며, 그 중에는 시계열 자체에서는 확인할 수 없는 외부적인 요인에 의한 변동성도 포함되어 있습니다.
평균으로 예측하는 것은 과거를 반영하는 방법이지만, 랜덤워크에서 과거는 현재 상태에 영향을 주지 않습니다.
단순 기법에서는 마지막 관측치로 예측하면 됩니다.
pred = pd.DataFrame(
{'production': beer.production[-1]},
index=future)
beer.production.plot()
pred.production.plot()
<Axes: xlabel='quarter'>
계절성 단순 기법
다음으로, 맥주 데이터처럼 계절성이 있는 경우, 단순 기법을 사용하면 어느 정도 예측이 가능하지만, 계절성을 추가하면 더욱 정확한 예측이 가능합니다.
단순 기법으로 예측을 하면, 그래프는 일정한 경향성을 유지하게 됩니다. 하지만 계절성이 포함된 단순 기법을 사용하면, 매년 반복되는 봄, 여름, 가을, 겨울의 계절성이 예측에 반영됩니다.
예를 들어, 맥주 회사로서 앞으로의 매출을 예측하고 싶다면, 작년의 매출이 다음 해, 그 다음 해에도 반복될 것이라는 가정을 하게 됩니다. 이런 가정은 전혀 이상하지 않습니다.
이런 방법을 계절성 단순 기법이라고 합니다.
이제 코드가 다소 복잡해집니다.
beer.production[-4:]
는 배열의 마지막 네 항목을 가리키는 표현입니다. 또한 tile
함수는 동일한 항목을 반복하여 이어붙입니다. 마지막 4개의 계절을 다섯 번 연속으로 복사하여 붙이려는 의미입니다. 그렇게하면 총 20개 항목이 연속됩니다.
import numpy as np
pred = pd.DataFrame(
{'production': np.tile(beer.production[-4:], 5)},
index=future)
beer.production.plot()
pred.production.plot()
<Axes: xlabel='quarter'>
표류 기법
다음에는 '표류 기법'에 대해 설명하겠습니다. 표류 기법이란 우리가 단순 기법에서 사용했던 것처럼 마지막 값이 계속 반복되는 것이 아니라, 추세를 고려하는 방법입니다. 즉, 추세를 구하고 이를 지속될 것으로 가정해 사용합니다.
예를 들어 추세를 어떻게 구할지에는 여러 방식이 있지만, 가장 간편한 방법은 시계열 데이터의 시작점부터 종료점까지를 연결하는 것입니다. 그렇게 하면 우리가 가정한 추세는 이런 형태를 띄를 것입니다.
물론, 추세를 이런 식으로 만드는 것이 자연스럽게 보일 수도 있지만, 어느 것이 정확하게 맞는지는 판단하기 어렵습니다. 최종적인 추세는 이런 형태가 될테지만, 데이터 값의 상승과 하강은 다른 외부 요인 때문일 수 있습니다.
즉, 이 형태가 최선의 추세라고 판단할 수도 있고 아닐 수도 있습니다. 대부분의 추세는 실제로 경험해보지 않으면 알 수 없기 때문에, 이는 결국 분석가의 판단에 달렸습니다.
시계열 데이터가 이렇게 이어질 것이라고 가정하거나, 혹은 이렇게 이어질 것이라고 가정할 수 있지만, 우리는 일단 첫 번째 추세를 가정해보겠습니다.
그러면, 첫 번째로 len
함수를 사용해 맥주가 생산된 전체 기간을 구해야 합니다. len
함수는 시퀀스의 길이를 반환하는 함수입니다. 전체 기간은 여기에서 1을 빼줍니다.
1을 빼주는 이유를 설명하자면, 만약 우리가 1월 1일과 4월 1일, 7월 1일 세 분기의 데이터를 가지고 있다면, 처음에서 끝까지의 간격은 두 분기가 됩니다. 즉, 전체 기간은 3에서 1을 뺀 2가 됩니다.
num = len(beer.production) - 1
num
217
이제 마지막 생산량에서 첫 생산량을 빼고 이를 기간으로 나눠주면, 한 분기 당 평균 변화량을 구할 수 있습니다.
drift = (beer.production[-1] - beer.production[0]) / num
drift
0.4147465437788018
이제 마지막 분기부터 앞으로 20개 분기 동안, 매 분기 같은 변화량으로 변한다고 가정하면 예측을 할 수가 있습니다.
import numpy as np
last = beer.production[-1]
pred = pd.DataFrame(
{'production': last + np.arange(1, 21) * drift},
index=future)
beer.production.plot()
pred.production.plot()
<Axes: xlabel='quarter'>
표류와 계절성 단순 기법의 결합
우리는 표류와 계절성 단순 기법을 결합할 수 있습니다. 마지막 관측치에 계절적 변동을 더하는 대신에, 표류 기법의 예측치에 계절적 변동을 더하면 됩니다.
seasonal = np.tile(beer.production[-4:], 5)
pred = pd.DataFrame(
{'production': seasonal + np.arange(1, 21) * drift},
index=future)
beer.production.plot()
pred.production.plot()
<Axes: xlabel='quarter'>