All Posts By

Fernando da Silva

Análise das Atas do COPOM com text mining

By | Data Science, Política Monetária

Introdução

Mineração de texto, ou text mining, é um tópico muito interessante, pois há um potencial enorme de aplicações para obtenção de insights através dessa técnica envolvendo análise textual. Com a finalidade de demonstrar seu uso, neste post faremos uma breve e introdutória análise das atas do Comitê de Política Monetária - COPOM usando text mining com o auxílio do pacote tidytext.

As atas do COPOM são um caminho natural para qualquer economista em busca de uma fonte de dados para exercitar o text mining, já que a autoridade monetária realiza ajustes (mudanças) no texto a cada reunião, realizada a cada 45 dias. As atas são disponibilizadas publicamente neste link em arquivos PDFs (atualmente há 238 reuniões realizadas). Inicialmente vamos pegar o arquivo da última reunião publicada e importar os dados para o R, fazendo tratamentos e posterior análise. Por fim, vamos coletar os dados de todo o histórico de atas para refazer a análise de forma a incorporar o componente temporal.

Um ponto importante é que utilizaremos as versões em inglês das atas, apesar de o COPOM disponibilizar também em português. Isso se deve ao fato de que as ferramentas aqui utilizadas, para aplicar a técnica de text mining, funcionarem melhor com textos na língua inglesa.

Pacotes

Os pacotes utilizados neste exercício podem ser instalados/carregados com o gerenciador pacman, todos provenientes do CRAN:


# Instalar/carregar pacotes
if(!require("pacman")) install.packages("pacman")
pacman::p_load(
"tidytext",
"pdftools",
"stopwords",
"textdata",
"dplyr",
"tidyr",
"lubridate",
"magrittr",
"knitr",
"ggplot2",
"ggthemes",
"jsonlite",
"purrr",
"stringr",
"scales",
"forcats"
)

Text mining de uma ata do COPOM

Vamos importar a última ata do COPOM para o R. Primeiro criamos um objeto para armazenar o link para o arquivo e, na sequência, usamos a função pdf_text do pacote pdftools para ler cada página do arquivo PDF e transformar os dados em um vetor de caracteres de tamanho igual ao número de páginas.

# URL da última ata do COPOM
www <- "https://www.bcb.gov.br/content/copom/copomminutes/MINUTES%20238.pdf"
# Ler arquivo PDF convertendo para caracter
raw_copom_last <- pdftools::pdf_text(www)

Tratamento de dados

Um primeiro olhar sobre os dados mostrará que há uma série de caracteres especiais "\n" e "\r" que indicam quebras de linhas. Portanto, vamos tratar de forma a obter um objeto tibble com uma coluna (text) com os dados do texto de cada página da ata, outra coluna (meeting) indicando o mês da reunião e a última coluna (page) que informará a página referente ao texto.

# Tratamento de dados
copom_last_clean <- dplyr::tibble(
text = unlist(strsplit(raw_copom_last, "\r"))
) %>% 
dplyr::mutate(
meeting = "May 2021", 
page = dplyr::row_number(),
text = gsub("\n", "", text)
)

Text mining

Agora estamos com os dados quase prontos para uma análise! O que faremos agora é começar a aplicar algumas das técnicas de text mining provenientes do livro Text Mining with R escrito pela Julia Silge e David Robinson.

O primeiro passo é "tokenizar" nossos dados, de forma a obter "unidades do texto" que servirão para a análise. Isso facilitará para realizarmos a contagem de palavras frequentes, filtrar palavras em específico, etc. O processo de "tokenizar" é definido no livro como:

"A token is a meaningful unit of text, most often a word, that we are interested in using for further analysis, and tokenization is the process of splitting text into tokens."

Para utilizar a técnica, usamos a função unnest_tokens do pacote tidytext e na sequência realizamos a contagem das palavras encontradas:

# Text mining - Criar tokens
copom_last <- copom_last_clean %>% 
tidytext::unnest_tokens(word, text)
# Contar palavras
copom_last %>%
dplyr::count(word, sort = TRUE) %>% 
dplyr::slice_head(n = 6) %>% 
knitr::kable()


Como podemos observar, as palavras mais frequentes são palavras comuns como "the", "of", "in", etc. que não servirão muito para nossa análise. Essas palavras são chamadas de "stop words" no mundo do text mining. Vamos fazer uma tratativa para remover essas palavras usando como base o objeto stop_words proveniente do pacote tidytext e, antes, removemos também números que foram apontados como "palavras":

copom_last_sw <- copom_last %>%
# Remover palavras comuns (stop words)
dplyr::anti_join(stop_words)%>%
# Remover números
dplyr::mutate(word = gsub("[^A-Za-z ]", "", word)) %>%
# Contar palavras
dplyr::count(word, sort = TRUE) %>% 
dplyr::filter(word != "")
copom_last_sw %>% 
dplyr::slice_head(n = 6) %>% 
knitr::kable()

Agora sim temos os dados prontos para uma análise de sentimento!

Análise de sentimento

Nosso foco agora é usar ferramentas de análise para tirar informação desses dados, ou seja, queremos saber o que as palavras das atas do COPOM podem indicar com base na tentativa de apontarmos um "score" para cada uma delas, usando datasets e bibliotecas de "sentimento do texto" do tidytext para isso.

Vamos ver quais são as palavras negativas e positivas usadas com mais frequência. A função get_sentiments do tidytext fará isso pra gente, bastando apontar um lexicon, que pode ser "bing", "afinn", "loughran" ou "nrc".

# Obter análise de sentimento das palavras com base em uma biblioteca
copom_last_sw %>%
dplyr::inner_join(tidytext::get_sentiments("bing")) %>% 
dplyr::slice_head(n = 10) %>% 
knitr::kable()

Como resultado, "risks" é a palavra negativa que aparece mais vezes (13 no total) nessa ata do COPOM, e "recovery", uma palavra positiva, é usada 4 vezes no total. Um segundo olhar sobre esse resultado pode levantar uma suspeita, pois "recovery" é apontada como uma palavra positiva (recuperação), no entanto, é razoável supor que pode estar também associada com uma situação anterior negativa. Isso é um possível problema da análise que o leitor pode investigar.
Agora vamos explorar a análise graficamente:

# Análise de sentimento final da ata de Maio/2021
copom_sentiment <- copom_last %>%
dplyr::inner_join(tidytext::get_sentiments("bing")) %>%
dplyr::count(meeting, page, sentiment) %>%
tidyr::pivot_wider(
id_cols = c(meeting, page),
names_from = sentiment, 
values_from = n,
values_fill = 0
) %>%
dplyr::mutate(sentiment = positive - negative)
# Gerar gráfico
copom_sentiment %>% 
ggplot2::ggplot(ggplot2::aes(page, sentiment, fill = sentiment > 0)) +
ggplot2::geom_col(show.legend = FALSE) +
ggplot2::scale_fill_manual(values = c("#b22200", "#282f6b")) +
ggplot2::labs(
x = "Página da ata",
y = "Sentimento",
title = "Análise de sentimento da Ata do COPOM - Maio/2021",
subtitle = "Bing lexicon",
caption = paste0("Elaboração: analisemacro.com.br\nDados: ", www)
)

