Criando uma versão "janela-móvel" de qualquer função

O que são cálculos em janela móvel e por quê são importantes em análise de dados e de séries temporais? Neste texto exploramos a criação e aplicação de funções em janelas móveis usando o R.

Em análise de séries temporais, nada é estático. Uma correlação pode existir em um subconjunto temporal ou uma média pode variar de um dia para o outro. Os cálculos em janelas móveis simplesmente aplicam funções a um subconjunto de largura fixa (ou não) desses dados (também conhecido como uma janela móvel), indexando uma observação a cada cálculo.

Existem alguns motivos comuns pelos quais você pode querer usar um cálculo em janela móvel em análise de séries temporais:

  • Obter a tendência central ao longo do tempo (média, mediana)
  • Obter a volatilidade ao longo do tempo (desvio-padrão, variância)
  • Detectar mudanças de tendência (médias móveis rápidas vs. lentas)
  • Obter uma relação entre duas séries temporais ao longo do tempo (covariância, correlação)

O tipo de cálculo em janela móvel mais simples existente no R é o base::cumsum() que faz a soma acumulada de valores de um vetor, no qual cada janela móvel é definida como sendo todos os valores anteriores ao atual. Um exemplo visual de soma acumula de uma sequência de números de 1 a 10:

Entendida a importância e intuição do funcionamento dessas operações, passemos a alguns exemplos práticos. Demonstraremos como operacionalizar esses cálculos em janelas móveis com as principais opções de pacotes de R disponíveis atualmente para essa finalidade:

  • timetk
  • slider
  • runner

Essa lista com certeza não esgota todas as possibilidades, existem outras opções e fica ao critério do usuário escolher o que melhor se adequa ao seu contexto.

Pacotes

Para reproduzir os códigos aqui expostos é necessário alguns pacotes de R, o código a seguir verifica se os mesmos estão instalados, instala caso necessário e carrega os mesmos para a memória:

# Instalar/carregar pacotes
if(!require("pacman")) install.packages("pacman")
pacman::p_load(
"magrittr",
"dplyr",
"ggplot2",
"ggthemes",
"tidyquant",
"timetk",
"tidyr",
"slider",
"lubridate",
"runner"
)

O pacote {timetk}

O pacote timetk é um toolkit para visualização, tratamento e transformação de séries temporais e oferece as funções slidify() e slidify_vec() para operações em janela móvel.

Como exemplo inicial, utilizamos os dataset FANG que traz os preços diários de 4 ações de tecnologia listadas na NASDAQ:

# Dados de preços diários de ações (FANG)
df_fang <- tidyquant::FANG
df_fang
# # A tibble: 4,032 x 8
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28
## 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8
## 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8
## 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4
## 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1
## 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6
## 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3
## 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7
## 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0
## 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1
## # ... with 4,022 more rows

Calculando uma média móvel de 30 períodos para o preço ajustado (adjusted):

df_fang %>%
dplyr::select(symbol, date, adjusted) %>%
dplyr::group_by(symbol) %>% 
# Criar média móvel
dplyr::mutate(
roll_avg_30 = timetk::slidify_vec(
.x = adjusted,
.f = mean, 
.period = 30, 
.align = "right", 
.partial = TRUE
)
) %>%
tidyr::pivot_longer(cols = c(adjusted, roll_avg_30)) %>%
# Gerar gráfico
timetk::plot_time_series(
date, 
value, 
.title = "Média móvel de 30 períodos",
.color_var = name,
.facet_ncol = 2, 
.smooth = FALSE, 
.interactive = FALSE
)

Para mais operações simultaneamente pode ser interessante primeiro definir as funções utilizando o slidify() para depois calcular a operação em janelas móveis:

# Média móvel
roll_avg_30 <- timetk::slidify(.f = mean, .period = 30, .align = "right", .partial = TRUE)
# Soma acumulada
roll_sum_30 <- timetk::slidify(.f = sum, .period = 30, .align = "right", .partial = TRUE)
# Desvio padrão móvel
roll_sd_30 <- timetk::slidify(.f = sd, .period = 30, .align = "right", .partial = TRUE)
# Regressão móvel (rolling regression)
roll_lm_90 <- timetk::slidify(~ lm(..1 ~ ..2 + ..3), .period = 90, .unlist = FALSE, .align = "right")

E então aplicar as funções criadas:

