내용을 읽기 전에 시계열 자료에 대한 개념이 헛깔린다면 아래 내용을 참조하기 바란다.

📌이동평균 평활법(Moving Average Smoothing)
이동평균 평활법은 분석자료의 일부만을 산술평균한 값으로 시계열자료를 평활화 하는 분석방법이다.
이동평균 평활법은 중심이동평균과 단순이동평균으로 구분된다. 만약 N이 5, 시점 $t$에 대해서의 중심이동평균은 시점 $t$을 중심으로 좌우 5개인 $t-2$부터 $t+2$까지의 산술 평균을 의미한다. 단순이동평균은 $t$시점 까지의 산술평균을 의미하므로, $t-4$부터 $t$까지의 산술 평균을 의미한다.
만약 시계열 자료에 계절변동이 포함되어 있다면, 주기에 따른 파동은 제거가 될 수 있으며, 우연변동의 분산을 1/N으로 감소하게 된다.
🌈단순 이동평균 평활법(Simple Moving Average Smoothing)
단순 이동평균 평활법은 아래의 수식과 같이 요약된다. 평균을 내는 기간을 의미하는 N이 짧으면 짧을수록 시계열자료에서 튀는 값에 대한 평활화 정도가 적어지며, N이 길어지면 평활화 정도가 커지게 된다. 이는 이상치가 존재하여도 관측자료가 많으면 평균에 미치는 영향이 적은 것과 유사하다.
$$m_t=[y_t+y_{t-1}+\cdots + y_{t-N+1}]/N$$
library(forecast)
library(stats)
AirPassengers
plot(AirPassengers)
# 3점 이동평균
lines(stats::filter(
AirPassengers,filter=rep(1/3,3),sides=1),col=2)
# 6점 이동평균
lines(stats::filter(
AirPassengers,filter=rep(1/6,6),sides=1),col=3)
# 9점 이동평균
lines(stats::filter(
AirPassengers,filter=rep(1/9,9),sides=1),col=4)

🌈이중 이동평균 평활법(Double Moving Average Smoothing)
이중 이동평균 평활법은 단순 이동평균이 추세를 보일 때 추가적으로 이를 평활화 하고자 할 때 적용하는 방법이다. 단순 이동평균의 결과를 다시 이동평균한 결과이므로, 단순이동평균 평활법에 추세가 남아있는 경우, 단기적 불규칙 변동을 제거하는데 활용된다.
$$m_t=[y_t+y_{t-1}+\cdots + y_{t-N+1}]/N$$
$$m^{(2)}_t=[m_t+m_{t-1}+\cdots+m_{t-N+1}]/N$$
$m^{(2)}_t$는 이중평균 평활법의 적용 결과
library(forecast)
library(stats)
plot(AirPassengers)
ff1=stats::filter(
AirPassengers,filter=rep(1,3)/3,side=1)
lines(stats::filter(
ff1,filter=rep(1,3)/3,sides=1),col=2)

🌈가중 이동평균 평활법(Weighted Moving Average Smoothing)
단순이동평균과 이중 이동평균은 시계열 자료 일부를 고려하여 분석하지만, 관측치에 대한 가중치는 모두 동일한 값으로 적용한다. 실제 이중 이동평균의 산정과정에서 적용되는 가중치는 중간의 관측값이 최근의 관측값보다 큰 가중치로 적용된다.
$$m^{(w)}_t=[w_t y_t+w_{t-1}y_{t-1}+ \cdots + w_{t-N+1}y_{t-N+1}]/N$$
$$(w_t+w_{t-1}+\cdots+w_{t-N+1})=1$$
library(forecast)
library(stats)
plot(AirPassengers)
# t-1시점에 0.4, t-2시점에 0.3 t-3시점에 0.2 t-4시점에 0.1 가중치를 부여
lines(stats::filter(
AirPassengers,(4:1)*.1,sides=1),col=2)

📌적용결과 확인하기
이동평균 평활법은 추세변동이나 계절변동을 포함하는 경우에도 이를 고려하여 분석이 가능한 모델로 개발되었다고한다. 해당 특징을 AirPassengers 자료로 확인해보자.
AirPassengers 자료의 경우에는 뒤로갈수록 자료의 분산이 커지므로 BoxCox변환을 통해 이동평균 적용에 대한 오차를 알아보도록하자. BoxCox에 대해 궁금하면 아래 링크를 참조하기 바란다.

library(forecast)
library(stats)
data(AirPassengers)
boxcox_out=BoxCox(AirPassengers,BoxCox.lambda(AirPassengers))
o1=stats::filter(boxcox_out,filter=rep(1/3,3),sides=1)
o2=stats::filter(boxcox_out,filter=rep(1/6,6),sides=1)
o3=stats::filter(boxcox_out,filter=rep(1/12,12),sides=1)
o4=stats::filter(o1,filter=rep(1,3)/3,sides=1)
o5=stats::filter(boxcox_out,(4:1)*.1,sides=1)
idx=complete.cases(o3)
{
par(mfrow=c(2,3))
plot(o1[idx]-boxcox_out[idx],type='l',ylim=c(-.1,.1))
plot(o2[idx]-boxcox_out[idx],type='l',ylim=c(-.1,.1))
plot(o3[idx]-boxcox_out[idx],type='l',ylim=c(-.1,.1))
plot(o4[idx]-boxcox_out[idx],type='l',ylim=c(-.1,.1))
plot(o5[idx]-boxcox_out[idx],type='l',ylim=c(-.1,.1))
}
아래 그림은 BoxCox변환한 AirPassengers 자료의 오차를 시각화한 것이다.

BoxCox 역변환을 통해 자료를 원래의 스케일로 변환한 후 계산한 오차이다.
{
par(mfrow=c(2,3))
plot(InvBoxCox(o1,
BoxCox.lambda(AirPassengers))[idx]-AirPassengers[idx],
type='l',ylim=c(-100,100))
plot(InvBoxCox(o2,
BoxCox.lambda(AirPassengers))[idx]-AirPassengers[idx],
type='l',ylim=c(-100,100))
plot(InvBoxCox(o3,
BoxCox.lambda(AirPassengers))[idx]-AirPassengers[idx],
type='l',ylim=c(-100,100))
plot(InvBoxCox(o4,
BoxCox.lambda(AirPassengers))[idx]-AirPassengers[idx],
type='l',ylim=c(-100,100))
plot(InvBoxCox(o5,
BoxCox.lambda(AirPassengers))[idx]-AirPassengers[idx],
type='l',ylim=c(-100,100))
}

📌코드 참고
library(forecast)
library(stats)
stats::filter(
AirPassengers,filter=rep(1,3)/3,side=1)
ma(AirPassengers, order=3, centre=F) #centre가 TRUE이면 중심이동평균