O gráfico conta uma história interessante. O texto começou negativo na página 3 (sobre atualização de conjuntura, cenário e riscos), mas depois foi "neutro" na página 4 (sentimento positivo - negativo = 0) - onde é discutido a condução da política monetária e -, por fim, na última página o texto fica positivo com a decisão da reunião do COPOM.

Vale enfatizar que a primeira página, referente a capa, não é considerada na análise. Já a página 2, da contracapa, possui igual número de palavras negativas e positivas.

Text mining com todas as atas do COPOM

Agora vamos aplicar a técnica apresentada acima para um conjunto maior de dados, desta vez vamos pegar todas as atas disponíveis no site do Banco Central do Brasil - BCB através deste link.

Vamos expandir nossa análise capturando o texto de cada Ata do COPOM desde sua 42ª reunião, totalizando 198 atas para nossa análise. Faremos a comparação da frequência relativa de palavras e tópicos e veremos como o sentimento (conforme explorado acima) varia entre os relatórios.

Dados

Infelizmente, os links para as atas em PDF seguem um padrão irregular, mas felizmente para você, preparei um web-scrapping que automatizará o processo de captura dos links e dados. O código a seguir coletará os arquivos PDF e os deixará prontos para a mineração com o tidytext.

# URL para página com JSON dos links das atas
www_all <- "https://www.bcb.gov.br/api/servico/sitebcb/copomminutes/ultimas?quantidade=2000&filtro="
# Raspagem de dados
raw_copom <- jsonlite::fromJSON(www_all)[["conteudo"]] %>% 
dplyr::as_tibble() %>% 
dplyr::select(meeting = "Titulo", url = "Url") %>% 
dplyr::mutate(url = paste0("https://www.bcb.gov.br", url)) %>% 
dplyr::mutate(text = purrr::map(url, pdftools::pdf_text))
# Tratamento de dados
copom_clean <- raw_copom %>% 
tidyr::unnest(text) %>% 
dplyr::filter(!meeting == "Changes in Copom meetings") %>% 
dplyr::group_by(meeting) %>%
dplyr::mutate(
page = dplyr::row_number(),
text = strsplit(text, "\r") %>% gsub("\n", "", .),
meeting = stringr::str_sub(meeting, 1, 3) %>% 
stringr::str_remove("[:alpha:]") %>% 
as.numeric()
) %>%
dplyr::ungroup() %>% 
tidyr::unnest(text) %>% 
dplyr::arrange(meeting)

Estatística básica dos dados

Vamos ver o que conseguimos obter calculando algumas estatísticas básicas dos textos.

Número de palavras por ata

# Frequência de palavras por ata
copom_words <- copom_clean %>%
tidytext::unnest_tokens(word, text) %>%
dplyr::count(meeting, word, sort = TRUE) %>%
dplyr::ungroup()
# Total de palavras por ata
total_words <- copom_words %>% 
dplyr::group_by(meeting) %>% 
dplyr::summarize(total = sum(n))
# Gerar gráfico
total_words %>% 
ggplot2::ggplot(ggplot2::aes(x = meeting, y = total)) +
ggplot2::geom_line(color = "#282f6b", size = 0.8)+
ggplot2::geom_point(
shape = 21, 
fill = "white", 
color = "#282f6b", 
size = 1.6, 
stroke = 1.1
) +
ggplot2::scale_y_continuous(labels = scales::number_format()) +
ggplot2::labs(
x = "Reunião (nº da ata)", 
y = "Número de palavras",
title = "Número de palavras nas atas do COPOM",
subtitle = "42ª até 238ª reunião",
caption = "Elaboração: analisemacro.com.br\nDados: BCB"
)

Percebe-se algumas mudanças ao longo do tempo, especialmente entre as reuniões número 180 e 181, onde houve remoção considerável de seções do comunicado na gestão Tombini. No mesmo sentido, de redução do total de palavras por comunicado, o início da gestão de Illan Goldfajn marcou mudança no layout e estrutura das seções da ata, alterações essas que permanecem em vigor até hoje.

A redução do tamanho dos comunicados, de forma geral, é certamente um ponto interessante que merece investigação em se tratando de qualidade de comunicação da política monetária.

Sobre o que os diretores discutiram nas reuniões?

Vamos compilar uma lista das palavras usadas com maior frequência em cada ata. Como antes, vamos omitir as "stop words".

# Palavras por ata
copom_text <- copom_clean %>% 
dplyr::select(meeting, page, text) %>%
tidytext::unnest_tokens(word, text)
# Gerar gráfico
copom_text %>% 
dplyr::mutate(word = gsub("[^A-Za-z ]", "", word)) %>%
dplyr::filter(word != "") %>%
dplyr::anti_join(stop_words) %>%
dplyr::group_by(meeting) %>%
dplyr::count(word, sort = TRUE) %>% 
dplyr::mutate(rank = dplyr::row_number()) %>%
dplyr::ungroup() %>% 
dplyr::arrange(rank, meeting) %>%
dplyr::filter(rank < 9, meeting > 230) %>% 
ggplot2::ggplot(ggplot2::aes(y = n, x = forcats::fct_reorder(word, n))) +
ggplot2::geom_col(fill = "#282f6b") +
ggplot2::facet_wrap(~meeting, scales = "free", ncol = 4) +
ggplot2::coord_flip() +
ggplot2::labs(
x = "",
y = "",
title = "Palavras mais frequentes nas atas do COPOM",
subtitle = "Excluídas palavras comuns (stop words) e números.",
caption = "Elaboração: analisemacro.com.br\nDados: BCB"
)


Como esperado, muitas discussões sobre inflação. Vamos tentar encontrar algo mais informativo com esses dados.

Conforme Silge e Robinson, podemos usar a função bind_tf_idf para juntar a frequência do termo (tf) e a frequência inversa do documento (idf) ao nosso conjunto de dados. Essa estatística diminuirá o peso em palavras muito comuns e aumentará o peso em palavras que só aparecem em algumas atas. Em essência, extrairemos o que há de especial em cada ata. As atas do COPOM sempre falarão muito sobre inflação e juros, e a estatística "tf-idf" pode nos dizer algo sobre o que é diferente em cada ata.

Também limparemos alguns termos adicionais que o pdftools captou (abreviações e palavras estranhas ou fragmentadas), aumentando nossa lista de "stop words".