df_fang %>%
dplyr::select(symbol, date, adjusted, volume) %>%
dplyr::group_by(symbol) %>% 
# Operações em janelas móveis
dplyr::mutate(
numeric_date = as.numeric(date),
m_avg_30 = roll_avg_30(adjusted),
m_sum_30 = roll_sum_30(adjusted),
m_sd_30 = roll_sd_30(adjusted),
m_lm_90 = roll_lm_90(adjusted, volume, numeric_date)
) %>% 
dplyr::filter(!is.na(m_lm_90))

## # A tibble: 3,676 x 9
## # Groups: symbol [4]
## symbol date adjusted volume numeric_date m_avg_30 m_sum_30 m_sd_30
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 FB 2013-05-10 26.7 30847100 15835 26.8 805. 0.842
## 2 FB 2013-05-13 26.8 29068800 15838 26.9 807. 0.805
## 3 FB 2013-05-14 27.1 24930300 15839 26.9 808. 0.756
## 4 FB 2013-05-15 26.6 30299800 15840 27.0 809. 0.747
## 5 FB 2013-05-16 26.1 35499100 15841 26.9 808. 0.762
## 6 FB 2013-05-17 26.2 29462700 15842 26.9 807. 0.766
## 7 FB 2013-05-20 25.8 42402900 15845 26.9 806. 0.794
## 8 FB 2013-05-21 25.7 26261300 15846 26.8 805. 0.822
## 9 FB 2013-05-22 25.2 45314500 15847 26.7 802. 0.863
## 10 FB 2013-05-23 25.1 37663100 15848 26.6 799. 0.880
## # ... with 3,666 more rows, and 1 more variable: m_lm_90 <list>

O pacote {slider}

O pacote slider oferece uma sintaxe familiar ao purrr para calcular operações em janelas móveis.

O exemplo mais simples de sua utilização é o cálculo de uma média móvel dado um vetor numérico. A janela móvel é indicada pelo argumento .before:

# Média móvel (2 "períodos") com alinhamento a direita
slider::slide_dbl(
.x = 1:5, 
.f = ~mean(.x), 
.before = 1, # valor atual + 1 valor anterior
.complete = TRUE 
)

O pacote oferece algumas funções derivadas de slide_dbl() para os cálculos mais comuns: slide_mean(), slide_sum(), slide_prod(), slide_min() e slide_max().

Para séries temporais o pacote oferece ainda um recurso interessante de realizar a operação com janela móvel relativa a um índice com as funções slide_index(). Se você já quis calcular algo como uma “média móvel de 3 meses”, em que o número de dias em cada mês é irregular, você pode gostar desta função.

No exemplo abaixo, primeiro é calculado uma média móvel de 90 períodos sem considerar o índice e depois é calculado outra média móvel, mas considerando um índice de 3 meses. A função slide_index_mean() realiza o cálculo da média móvel de 3 meses considerando o valor atual em relação a mesma data aproximada de 3 meses anteriores (não necessariamente 90 observações anteriores). O resultado é uma média móvel diária relativa a uma janela móvel de 3 meses:


# Calcular médias móveis
df_amzn_roll <- df_fang %>%
dplyr::filter(symbol == "AMZN") %>%
dplyr::select(date, symbol, adjusted) %>%
dplyr::mutate(
roll_avg_90 = slider::slide_mean(adjusted, before = 89, complete = TRUE),
roll_idx_3m = slider::slide_index_mean(
x = adjusted, # vetor numérico
i = date, # vetor índice (datas)
before = ~ .x %m-% base::months(3), # para evitar erro gerado por NAs
complete = TRUE
)
)

# Gerar gráfico
df_amzn_roll %>%
dplyr::filter(date <= lubridate::as_date("2014-06-30")) %>%
tidyr::pivot_longer(cols = c(adjusted, roll_avg_90, roll_idx_3m)) %>%
timetk::plot_time_series(
date,
value,
.title = "AMZN",
.color_var = name,
.smooth = FALSE,
.interactive = FALSE
)

Perceba que a média móvel que considera o índice de 3 meses tem início anterior, pois considera que os meses possuem número de dias irregulares e se ajusta a tal.

O pacote {runner}

O pacote runner oferece um framework arrojado para calcular operações com janelas móveis de tamanho fixo ou variante, opções de inclusão de lags, tratamento de NAs, computação paralela, e é desenhado para dados em formato de séries temporais ou longitudinais.

Exemplos simples de médias móveis com e sem lag:


# Média móvel de 2 "períodos"
runner::runner(1:5, f = mean, k = 2, na_pad = TRUE)

