O filtro de médias móveis serve para suavizar séries de tempo e encontrar tendências nos dados. Este filtro é bastante simples e pode ser escalado com facilidade para lidar com múltiplas séries de tempo.
Published
February 20, 2024
Médias móveis
Uma média móvel, como o nome sugere, calcula a média de uma série temporal em janelas móveis. Tipicamente, a média móvel serve como uma estimativa da tendência da série; também é bastante comum usar a média móvel para identificar ciclos ou padrões numa série. Aplicar uma média móvel numa série de tempo \(y_t\) produz uma nova série \(z_t\):
\[
z_t = \sum_{j = -k}^{k}a_{j}y_{t+j}
\] No caso mais simples, k = 1, temos:
O exemplo acima, mostra uma média móvel simétrica de ordem 3, onde todos os pesos são iguais. Cada ponto na nova série \(z_t\) é uma média entre os valores vizinhos da série original. Tipicamente, valores próximos uns aos outros no tempo costumam ser similares; na prática, isto torna o filtro de médias móveis bastante suave.
Evidentemente, não é possível calcular este filtro no início e no final da série \(y_t\). Isto implica que a série \(z_t\) tem menos observações do que a série original \(y_t\). Isto, de fato, é um ponto negativo quando se usa filtros de médias móveis.
Médias móveis simétricas sempre tem um número ímpar de termos: no exemplo acima, \(k=1\) resultou num filtro com três termos; se tivéssemos usado \(k=2\) teríamos cinco termos e assim por diante. É possível fazer médias móveis não-simétricas variando a janela temporal. A equação abaixo mostra um filtro que soma as últimas duas observações e tira a média junto com a observação atual e a próxima observação.
Não existe forte “contraindicação” sobre o uso de médias móveis não-simétricas. Vale notar, contudo, que é relativamente fácil transformar uma média móvel não-simétrica em uma média móvel simétrica. Vamos tirar uma média móvel sobre a média móvel acima:
A série final é uma média móvel simétrica com menor peso nas pontas. Esta é uma média móvel de ordem 2x4. Este tipo de filtro também é conhecido como média móvel ponderada.
Na primeira demonstração acima, definimos que todos \(a_j\) teriam o mesmo valor. Isto não é necessário. Imagine que queremos um filtro que olha somente para o passado e atribui pesos decrescentes à medida que a observação se afasta no tempo. No caso de uma janela com apenas dois períodos teríamos algo da forma:
Pode-se definir uma média móvel ponderada com pesos que apresentam algum tipo de decaimento. O filtro acima também é conhecido como média móvel linearmente ponderada, pois os pesos decaem linearmente. Outra opção seria usar pesos que apresentam decaimento exponencial como
Comparando os termos da expressão acima com a média móvel ponderada anterior, vemos que os termos do passado agora tem menor peso. De maneira mais geral, o filtro de médias móveis exponencial é dado por
Para montar um exemplo de médias móveis vamos importar uma das rubricas da balança de pagamentos do Brasil. O gráfico abaixo mostra a série anual da rubrica “passe de atletas”1.
Num caso aplicado, seria interessante ter uma maneira simples de escalar o processo acima para múltiplas séries de tempo. Para calcular médias móveis com grande velocidade, vamos usar o pacote RcppRoll, onde cpp significa C++, a linguagem subjacente do pacote. A função roll_mean() aplica uma média móvel sobre uma série.
Objetos ts foram criados especificamente para armazenar séries de tempo, têm muitas praticidades, e são pré-carregados no R. A vasta maioria dos dados, e análises de dados, segue o paradigma de bases retangulares, de data.frame s por assim dizer. Neste sentido, o ts ou mts acaba sendo pouco prático2 quando se trabalha com múltiplas séries de tempo ou até mesmo com séries de tempo com altas frequências.
No exemplo abaixo vamos importar 9 séries do Índice de Produção Industrial (IPI) e aplicar uma média móvel 2x12 em cada uma delas. O código abaixo mostra como importar as séries e empilhá-las num único tibble. Para manipular os dados vamos utilizar o popular pacote dplyr3.
É relativamente simples aplicar o filtro sobre cada uma das séries. O gráfico mostra o resultado final.
series = series |>group_by(series_id) |>mutate(trend =roll_mean(value, n =12, fill =NA),trend =roll_mean(trend, n =2, fill =NA) )ggplot(series, aes(date)) +geom_line(aes(y = value), alpha =0.8, color ="#126782") +geom_line(aes(y = trend), lwd =0.8, color ="#023047") +facet_wrap(vars(series_id)) + theme_series
Extraindo tendência
Médias móveis fornecem uma estimativa simples da tendência de uma série. Neste sentido, é comum utilizar o filtro de médias móveis para decompor uma série. O código abaixo usa a base USMacroG para importar algumas séries macroeconômicas trimestrais dos EUA no período 1947-19974. Vamos modelar a tendência das séries usando uma janela de quatro anos (dois anos para trás e dois anos para frente), ou seja, uma média móvel de ordem 25.
O gráfico das séries livres de tendência é apresentado abaixo. Note que as séries não foram dessazonalisadas, portanto, elas ainda apresentam oscilação sazonal.
Apesar de simples, o filtro de médias móveis é bastante utilizado e tem boas propriedades estatísticas. O gráfico abaixo compara o ajuste de um filtro de médias móveis com o filtro Baxter-King5, que é bastante mais sofisticado. Como se vê, ambos os filtros chegam em resultados muito similares.
O filtro de médias móveis exponenciais ou EWMA (exponentially weighted moving average) é bastante popular em finanças. Na prática, ele é um caso específico de suvização exponencial6. Tecnicamente, seria possível implementá-lo usando stats::filter fornecendo os pesos adequados. O código abaixo replica o primeiro exemplo, da série “passe de atletas”.
O exemplo acima é um tanto quanto artificial. Tipicamente, o filtro EWMA é utilizado em séries financeiras de alta frequência. O código abaixo usa o pacote TTR (Technical Trading Rules), especializado em funções para finanças, para computar o EWMA de uma série de preço. Para tornar o exemplo mais simples vamos aproveitar a base ttrc do próprio pacote.
Code
library(TTR)data(ttrc)ttrc = ttrc |>rename_with(tolower) |> dplyr::filter(date >=as.Date("2004-01-01")) |>mutate(trend07 =EMA(close, n =7),trend30 =EMA(close, n =30),trend50 =EMA(close, n =50) ) |>select(date, close, starts_with("trend")) |>pivot_longer(cols =-date, names_to ="series")ggplot(ttrc, aes(x = date, y = value, color = series)) +geom_line(data = dplyr::filter(ttrc, series =="close"), alpha =0.8) +geom_line(data = dplyr::filter(ttrc, series !="close"), lwd =0.5) +scale_color_manual(name ="",values =c("#073B4C", "#FFD166", "#0CB0A9", "#F4592A"),labels =c("Original", "EWMA7", "EWMA25", "EWMA50")) + theme_series
Footnotes
Nome completo: “Transfer rights on sporting club players - annual - net”.↩︎
Existem, na verdade, uma infinitude de classes de objetos para lidar com séries temporais no R. Para uma referência veja o pacote tsbox que funciona como uma pedra de Rosetta entre esses diferentes tipos de objetos.↩︎
Apesar de muito bom, o dplyr tem um problema chato quando se trabalha com séries de tempo. Há um conflito entre as funções dplyr::filter e stats::filter, que se utiliza para calcular uma média móvel. É muito comum carregar o pacote e esquecer deste detalhe. Para resolver os conflitos basta sempre declarar a função usando o operador “quatro pontos”, ::.↩︎
Para a definição das variáveis, consulte ?USMacroG↩︎
M. Baxter and R.G. King. Measuring business cycles: Approximate bandpass filters. The Review of Economics and Statistics, 81(4):575-93, 1999.↩︎