# Stop words personalizadas
custom_stop_words <- dplyr::bind_rows(
dplyr::tibble(
word = c(
tolower(month.abb), 
tolower(month.name),
"one","two","three","four","five","six","seven","eight","nine","ten",
"eleven","twelve", "wkh", "ri", "lq", "month", "wr", "dqg",
"hdu", "jurzwk", "zlwk", "zlwk", "hfhpehu", "dqxdu", "kh", "sulfh", "dv",
"kh", "prqwk", "hdu", "shulrg", "dv", "jurzwk", "wkdw", "zdv", "iru", "dw",
"wkdw", "jrrgv", "xqh", "eloolrq", "eloolrq", "iluvw", "dq", "frqvxphu", 
"prqwk", "udwh", "sulo", "rq", "txduwhu", "vhfwru", "pandemic", 
"dffxpxodwhg", "hg", "kdyh", "sdqghg", "sulfhv", "rq", "sdqvlrq", 
"percent", "forvhg", "frpsduhg", "lqgh", "ryhpehu", "wklv", "kdv", "prqwkv",
"bcbgovbr", "banco", "head"
), 
lexicon = c("custom")
),
stop_words
)
# Palavras por ata
copom_text_refined <- copom_text %>% 
dplyr::mutate(word = gsub("[^A-Za-z ]", "", word)) %>%
dplyr::filter(word != "") %>%
dplyr::group_by(meeting) %>% 
dplyr::count(word, sort = TRUE) %>% 
tidytext::bind_tf_idf(word, meeting, n) %>% 
dplyr::arrange(desc(tf_idf))
# Gerar gráfico
copom_text_refined %>% 
dplyr::anti_join(custom_stop_words, by = "word") %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>% 
dplyr::group_by(meeting) %>%
dplyr::mutate(id = dplyr::row_number()) %>%
dplyr::ungroup() %>% 
dplyr::filter(id < 9, meeting > 230) %>% 
ggplot2::ggplot(ggplot2::aes(y = tf_idf, x = word, fill = meeting)) +
ggplot2::geom_col(show.legend = FALSE) +
ggplot2::facet_wrap(~meeting, scales = "free", ncol = 4) +
ggplot2::coord_flip() +
ggplot2::labs(
x = "",
y = "tf-idf",
title = "Palavras mais distintas nas atas do COPOM",
subtitle = "Estatística tf-idf do tidytext",
caption = "Elaboração: analisemacro.com.br\nDados: BCB"
) +
ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1))

Esse gráfico já mostram uma história interessante dessa amostra que escolhemos. Podemos observar preocupações com o BREXIT, reformas e agenda econômica, o surgimento do termo "covid" a partir da ata nº 231 e subsequente adoção do forward guidance e, por fim, também como destaque, a mudança da política para uma "normalização parcial" (termos "partial" e "normalization").

Comparando o sentimento entre as atas
Como o sentimento variou entre as atas? Vamos usar a abordagem que usamos no início para a ata de maio/2021 e aplicá-la a cada ata.

# Análise de sentimento das atas
copom_sentiment_all <- copom_text %>%
dplyr::anti_join(stop_words) %>%
dplyr::inner_join(tidytext::get_sentiments("bing")) %>%
dplyr::count(meeting, page, sentiment) %>%
tidyr::pivot_wider(
id_cols = c(meeting, page),
names_from = sentiment, 
values_from = n,
values_fill = 0
) %>%
dplyr::mutate(sentiment = positive - negative)
# Gerar gráfico
copom_sentiment_all %>% 
dplyr::filter(meeting > 230) %>% 
ggplot2::ggplot(ggplot2::aes(page, sentiment, fill = sentiment > 0)) +
ggplot2::geom_col(show.legend = FALSE) +
ggplot2::scale_fill_manual(values = c("#b22200", "#282f6b")) +
ggplot2::facet_wrap(~meeting, ncol = 4, scales = "free_x") +
ggplot2::annotate("segment", x = -Inf, xend = Inf, y = -Inf, yend = -Inf)+
ggplot2::annotate("segment", x = -Inf, xend = -Inf, y = -Inf, yend = Inf) +
ggplot2::labs(
x = "Página da ata",
y = "Sentimento",
title = "Análise de sentimento das Ata do COPOM",
subtitle = "Bing lexicon, amostra das últimas 8 atas",
caption = "Elaboração: analisemacro.com.br\nDados: BCB"
)

O resultado mostra que o sentimento mudou consideravelmente no texto das atas da amostra das últimas 8 reuniões, com forte predominância "negativa" desde o advento da pandemia da Covid-19.

Conclusão

Isso é tudo por hoje. Essas técnicas são muito interessantes e promissoras, merecendo exploração aprofundada. Certamente o exercício aqui apresentado possui falhas e pontos de melhoria, mas serve como uma introdução ao tema. Espero que goste, caro leitor!

Possui interesse no tema de política monetária? A Análise Macro disponibiliza o curso de Teoria da Política Monetária dentro da temática de Central Banking. Aproveite!

_____________

Como automatizar scripts de R usando o GitHub Actions

By | Data Science

Se você tem um script de R para executar uma determinada rotina em uma frequência predeterminada, talvez seja interessante investir em economia de tempo automatizando essa tarefa. Nesse tutorial demonstramos como utilizar o GitHub Actions para rodar scripts de R automaticamente, se livrando de qualquer trabalho manual e repetitivo.

São diversos os contextos e aplicações nos quais a automatização de um script de R pode ser útil, dentre os principais exemplos destaca-se:

  • Relatórios dinâmicos com R Markdown
  • Atualização de dashboards
  • Extração e atualização de uma base de dados
  • Web scrapping de uma página
  • Rotinas de robustez contra erros em pacotes

Muitos desses exemplos, e a depender do contexto, requerem que o usuário de R execute o script em uma frequência mensal, semanal, diária ou até mesmo de 1 em 1 hora. Dessa forma, a automatização não é apenas interessante, mas vital para um bom fluxo de trabalho.

Vamos pegar um desses exemplos como demonstração nesse tutorial: faremos um web-scrapping da página de criptomoedas do site TradingView para obter dados de cotações, volume, retornos %, etc. e, adicionalmente, criaremos uma dashboard básica com esses dados para demonstrar uma possibilidade de uso. Por fim, automatizaremos esse processo de modo que a dashboard seja atualizada de hora em hora.

Pré-requisitos

Antes de começar, para esse tutorial deve-se cumprir os seguintes pré-requisitos (pule essa etapa se você já os cumpre):

Se houverem dificuldades com o Git, principalmente se for a primeira vez, recomendo fortemente dar uma lida no Happy Git and GitHub for the useR, que é um tutorial de como usar o Git e GitHub integrado ao R/RStudio.

Criar um pacote