## [1] NaN 1.5 2.5 3.5 4.5

# Média móvel de 2 "períodos" com 1 lag
runner::runner(1:5, f = mean, k = 2, lag = 1, na_pad = TRUE)

## [1] NaN NaN 1.5 2.5 3.5

Às vezes, as observações de um conjunto de dados não são espaçados igualmente (ausência de fins de semana, feriados, etc.) e, portanto, o tamanho da janela móvel deve variar para manter o período de tempo esperado. Para essa situação, podemos especificar o argumento idx para que a função em execução seja aplicada nas janelas móveis dependendo da data. Neste exemplo abaixo a janela móvel possui o índice semanal variante conforme a data:


# Criar vetores de dados
dates <- Sys.Date() + cumsum(sample(1:5, 20, replace = TRUE)) # série temporal irregular
values <- 1:20

dates

## [1] "2021-08-23" "2021-08-24" "2021-08-26" "2021-08-30" "2021-09-04"
## [6] "2021-09-08" "2021-09-12" "2021-09-13" "2021-09-16" "2021-09-18"
## [11] "2021-09-23" "2021-09-26" "2021-09-27" "2021-09-30" "2021-10-03"
## [16] "2021-10-07" "2021-10-11" "2021-10-15" "2021-10-17" "2021-10-20"

values

## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

# Calcular média móvel com janela variante
runner::runner(
x = values,
k = "7 days",
idx = dates,
f = mean
)

## [1] 1.0 1.5 2.0 3.0 4.5 5.5 6.5 7.0 8.0 8.5 10.5 11.5 12.0 13.0 14.0
## [16] 15.5 16.5 17.5 18.0 19.0

Espero que esta exploração com exemplos de cálculos com funções em janelas móveis tenha sido proveitosa, apesar de breve e resumida. Recomendo fortemente consultar as documentações dos pacotes, pois os mesmos fornecem todas as informações, detalhes, motivação e mais exemplos.

 

Compartilhe esse artigo

Facebook
Twitter
LinkedIn
WhatsApp
Telegram
Email
Print

Comente o que achou desse artigo

Outros artigos relacionados

Coletando e integrando dados do BCB, IBGE e IPEA de forma automatizada

Quem trabalha com modelagem e previsão macroeconômica sabe o quanto é demorado reunir dados de diferentes fontes — Banco Central, IBGE, IPEA, FRED, IFI... Cada um com sua API, formato, frequência e estrutura. Esse gargalo de coleta e padronização consome tempo que poderia estar sendo usado na análise, nos modelos ou na comunicação dos resultados.

Foi exatamente por isso que criamos uma rotina de coleta automatizada, que busca, trata e organiza séries temporais econômicas diretamente das APIs oficiais, pronta para ser integrada a pipelines de previsão, dashboards ou agentes de IA econometristas.

Criando operações SQL com IA Generativa no R com querychat

No universo da análise de dados, a velocidade para obter respostas é um diferencial competitivo. Frequentemente, uma simples pergunta de negócio — “Qual foi nosso produto mais vendido no último trimestre na região Nordeste?” — inicia um processo que envolve abrir o RStudio, escrever código dplyr ou SQL, executar e, finalmente, obter a resposta. E se pudéssemos simplesmente perguntar isso aos nossos dados em português, diretamente no nosso dashboard Shiny?

Dashboard Financeiro com IA e Shiny Python: Análise de Dados Abertos da CVM

Este artigo apresenta um tutorial completo sobre como construir uma ferramenta de análise financeira de ponta. Utilizando Shiny for Python, demonstramos a automação da coleta de dados das Demonstrações Financeiras Padronizadas (DFP) da CVM e o tratamento dessas informações com Pandas. O ponto alto do projeto é a integração da IA Generativa do Google Gemini, que atua como um assistente de análise, interpretando os dados filtrados pelo usuário e fornecendo insights contábeis e financeiros em tempo real. O resultado é um dashboard dinâmico que democratiza a análise de dados complexos e acelera a tomada de decisão.

Boletim AM

Receba diretamente em seu e-mail gratuitamente nossas promoções especiais e conteúdos exclusivos sobre Análise de Dados!

Boletim AM

Receba diretamente em seu e-mail gratuitamente nossas promoções especiais e conteúdos exclusivos sobre Análise de Dados!

como podemos ajudar?

Preencha os seus dados abaixo e fale conosco no WhatsApp

Boletim AM

Preencha o formulário abaixo para receber nossos boletins semanais diretamente em seu e-mail.