Usando tidyr para criar dummies e lags

Imagine que você tenha uma base de dados com dezenas (ou centenas) de variáveis e quer criar os lags de 1 a 5 para cada uma destas variáveis. Além disso, precisa adicionar dummies mensais para controlar algum fator sazonal. Esta tarefa faz parte do dia-a-dia de quem trabalha com modelos econométricos/machine learning e ter uma forma prática ajuda bastante.

Existem algumas formas de resolver esse problema. Uma bem simples e prática envolve o tidyr, pacote do tidyverse dedicado à manipulação de frames. Suas principais funções são pivot_longer e pivot_wider, usadas para posicionar os dados, respectivamente, em formato long e wide. Fazendo uso correto destas funções e de seus argumentos, é possível resolver o problema de forma muito simples. Então vamos lá!

Para começar, vou criar uma frame artificial contendo uma coluna com datas mensais e três colunas representando diferentes variáveis – var1, var2 e var3. Lembrando que a abordagem é geral o suficiente para acomodar qualquer número de variáveis.

library(tidyverse)
library(lubridate)

set.seed(123)

df <- tibble(data = seq(ymd("2019-01-01"), ymd("2020-07-01"), by = "month"),
             var1 = rnorm(19),
             var2 = rnorm(19),
             var3 = rnorm(19))

tail(df, 5)
## # A tibble: 5 x 4
##   data         var1    var2    var3
##   <date>      <dbl>   <dbl>   <dbl>
## 1 2020-03-01 -0.556  0.878  -0.0429
## 2 2020-04-01  1.79   0.822   1.37  
## 3 2020-05-01  0.498  0.689  -0.226 
## 4 2020-06-01 -1.97   0.554   1.52  
## 5 2020-07-01  0.701 -0.0619 -1.55

De início, vamos criar as dummies sazonais. O primeiro passo é criar uma coluna com os meses da coluna de datas. A função month do pacote lubridate fornece uma forma conveniente para isso. Em seguida, criamos uma coluna com valores iguais a 1 – isso ficará mais claro adiante e os nomes das colunas não importam. Por fim, recorremos à função pivot_wider. Transpondo a coluna dos meses e preenchendo com os 1’s, obteríamos um frame no qual cada mês é uma coluna onde o valor é igual a 1 para o mês correspondente na coluna data e NA nos demais. Como nosso objetivo é ter 0’s no lugar desses NA, usamos o argumento values_fill.

df_seasonal <- df %>%
  
  mutate(meses = month(data, label = T, abbr = T),
         um = 1) %>%
  
  pivot_wider(names_from = meses, values_from = um, values_fill = list(um = 0))

tail(df_seasonal, 5)
## # A tibble: 5 x 16
##   data         var1    var2    var3   jan   fev   mar   abr   mai   jun   jul
##   <date>      <dbl>   <dbl>   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2020-03-01 -0.556  0.878  -0.0429     0     0     1     0     0     0     0
## 2 2020-04-01  1.79   0.822   1.37       0     0     0     1     0     0     0
## 3 2020-05-01  0.498  0.689  -0.226      0     0     0     0     1     0     0
## 4 2020-06-01 -1.97   0.554   1.52       0     0     0     0     0     1     0
## 5 2020-07-01  0.701 -0.0619 -1.55       0     0     0     0     0     0     1
## # … with 5 more variables: ago <dbl>, set <dbl>, out <dbl>, nov <dbl>,
## #   dez <dbl>

Agora vamos criar os lags das variáveis. Aqui a gente vai usar tanto a pivot_longer como pivot_wider. O primeiro passo é deixar o frame no formato long, isto é, as variáveis serão empilhadas em uma única coluna que eu chamarei de “var”. Os valores ficarão na coluna “l0” que denota o lag 0.

Na sequência, vamos criar colunas com os lags desejados: 1 a 5. Essas colunas também precisarão ser colocadas no formato long. Finalmente, vamos expandir o frame (formato wider) reunindo as duas colunas: “var” e “lags”. Isto produzirá o resultado desejado. O group_by e arrange são utilizados para garantir a ordem temporal dos dados.

df_lags <- df %>%
  
  pivot_longer(-"data", names_to = "var", values_to = "l0") %>%
  
  group_by(var) %>%
  
  arrange(data) %>%
  
  mutate(l1 = lag(l0, 1),
         l2 = lag(l0, 2),
         l3 = lag(l0, 3),
         l4 = lag(l0, 4),
         l5 = lag(l0, 5)) %>%
  
  pivot_longer(-c(data, var), names_to = "lag", values_to = "valor") %>%
  
  pivot_wider(names_from = c("var", "lag"), values_from = "valor")

tail(df_lags, 5)
## # A tibble: 5 x 19
##   data       var1_l0 var1_l1 var1_l2 var1_l3 var1_l4 var1_l5 var2_l0 var2_l1
##   <date>       <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
## 1 2020-03-01  -0.556   0.111   0.401   0.360   1.22   -0.446  0.878    0.895
## 2 2020-04-01   1.79   -0.556   0.111   0.401   0.360   1.22   0.822    0.878
## 3 2020-05-01   0.498   1.79   -0.556   0.111   0.401   0.360  0.689    0.822
## 4 2020-06-01  -1.97    0.498   1.79   -0.556   0.111   0.401  0.554    0.689
## 5 2020-07-01   0.701  -1.97    0.498   1.79   -0.556   0.111 -0.0619   0.554
## # … with 10 more variables: var2_l2 <dbl>, var2_l3 <dbl>, var2_l4 <dbl>,
## #   var2_l5 <dbl>, var3_l0 <dbl>, var3_l1 <dbl>, var3_l2 <dbl>, var3_l3 <dbl>,
## #   var3_l4 <dbl>, var3_l5 <dbl>

A grande vantagem desta abordagem é não precisar escrever o nome das variáveis para criar os lags – lembrem-se de que elas podem ser dezenas ou centenas. Adicionalmente, não precisamos criar funções auxiliares ou recorrer à geração dinâmica de variáveis – o que no contexto do tidyverse envolve algum tipo de Non-Standard Evaluation – nem sempre tão simples para quem tá começando.

Aviso legal: Todo o conteúdo desta página é de responsabilidade pessoal do autor e não expressa a visão da instituição a qual o autor tem vínculo profissional.

Avatar
J. Renato Leripio
Quantitative Research Analyst

Analista de pesquisa quantitativa na Itaú Asset Management.