O primeiro passo é criar um pacote R que vai conter nosso script na forma de uma função. Mas não se assuste, é mais fácil do que parece pois, felizmente, a comunidade de R já criou diversas ferramentas para facilitar esse processo. Ao final, utilizaremos a função para a rotina de web-scrapping dos dados de criptomoedas, da mesma forma que a maioria dos pacotes de coleta de dados funcionam.

Aqui utilizamos o pacote usethis para criar um pacote básico. Basta passar para função create_package o caminho (path) de onde se deseja armazenar os arquivos que serão gerados, no nosso caso ficará dentro de uma pasta chamada cryptoscrap.

usethis::create_package("cryptoscrap")

Uma nova sessão do R será aberta com um R Project já pré configurado e com os arquivos mínimos do nosso pacote.

Em sequência, abrimos o arquivo DESCRIPTION recém criado e preenchemos algumas informações básicas como título do pacote, descrição, informações de autor, etc. e, opcionalmente pode ser utilizado uma licença como a MIT (neste caso pode ser usado a função usethis::use_mit_license()). Para saber mais sobre esse preenchimento básico, assim como outras informações sobre desenvolvimento de pacotes, recomendo conferir o livro do Hadley Wickham e Jenny Bryan intitulado R Packages.

Finalizado esse procedimento inicial, podemos prosseguir para o script de R.

Escrever um script de R

Conforme mencionado, faremos um web-scrapping de uma tabela com dados de criptomoedas provenientes do TradingView. Utilizaremos apenas dois pacotes nesta etapa, o rvest e o purrr, e colocaremos a rotina dentro de uma função que será solicitada na dashboard mais adiante.

Para começar a criar a função usamos novamente o pacote usethis, gerando um novo arquivo de scripts dentro da pasta R do nosso projeto (entre aspas o nome do arquivo .R que será gerado):

usethis::use_r("crypt_scrap")

No novo arquivo .R criado escrevemos nossa função:

crypt_scrap <- function() {

# Crytocurrency page URL
crypto_url <- rvest::read_html(
"https://www.tradingview.com/markets/cryptocurrencies/prices-all/"
)

# Get raw data from URL
crypto_raw <- rvest::html_nodes(crypto_url, css = "table")

# Convert from list to tibble
crypto_table <- rvest::html_table(crypto_raw)
crypto_table <- purrr::pluck(crypto_table, 1)
crypto_table <- purrr::set_names(
crypto_table,
c(
"Name", 
"Mkt Cap",
"FD Mkt Cap",
"Last", 
"Avail. Coins", 
"Total Coins",
"Traded Vol", 
"Chg %"
)
)

# Return tibble
return(crypto_table)
}

Em sequência, devemos adicionar as dependências do nosso pacote, ou seja, estes dois pacotes que estamos utilizando na nossa função. Isso faz com que, ao carregar localmente o pacote que estamos criando por exemplo, estes dois pacotes externos também sejam carregados para a sessão do R ao utilizar a função crypt_scrap que criamos.

Para adicionar as dependências é muito simples:

usethis::use_package("rvest")
usethis::use_package("purrr")

Agora já temos um rascunho de pacote pronto para ser testado! Para testar, é importante verificar se a função está funcionando localmente. Outro teste que pode ser feito é através do pacote devtools, que oferece ferramentas para desenvolvimento de pacotes com uma série de parametrizações úteis que indicam se o pacote e suas funções seguem as regras e recomendações gerais do CRAN. Para isso basta rodar:

devtools::check()

Ao final dos testes é indicado se houve errors, warnings ou notes. Se estiver tudo ok, o que foi o caso, podemos prosseguir.

Criar um repositório no GitHub

Agora vamos criar um repositório no GitHub para hospedar nosso pacote com os arquivos. Tudo que precisa ser feito é:

  1. Estar logado no GitHub
  2. Clicar no botão New
  3. Definir um nome do repositório (aqui usamos crypto_scrap)
  4. Clicar em Create repository

Opcionalmente pode ser incluído uma descrição, similar ao que preenchemos no arquivo DESCRIPTION do nosso pacote.

Em seguida o GitHub criará um link para o repositório criado, direcionando para esta página, onde aparecerão alguns comandos em Git que podemos utilizar para integrar nosso projeto local com o repositório. Tudo que precisamos fazer é copiar o código que aparece na sugestão intitulada "…or create a new repository on the command line" e colar no Terminal do RStudio para fazer a configuração necessária. No nosso exemplo executamos o código abaixo:

echo "# crypto_scrap" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/schoulten/crypto_scrap.git
git push -u origin main

Com efeito, nossa pasta de pacote local estará configurada como um repositório Git e assim já podemos subir nossos arquivos (fazer o push) para o GitHub. Para isso, executamos no Terminal:

git add .
git commit -m "adding package"
git push

Ao voltarmos na página do GitHub, dando uma atualizada na página, todos os nossos arquivos locais também estarão lá disponíveis.

Criar uma dashboard

Agora vamos deixar o GitHub em stand by para voltar ao R e criar uma dashboard com os dados das criptomoedas. Nesse exemplo vamos criar uma dashboard estática contendo apenas uma tabela com os dados (bem básico), e utilizamos o pacote flexdashboard para tal. Os códigos em .rmd para gerar a dashboard podem ser conferidos nesse link. O resultado é esse:

Criado o nosso "produto final", podemos partir para a etapa final de automatização.

Usar o renv para gerenciar dependências

Em seguida, fazemos um passo adicional em nosso projeto que é criar um arquivo renv.lock que conterá uma série de configurações de gerenciamento de dependências do nosso projeto/pacote. O que basicamente isso significa é que será tirada uma "foto" do estado atual de configurações, pacotes, dependências que estamos utilizando, para que o projeto seja reprodutível em outro ambiente, como o GitHub Actions.

Esse assunto é bastante complexo e não é o foco aqui se aprofundar no tema, de modo que tudo que precisa ser feito é executar no Console:

renv::init()
renv::snapshot()

Agendar tarefa com o GitHub Actions

Como pode ter sido observado, criamos um fluxo de trabalho aqui:

  • Temos um pacote com uma função para extrair dados de criptomoedas
  • Usamos essa função em uma dashboard que gera uma tabela com os dados

Agora o que queremos é delegar esse fluxo de trabalho para que o GitHub Actions faça isso automaticamente para nós, em uma periodicidade de 60 em 60 minutos, já que os dados oferecem essa tempestividade.

Portanto, o que faremos na prática é:

  • Criar um arquivo (workflow) que agenda uma tarefa no GitHub Actions
  • Especificar um intervalo de agendamento da tarefa
  • Configurar o ambiente R no GH Actions
  • Executar script de web-scrapping + atualização da dashboard
  • Salvar (commit e push) os resultados

Importante: a indentação do código é importante aqui, se tiver dificuldade em identificar os espaçamentos corretos, confira neste link como ficou o resultado final dessa parte do nosso código.

Criar arquivo de workflow do GitHub Actions

Primeiro precisamos criar o arquivo de workflow, que conterá toda a rotina do que queremos que o GitHub Actions execute. Ele deve estar dentro de uma pasta .github/workflows a partir da raiz do projeto que estamos trabalhando e ter uma extensão .yaml. Em nosso exemplo, ficou assim:

├── .github
│ ├── workflows
│ ├── update.yaml

O nome do arquivo update.yaml pode ser qualquer um.

Especificar um intervalo de agendamento da tarefa

Utilizaremos um agendador de tarefas denominado "CRON" para executar periodicamente nossos códigos.

A parte mais importante para se compreender é como especificar o intervalo de tempo com o qual estamos trabalhando utilizando uma expressão: o script deve ser executado a cada 5 minutos? De hora em hora? Diariamente? Mensalmente? Para cada uma dessas possibilidades há uma expressão específica de configuração e neste link podemos encontrar alguns exemplos dessas expressões para tomar como base.

Em nosso caso, como queremos a execução de 60 em 60 minutos, usamos a expressão "0 * * * *". No arquivo de workflow a sintaxe é conforme abaixo, logo nas primeiras linhas:

on:
push:
branches:
- main
- master
schedule:
- cron: "0 * * * *"

Configurar o ambiente R no GH Actions

Em seguida, vamos configurar uma sessão de R no servidor do GitHub Actions, já que, basicamente, o GH Actions é nada mais do que um computador localizado em algum lugar do mundo que o GitHub nos disponibiliza para executarmos algo nele, em nosso caso são scripts de R. A abordagem aqui utilizada se baseia em alguns templates fornecidos pela equipe do r-lib.

Resumidamente, esta parte do arquivo de workflow:

- Especifica o tipo de sistema operacional a ser usado (usaremos Windows)
- Define as variáveis de ambiente R e configura a autenticação de acesso ao repositório GitHub
- Instala o R
- Instala/restaura os pacotes necessários

name: Dashboard update
jobs:
Dashboard-update:
runs-on: windows-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
- uses: r-lib/actions/setup-r@v1
- name: Cache packages
uses: actions/cache@v2
with:
path: ~\AppData\Local\renv
key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
restore-keys: |
${{ runner.os }}-renv-
- name: Restore packages
run: |
if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv")
renv::restore()
shell: Rscript {0}
- name: Install workflow dependencies
run: |
install.packages(c("rcmdcheck", "sessioninfo", "devtools"))
shell: Rscript {0}

Executar script de web-scrapping + atualização da dashboard

Com o R configurado, agora executa-se os scripts que escrevemos. No nosso exemplo, a função de web-scrapping do pacote que criamos será chamada dentro do arquivo .rmd que gera a dashboard.

- name: Install pandoc
run: |
choco install pandoc
- name: Render Rmarkdown/Update data
run: |
devtools::load_all()
rmarkdown::render("index.Rmd")
shell: Rscript {0}

Salvar (commit e push) os resultados

Até aqui o workflow terá gerado uma nova dashboard com os dados de criptomoedas atualizada, mas tudo isso (esses arquivos) estarão no servidor do GitHub Actions. Portanto, precisamos, por fim, fazer o commit & push dessas atualizações para o nosso repositório do GitHub. Isso é feito conforme abaixo:

- name: Commit and push
run: |
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add .
git commit -m "Dashboard update"
git push

- name: Session info
run: |
options(width = 500)
pkgs <- installed.packages()[, "Package"]
sessioninfo::session_info(pkgs, include_base = TRUE)
shell: Rscript {0}

Salvar resultados no repositório

Ok, o trabalho duro já passou, agora só falta fazermos um novo check em nosso pacote, verificar se está tudo ok e salvo e subir as últimas modificações para o repositório do GitHub.

Primeiro o check no Console do RStudio:

devtools::check()

Apareceram 2 notes e para corrigir adicionamos as seguintes linhas no arquivo .Rbuildignore:

index.Rmd
index.html
^\.github$

Agora podemos prosseguimos com o commit & push, no Terminal do RStudio:

git add .
git commit -m "create pkg, dash and workflow"
git push

Ao visitarmos novamente a página do nosso repositório criado no site do GitHub, veremos todas as modificações realizadas.  E pronto!

Além disso, no botão "Actions" do repositório aparecerá o nosso workflow desenhado para executar toda essa rotina que trabalhamos aqui, de hora em hora. Portanto, estamos por fim "livres" de trabalho manual e o nosso produto final, uma dashboard, está totalmente automatizada. Legal, não?!

Recomendo esperar até 24 horas para verificar se o workflow está funcionando, pois o GitHub pode ter um delay para começar a executar toda a rotina definida.

O resultado final desse exemplo disponibilizei neste repositório e a dashboard pode ser acessada por este link.

Ressalvas

Esse tutorial introdutório resumiu demasiadamente diversos pontos que merecem atenção, de modo que, possivelmente, não cobre alguns possíveis obstáculos que podem surgir ao longo do desenvolvimento de um projeto desses. Recomendamos utilizar exaustivamente as documentações dos pacotes e ferramentas aqui utilizadas, de modo a entender mais profundamente o que está sendo feito.

Por fim, eu adoraria saber como como foi a experiência individual ao tentar reproduzir esse tutorial. Sinta-se livre para mandar um feedback, caro leitor(a).

 

5 dicas de R que gostaria de ter recebido no início

By | Data Science

Se você está começando a aprender sobre R agora, ou até mesmo se já possui algum conhecimento, mas sente que chegou em um ponto de "estagnação", esse texto é pra você. Aqui reúno cinco dicas e aprendizados que passei a adotar que ajudam na curva de aprendizado da linguagem, mas que não foram tão óbvias no início.

1. Use um guia de estilo (style guide)

Guias de estilo são importantes e devem ser encarados como nossos amigos na hora de escrever um código. Em outras palavras, assim como a pontuação e acentuação correta, os guias de estilo tornamascoisasmaisfaceisdeler.

Boas referências sobre guia de estilo podem ser encontradas no  Tidyverse Style Guide, escrito pelo Hadley Wickham, que foi baseado no Google's R Style Guide. Uma leitura desses guias é certamente proveitosa, pois reúnem um compilado de boas práticas para escrever códigos em R, contemplando temas como sintaxe, nomes de objetos, indentação do código, controle de fluxo, etc.

Eu posso garantir que não é nada de outro mundo, e tornará sua vida no R mais fácil a longo prazo.

2. Otimize o RStudio

Você certamente já se deparou com arquivos Rhistory em suas pastas de códigos de R ou mensagens, um tanto quanto incomodativas, sobre salvar objetos usando .Rdata. Apesar de ser um comportamento com boa intenção da configuração padrão do RStudio - ou seja, tentar evitar a perda de comandos e objetos de uma sessão do R - não traz muitos benefícios e, em geral, esses recursos devem ser deixados de lado. Há pouco controle sobre o comportamento desses arquivos que são gerados automaticamente pelo programa, além de outras desvantagens, o que certamente não é o ideal para qualquer código que preze pela reprodutibilidade.

Portanto, e partindo do princípio de que um código reprodutível é idealmente organizado em um arquivo .R, desabilitar o .Rhistory e o .Rdata tornará sua experiência no RStudio menos contraproducente. Para desabilitar esses recursos é simples: navegue por Tools > Global Options > General e desmarque as opções:

- Restore .RData into workspace at startup
- Always save history (even when not saving .RData)

Além disso, o tema visual padrão do RStudio é uma tela branca não muito convidativa para quem passa algumas longas horas olhando para ela. Felizmente podemos modificar o visual do programa de modo a adaptá-lo às preferências do usuário. Na mesma tela de configuração indicada acima, no menu Appearance, é possível escolher outros temas com cores diferentes. Você pode testar e modificar para verificar com qual tema se adapta melhor, eu utilizo o tema "Cobalt" atualmente.

3. Aprenda a generalizar tarefas

Se você precisa repetir um código mais do que duas vezes em um determinado projeto, é melhor você criar uma função que generalize a ação sendo executada. No final das contas, serão menos linhas e mais fácil de ler.

Esse princípio, proferido e seguido por diversos programadores, é um ponto de inflexão quando programamos em R ou qualquer outra linguagem. A ideia básica é trazer facilidade, agilidade e eficiência para as rotinas de programação, algo que é usualmente ignorado nos primeiros passos em uma nova linguagem.

Quando optamos por duplicar um determinado código, por exemplo, o tratamento de um conjunto de dados ou as configurações de saída de um gráfico, estamos explicitamente dificultando a atualização e manutenção desse código, já que a mesma modificação teria que ser feita em mais de uma linha ao longo do código. Com uma função, que executa a mesma tarefa, é mais simples fazer a manutenção do código, bastando modificá-la uma única vez. Apesar de haver um custo de aprendizado maior, escrever funções tornará suas rotinas mais robustas, além de haver ganhos de economia de tempo.

Mas não há motivo para se apavorar e perder tempo criando funções para tudo, comece pelo básico. Uma boa referência sobre esse assunto pode ser o livro Advanced R do Hadley Wickham, que tem um capítulo dedicado ao tema.

4. Leia (e escreva) a documentação

É comum receber dúvidas de iniciantes ou ler postagens em fóruns com perguntas que são facilmente encontradas digitando "?nome_da_função" no Console. Apesar de ser um comportamento intuitivo, não é recomendável procurar respostas em outras pessoas sem antes instigar em si próprio um pouquinho de instinto investigativo. Nesse ponto entram as documentações, que nada mais são do que guias descritivos sobre um determinado pacote e/ou função.

Não cair na tentação de enviar a dúvida para um amigo ou conhecido, mas, antes, ir pesquisar sobre a documentação do pacote é uma maneira educada de atingir maior independência na linguagem. Portanto, use e abuse das documentações pois, certamente, a pessoa que escreveu a documentação teve bastante trabalho e pensou nos detalhes e dúvidas que poderiam surgir com o uso do objeto da documentação.

Dentro dessa dica também é importante não menosprezar o uso dos comentários em um código. Não há necessidade de poluir o código com comentários, mas também não há razão para ter medo de usá-los, e isso facilita a vida de outras pessoas que podem vir a ter acesso ao seu código.

5. Reprodutibilidade é importante

Especialmente quando trabalhando com dados e problemas do mundo real em uma equipe, o usuário de R precisa pensar, e escrever, de uma maneira que a solução que se quer encontrar seja replicável para outras pessoas, do contrário não haverá sentido em compartilhar sua análise ou código. Esse é um princípio básico do método científico.

Uma boa aplicação desse princípio são os chamados arquivos de projeto ou .Rproj no RStudio, que permitem criarmos uma estrutura e configuração comum de pastas que podem ser usadas para determinado script de R. Isso é importante pois facilita a utilização do código por outros usuários, já que embute configurações globais como encoding, caminhos de pastas e diretórios de trabalho, etc. Portanto, uma boa prática ao iniciar um novo projeto ou código, por exemplo uma análise de dados, que será disponibilizado para outras pessoas/ambientes, é utilizar o pacote usethis para criar um novo projeto através de uma simples função:


usethis::create_project("nome_do_projeto")

A função cria um novo arquivo de projeto já com uma estrutura de pasta organizada para armazenar scripts, rápido e fácil. O pacote também oferece outras funcionalidades diversas para otimizar a organização de projetos, pacotes, etc., com especial importância para a reprodutibilidade. Vale a pena explorar e aprender sobre.

E, por fim, mas não menos importante: aproveite e faça os cursos da Análise Macro, pois eles irão facilitar e acelerar a sua curva de aprendizado da linguagem R!

 

Dados do IPEADATA no R: web-scrapping e outras alternativas

By | Data Science

Graças ao esforço da comunidade brasileira de R, já foram lançados alguns pacotes para acesso aos dados do IPEADATA através do R, como o ecoseries e o ipeadatar. Ambos os pacotes são de fácil utilização e facilitam a vida de quem precisa coletar os dados da plataforma de dados do IPEA. Infelizmente, estes pacotes encontram-se arquivados no CRAN, que é o servidor oficial de pacotes do R, até a data de publicação deste post. Isso pode ser muito frustrante para quem pretendia instalar os pacotes pela primeira vez ou, por exemplo, para quem os utiliza em ambiente de produção. Portanto, como acessar os dados do IPEADATA até que estes pacotes retornem ao CRAN?

Existem algumas alternativas. Primeiro, vamos carregar os pacotes utilizados neste post:

# Instalar/carregar pacotes
if(!require("pacman")) install.packages("pacman")
pacman::p_load(
"devtools",
"purrr",
"jsonlite",
"magrittr",
"dplyr",
"tidyr",
"lubridate",
"httr"
)

1. Instalar uma versão específica do pacote através do devtools

Com o pacote devtools é possível instalar uma versão específica de um pacote que esteja arquivada no CRAN, de modo que podemos pegar a última versão estável, ou seja, em funcionamento, do pacote e instalar facilmente com a função install_version. No caso do ipeadatar, a penúltima versão disponível no CRAN Package Archive é a 0.1.1, que parece ser estável, haja visto que o pacote foi arquivado após o lançamento da versão 0.1.2. Para instalar por esse método é bastante simples, bastando indicar o pacote, versão e repositório (CRAN).

devtools::install_version(
"ipeadatar", 
version = "0.1.1", 
repos = "http://cran.r-project.org"
)

2. Instalar uma versão específica através do install.packages

O mesmo procedimento anterior é possível de ser feito através do já conhecido install.packages. Basta saber a URL onde a versão do pacote desejado está localizada. Essa informação pode ser obtida na página do CRAN Package Archive. No caso do ipeadatar, uma vez que você tenha a URL, instalar o pacote é similar ao exemplo anterior:

install.packages(
"https://cran.r-project.org/src/contrib/Archive/ipeadatar/ipeadatar_0.1.1.tar.gz", 
repos = NULL, 
type = "source"
)

3. Criar uma função para extrair dados do IPEADATA

A penúltima alternativa deste breve post envolve basicamente replicar o que os pacotes já fazem. Neste caso, iremos utilizar a API do IPEADATA para coletar os dados. A função abaixo realiza a coleta e tratamento prévio dos dados, com um nível básico de robustez contra erros, retornando um objeto tibbl em formato long com colunas de data, valor e código da(s) série(s). A função possui somente um argumento que é o código da série, podendo ser utilizado como vetor de caracteres com mais de um código para coletar em uma única vez.

get_ipea <- function(code){

# Check if argument is character
if (purrr::is_character(code) & !purrr::is_null(code)) {
code <- toupper(code)
} else stop("Code argument must be a character, check the argument.", call. = FALSE)

# Ler página
raw_dados <- try(
suppressWarnings(
purrr::map(
.x = purrr::map(
.x = code,
~paste0(
"http://www.ipeadata.gov.br/api/odata4/ValoresSerie(SERCODIGO='",
.x,
"')"
)
),
~jsonlite::fromJSON(.x)
)
),
silent = TRUE
)

# Warning message for series with null return
if (0 %in% purrr::map(
.x = 1:length(code) %>% purrr::set_names(code),
~length(raw_dados[[.x]][["value"]]))
) {

flag <- purrr::map(
.x = 1:length(code) %>% purrr::set_names(code),
~length(raw_dados[[.x]][["value"]])
) %>%
dplyr::as_tibble() %>%
tidyr::pivot_longer(colnames(.)) %>%
dplyr::filter(value == 0)

if (0 %in% flag$value) {

stop(
paste0(
"Code [",
paste(flag$name, collapse = ", "),
"] not available / not found in IPEADATA."
),
call. = FALSE
)

}

}

# Data wrangling
dados <- purrr::map(.x = 1:length(code), ~raw_dados[[.x]][["value"]]) %>%
dplyr::bind_rows() %>%
dplyr::select("date" = `VALDATA`, "value" = `VALVALOR`, "code" = `SERCODIGO`) %>%
dplyr::mutate(
date = lubridate::as_date(date),
code = as.factor(code)
) %>%
dplyr::as_tibble()

return(dados)

}

A lista de códigos das séries disponíveis no IPEADATA é disponibiliza em um formato não muito convidativo neste link. Portanto, criei um protótipo de pacote chamado tidyipea e disponibilizei no meu GitHub com uma função chamada codes_ipea, que tem a mesma utilidade da função ipeadatar::available_series, retornando todas os códigos de séries disponíveis na API do IPEA.

No tidyipea também é possível utilizar a função get_ipea apresentada acima. Para instalar o pacote, basta executar:

devtools::install_github("schoulten/tidyipea")

Veja detalhes e informações na página do pacote.

4. Fazer web-scrapping simples

Por fim, também é possível fazer um web-scrapping de modo bem simples. O único requisito é saber o código da série que ser quer obter (você pode obter essa informação através do pacote tidyipea). Vamos a um exemplo com dados do CAGED: primeiro criamos um objeto com o código da série, em seguida fazemos a leitura da página com a função GET do pacote httr. Se estiver tudo ok, basta extrairmos o conteúdo do segundo elemento do objeto raw_data, que é uma lista, e realizar pequenas tratativas para transformar em um tibble.

code <- "GAC12_SALMINRE12"

# Ler página usando web-scrapping
raw_data <- httr::GET(
sprintf("http://ipeadata.gov.br/api/odata4/ValoresSerie(SERCODIGO='%s')", code)
)

# Tratar dados
tbl_data <- httr::content(raw_data)[[2]] %>% 
dplyr::bind_rows() %>% 
dplyr::select("date" = `VALDATA`, "value" = `VALVALOR`) %>% 
dplyr::mutate(date = lubridate::as_date(date))

Assim fica demonstrado diversas alternativas para obtenção dos dados do IPEADATA. A recomendação é sempre utilizar pacotes que estão disponíveis no CRAN, pois os mesmos são robustos entre plataformas e contra erros, além de terem sido testados exaustivamente. No momento atual, os pacotes que faziam esse trabalho de coleta de dados do IPEADATA estão arquivados no CRAN, portanto, recomendamos que utilize as alternativas aqui apresentadas em sequência, conforme melhor atender à necessidade em específico.

Como deflacionar uma série no R

By | Dados Macroeconômicos, Data Science, Economia, Hackeando o R

Algumas pessoas ainda devem se lembrar de quando era possível "fazer o rancho", as compras do mês, com apenas cem reais no bolso, mas já faz bastante tempo que uma simples ida ao supermercado é deveras custosa. O carro popular que podia ser adquirido por menos de R$ 30 mil nos anos 1990 é facilmente vendido por não menos de R$ 50 mil hoje. Ou seja, com uma nota de R$ 100 não se compra a mesma quantidade de bens hoje do que era possível há 20 anos e isso constitui um problema econômico básico, motivo pelo qual devemos deflacionar valores monetários para poder compará-los no tempo.

Sendo assim, se queremos ter uma visão realística quando analisando uma série de preços, devemos transformar os valores nominais para valores deflacionados, ou reais, contabilizando o efeito inflacionário do período. Apesar de ser um procedimento padrão, costumam haver alguns equívocos, de modo que iremos esclarecer alguns pontos e explorar uma abordagem moderna da técnica usando a linguagem R.

Para transformar uma série de preços para valores reais, são necessárias duas coisas: os dados nominais e um índice de preços adequado. A série de dados nominais pode ser qualquer uma que mede algo em valores correntes (R$), como por exemplo a série do salário mínimo, disponibilizada pelo IPEADATA. Já o índice de preços adequado pode vir de diversas fontes, os mais conhecidos no Brasil são o Índice Nacional de Preços ao Consumidor Amplo (IPCA), o Índice Nacional de Preços ao Consumidor (INPC) e o Índice Geral de Preços - Mercado (IGP-M), este divulgado pela FGV e aqueles pelo IBGE. O que esses índices fazem é medir o preço de uma cesta de produtos em um dado período de tempo, geralmente um mês cheio, em relação a um outro período de tempo base.

Com esses dois dados, basta aplicar a fórmula de deflacionamento a seguir:

Onde:

é o valor real, ou deflacionado, no período i na data-base j

 

é o índice de preços fixado na data-base j

 

é o índice de preços no período i

 

é o valor ou preço nominal no período i

 

Com esta fórmula é possível obter o valor deflacionado a partir da multiplicação do valor nominal em um dado período pela razão entre o índice de preços na data-base de referência em relação ao índice no período de análise/interesse. Agora vamos exemplificar com dados para facilitar o entendimento.

Como exemplo, usaremos os dados do salário mínimo do Brasil a partir de 2000 e o INPC para deflacionar a série. Podemos obter esses dados no R com o código a seguir:

# Instalar/carregar pacotes
if(!require("pacman")) install.packages("pacman")
pacman::p_load(
"deflateBR",
"dplyr",
"lubridate",
"httr",
"sidrar",
"purrr",
"ggplot2",
"deflateBR",
"tidyr",
"magrittr"
)

## Coletar dados
# Salário mínimo nominal (código IPEADATA = MTE12_SALMIN12) usando web-scrapping
dados_nominal <- httr::GET(
"http://ipeadata.gov.br/api/odata4/ValoresSerie(SERCODIGO='MTE12_SALMIN12')"
)

# Dados do INPC usando API do Sidra/IBGE
dados_indice <- sidrar::get_sidra(api = "/t/1736/n1/all/v/2289/p/all/d/v2289%2013")

## Tratar dados e filtrar desde o ano 2000
# Salário mínimo nominal
nominal <- httr::content(dados_nominal)[[2]] %>% 
dplyr::bind_rows() %>% 
dplyr::select("date" = `VALDATA`, "nominal" = `VALVALOR`) %>% 
dplyr::mutate(date = lubridate::as_date(date)) %>% 
dplyr::filter(date >= as.Date("2000-01-01"))

# INPC
indice <- dados_indice %>% 
dplyr::select(
"date" = `Mês (Código)`,
"indice" = `Valor`
) %>%
dplyr::mutate(date = lubridate::ym(date))

## Juntar dados
salario_minimo <- dplyr::left_join(nominal, indice, by = "date") %>% 
tidyr::drop_na()

Os dados obtidos podem ser visualizados na tabela abaixo:

Agora vamos supor que queremos saber o salário mínimo real de novembro de 2020 na data-base de abril de 2021. Primeiro calculamos o chamado fator de deflacionamento, que é a fração entre parênteses na fórmula acima, fixando a data-base em abril/2021. O resultado e o cálculo podem ser vistos a seguir:

# Calcular fator de deflacionamento
salario_minimo %<>% 
dplyr::mutate(
fator = (indice[date == "2021-04-01"]/indice)
)

Em seguida, para transformar o valor nominal (R$ 1.045,00) em valor real, basta multiplicá-lo por 1,0384, isto é, R$ 1.045,00 de novembro de 2020 equivale a R$ 1.085,12 em abril de 2021.

Agora que sabemos o procedimento passo a passo do cálculo de deflacionamento, vamos aplicar a fórmula na série do salário mínimo e plotar um gráfico comparativo.

# Calcular salário mínimo real
salario_minimo %<>% 
dplyr::mutate(
real = (indice[date == "2021-04-01"]/indice) * nominal
)

# Visualização de dados
ggplot2::ggplot(salario_minimo, ggplot2::aes(x = date))+
ggplot2::geom_line(
ggplot2::aes(y = nominal, colour = "Salário Mínimo - Nominal"), size = 1.5
) +
ggplot2::geom_line(
ggplot2::aes(y = real, colour = "Salário Mínimo - Real"), size = 1.5
) +
ggplot2::scale_colour_manual(NULL, values = c("#282f6b", "#b22200")) + 
ggplot2::scale_y_continuous(n.breaks = 8) +
ggplot2::labs(
x = NULL,
y = "R$",
title = "Salário Mínimo no Brasil",
subtitle = "Valores em Reais deflacionados pelo INPC a preços de abril/2021.",
caption = "Fonte: analisemacro.com.br com dados de IBGE e IPEA."
) +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(face = "bold"),
plot.subtitle = ggplot2::element_text(face = "italic"),
legend.text = ggplot2::element_text(face = "bold")
)

 

Perceba que, apesar de ser simples e fácil, é um pouco trabalhoso obter os dados do índice, tratar, fazer o cálculo, etc., para enfim obter nossa série deflacionada, mesmo que em nosso exemplo tenhamos "quebrado" o procedimento em etapas adicionais para fins de didática. Felizmente, existe um pacote no R chamado deflateBR que facilita essas tarefas em uma única função, criado pelo Fernando Meireles.

Com a função deflate, tudo que precisamos é apontar a série que queremos deflacionar, o vetor de datas correspondentes, uma data-base e um índice de preços que pode ser: IPCA, IGP-M, IGP-DI, IPC ou INPC. Vamos ao exemplo anterior usando essa função. criaremos uma outra coluna em nosso objeto nomeando-a "deflacionado" para diferenciar dos valores anteriores já calculados:

# Deflacionar usando o pacote deflateBR
salario_minimo %<>% 
dplyr::mutate(
deflacionado = deflateBR::deflate(
nominal_values = nominal,
nominal_dates = date %m+% months(1), # evitar que a função pegue valor com 1 lag
real_date = format(tail(date, 1), "%m/%Y"), 
index = "inpc"
)
)

# Verificar se valores são iguais
all.equal(salario_minimo$real, salario_minimo$deflacionado)

# [1] TRUE

Note que o cálculo foi exatamente o mesmo, mas bastou que indicássemos para a função o que e por qual índice necessitamos deflacionar, muito mais simples e prático.

Por fim, vamos confrontar nossos cálculos com os cálculos do IPEA, haja visto que o mesmo disponibiliza em sua plataforma a série do salário mínimo real. Assim certificamos de que fizemos tudo corretamente:

# Salário mínimo real IPEA (código IPEADATA = GAC12_SALMINRE12) usando web-scrapping
dados_real <- httr::GET(
"http://ipeadata.gov.br/api/odata4/ValoresSerie(SERCODIGO='GAC12_SALMINRE12')"
)

# Salário mínimo real IPEA
real <- httr::content(dados_real)[[2]] %>% 
dplyr::bind_rows() %>% 
dplyr::select("date" = `VALDATA`, "ipea" = `VALVALOR`) %>% 
dplyr::mutate(date = lubridate::as_date(date)) %>% 
dplyr::filter(date >= as.Date("2000-01-01"))

# Juntar dados e arredondar casas decimais
salario_minimo <- left_join(salario_minimo, real, by = "date") %>% 
dplyr::mutate(dplyr::across(real:ipea, ~round(.x, 2)))

# Verificar se valores são iguais
all.equal(salario_minimo$real, salario_minimo$ipea)

# [1] TRUE

Assim, demonstramos duas formas de deflacionar valores usando o R, ambas equivalentes entre si.

 

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

Assinar Gratuitamente
{"cart_token":"","hash":"","cart_data":""}