Tag

output shiny Archives - Análise Macro

Criando uma dashboard de análise da inflação

By | Data Science

Quando o objetivo é analisar dados, é necessário utilizar as ferramentas adequadas para tornar os dados brutos em informação que seja útil. Para tal objetivo, uma dashboard pode ser o formato mais conveniente, dado seu poder de customização, compartilhamento e automatização. Nesse contexto, exploramos como exemplo a construção de uma dashboard simples aplicada à análise dos dados de inflação do Brasil, fazendo uso dos principais pacotes do R.

Visão geral

O objetivo geral é construir uma dashboard dinâmica onde seja possível analisar o comportamento histórico dos principais indicadores para acompanhamento de conjuntura do tema inflação, tais como: IPCA, IGP-M, INPC, etc. Além disso, para dar mais autonomia ao usuário final da dashboard, a mesma contará com opções de filtros de datas, indicadores, medidas, etc., tornando possível uma análise mais customizável.

Em resumo, a dashboard terá a seguinte aparência:

Você também pode conferir o resultado completo neste link.

Pacotes e framework

Para construir a dashboard você precisará dos seguintes pacotes disponíveis em sua instalação de R:


# Carregar pacotes
library(shiny)
library(ggplot2)
library(readr)
library(lubridate)
library(dplyr)
library(stringr)
library(forcats)
library(tidyr)
library(scales)
library(ggtext)
library(tsibble)
library(fabletools)
library(feasts)
library(Hmisc)
library(rmarkdown)
library(flexdashboard)

Optou-se por utilizar o framework dos pacotes shiny e flexdashboard, que oferecem uma sistemática de programação reativa e sintaxe simples e amigável, respectivamente, tornando o processo de criação de dashboards dinâmicas mais fácil. Além disso, a dashboard foi hospedada no serviço shinyapps.io e automatizada usando o GitHub Actions (confira neste link um tutorial de uso).

Para aprofundamento e detalhes confira o Curso de Análise de Conjuntura usando o R.

Criando a dashboard

O primeiro passo é criar um projeto do RStudio, para isso use usethis::create_project("nome_do_projeto").

Em seguida, criamos o arquivo principal da dashboard utilizando o template básico oferecido pelo pacote flexdashboard: basta navegar pelos menus File > New File > R Markdown > From Template > Flex Dashboard {flexdashboard} > OK e salvar o arquivo .Rmd na raiz do projeto.

No arquivo editamos os metadados com as definições desejadas para a dashboard, conforme abaixo:


---
title: "Diagnóstico da Inflação"
output:
flexdashboard::flex_dashboard:
orientation: rows
source_code: NULL
social: menu
navbar:
- { icon: "fa-github", href: "https://ENDEREÇO_DO_SITE/", align: right }
- { icon: "fa-linkedin", href: "https://ENDEREÇO_DO_SITE/", align: right }
- { icon: "fa-at", href: "mailto:MEU@EMAIL.COM", align: right }
runtime: shiny
---

Agora podemos começar a trabalhar nos dados e elementos visuais da dashboard. No primeiro chunk do documento (global) especificamos os pacotes, carregamos os dados e definimos objetos úteis a serem utilizados nos gráficos.

Os dados ficam salvos em uma pasta chamada "data" e são atualizados automaticamente por um script independente, visando diminuir dependências e tempo de carregamento da dashboard. Neste link você pode baixar os dados para poder reproduzir o exemplo.


# Carregar pacotes
library(shiny)
library(ggplot2)
library(readr)
library(lubridate)
library(dplyr)
library(stringr)
library(forcats)
library(tidyr)
library(scales)
library(ggtext)
library(tsibble)
library(fabletools)
library(feasts)
library(Hmisc)

# Carregar dados públicos previamente importados via pacotes
tbl_inflation <- readr::read_rds("data/tbl_inflation.rds")

# Objetos úteis na dashboard
colors <- c(
blue = "#282f6b",
red = "#b22200",
yellow = "#eace3f",
green = "#224f20",
purple = "#5f487c",
black = "black"
)

Em seguida, criamos uma linha usando cabeçalho Markdown de nível dois (------------------) e determinamos que esse elemento seja a sidebar da dashboard. Essa barra lateral é, usualmente, onde são exibidos os filtros e opções de interatividade, além de poder servir de espaço para textos informativos. No nosso exemplo, colocamos um texto breve e definimos, conforme chunk abaixo, os inputs que criamos através do shiny para aplicar filtros e manipulações de dados:


# Cria input do tipo "lista de caixas de seleção" com índices de preços como opções
# Objetivo: usuário pode escolher quaL indicador será exibido no gráfico
shiny::selectInput(
inputId = "variavel",
label = shiny::strong("Indicador:"),
choices = unique(tbl_inflation$variable),
selected = unique(tbl_inflation$variable)[1],
multiple = FALSE
)

# Cria input do tipo "calendário" de seleção de data de início e fim
# Objetivo: usar as datas selecionadas para filtrar amostra de dados utilizada no gráfico/cálculos
shiny::dateRangeInput(
inputId = "data",
label = shiny::strong("Data inicial e final:"),
min = min(tbl_inflation$date),
max = max(tbl_inflation$date),
start = min(tbl_inflation$date),
end = max(tbl_inflation$date),
language = "pt-BR",
separator = " - ",
format = "mm/yyyy"
)

# Cria input do tipo "campo numérico" para entrada de um ano para comparação
# Objetivo: comparar medidas (mediana e IQR) com dados observados referente ao ano
shiny::numericInput(
inputId = "ano",
label = shiny::strong("Comparar com o ano:"),
value = lubridate::year(max(tbl_inflation$date)),
min = lubridate::year(min(tbl_inflation$date)),
max = lubridate::year(max(tbl_inflation$date)),
step = 1
)

# Tratamento para atualizar o ano pré-selecionado no input$ano em resposta a uma
# mudança da amostra de dados definida pelo usuário no input$data:
# o objetivo é que quando o usuário diminui a amostra de dados, o ano de comparação
# selecionado não fique fora dessa nova amostra e seja atualizado para um novo
# valor o mais próximo possível dos valores extremos (anos) da nova amostra
shiny::observeEvent(
eventExpr = input$data, # observa mudanças do usuário na amostra de dados
handlerExpr = { # expressões que serão executadas quando input$data mudar

data_inicial <- lubridate::year(input$data[1])
data_final <- lubridate::year(input$data[2])

shiny::updateNumericInput( # atualiza o valor de input$ano quando a mudança é detectada
inputId = "ano",
value = if(!input$ano %in% data_inicial:data_final & data_inicial > input$ano){
data_inicial
} else
if(!input$ano %in% data_inicial:data_final & data_final < input$ano){
data_final
} else input$ano,
min = data_inicial,
max = data_final,
step = 1
)

}
)

# Cria input do tipo "lista de caixas de seleção" com componentes para filtragem
shiny::checkboxGroupInput(
inputId = "componentes",
label = shiny::strong("Componentes:"),
choices = c("% a.m.", "Tendência", "Sazonalidade", "Média"),
selected = c("% a.m.", "Tendência", "Média")
)

Uma vez definidos os inputs, passamos à construção dos outputs que serão dois gráficos neste caso. Para tal, criamos mais duas linhas: a primeira responsável por criar o gráfico dinâmico que compara a sazonalidade dos dados históricos com um determinado ano, especificado pelo usuário; e a segunda é responsável pelo gráfico dinâmico que mostra alguns componentes da série escolhida.

Veja, por exemplo, que o primeiro gráfico é afetado por mudanças no input referente ao filtro de datas, definição de ano de comparação e o indicador escolhido, sendo assim o script exige essas entradas/definições, utiliza esses valores armazenados para tratamento e cálculos nos dados, e, por fim, gera o gráfico de ggplot2 dinamicamente:


# Gerar gráfico dinâmico (se atualiza conforme o input da sidebar)
shiny::renderPlot({

# Use a função req para exigir que os valores do inputs sejam informados pelo usuário,
# isso evita que o R execute o script "ansiosamente"
shiny::req(
input$data,
input$ano %in% lubridate::year(input$data[1]):lubridate::year(input$data[2]),
input$variavel
)


# Valores dos inputs salvos em objetos auxiliar, por conveniência
data_inicial <- lubridate::floor_date(input$data[1])
data_final <- lubridate::floor_date(input$data[2])
data_compara <- input$ano
indicador <- input$variavel


# Script para calcular padrão sazonal mensal conforme inputs do usuário: mediana e IQR
seas_pattern <- tbl_inflation %>%
dplyr::group_by(
variable,
date_m = lubridate::month(.data$date, label = TRUE, locale = "pt_BR.utf8") %>%
stringr::str_to_sentence(locale = "br") %>%
forcats::as_factor()
) %>%
dplyr::filter(date >= data_inicial & date <= data_final) %>%
dplyr::summarise(
iqr = ggplot2::median_hilow(mom, conf.int = 0.5),
.groups = "drop"
) %>%
tidyr::unnest(cols = iqr) %>%
dplyr::rename("median" = "y", "date" = "date_m") %>%
dplyr::left_join(
tbl_inflation %>%
dplyr::filter(
date >= data_inicial & date <= data_final,
lubridate::year(.data$date) == data_compara
) %>%
dplyr::mutate(
date = lubridate::month(.data$date, label = TRUE, locale = "pt_BR.utf8") %>%
stringr::str_to_sentence(locale = "br") %>%
forcats::as_factor()
),
by = c("variable", "date")
) %>%
tidyr::pivot_longer(
cols = -c(variable, date, ymin, ymax),
names_to = "measure",
values_to = "value"
) %>%
dplyr::mutate(
measure = dplyr::recode(
measure,
"median" = "Mediana",
"mom" = as.character(data_compara)
)
) %>%
dplyr::filter(variable == indicador)


# Gerar gráfico dinâmico
seas_pattern %>%
ggplot2::ggplot() +
ggplot2::aes(x = date, y = value, color = measure, shape = measure, group = measure) +
ggplot2::geom_hline(yintercept = 0) +
ggplot2::geom_ribbon(
ggplot2::aes(
ymin = ymin,
ymax = ymax,
fill = "IQR (CI = 0,5)"
),
alpha = 0.2,
color = NA
) +
ggplot2::geom_line(size = 1.2) +
ggplot2::geom_point(size = 3) +
ggplot2::scale_color_manual(
NULL,
values = c(unname(colors["red"]), unname(colors["black"])),
guide = ggplot2::guide_legend(
override.aes = list(
shape = c(16, NA)
)
)
) +
ggplot2::scale_fill_manual(
NULL,
values = c("IQR (CI = 0,5)" = unname(colors["black"]))
) +
ggplot2::scale_shape_manual(
NULL,
values = c(16, NA)
) +
ggplot2::scale_y_continuous(
breaks = scales::extended_breaks(n = 6),
labels = scales::label_number(decimal.mark = ",", accuracy = 0.01),
minor_breaks = NULL
) +
ggplot2::labs(
title = paste0("**", indicador, "**: padrão sazonal"),
subtitle = paste0(
"% a.m., ",
paste0(
lubridate::year(data_inicial),
"m",
ifelse(
lubridate::month(data_inicial) < 10,
paste0("0", lubridate::month(data_inicial)),
lubridate::month(data_inicial)
),
"-",
lubridate::year(data_final),
"m",
ifelse(
lubridate::month(data_final) < 10,
paste0("0", lubridate::month(data_final)),
lubridate::month(data_final)
)
)
),
x = NULL,
y = NULL,
caption = "**Dados:** FGV e IBGE | **Elaboração:** Fernando da Silva"
) +
ggplot2::theme_light() +
ggplot2::theme(
legend.position = "bottom",
legend.key = ggplot2::element_blank(),
legend.key.width = ggplot2::unit(1, "cm"),
legend.key.height = ggplot2::unit(0.5, "cm"),
legend.text = ggplot2::element_text(size = 12),
plot.title = ggtext::element_markdown(size = 30, colour = colors["blue"]),
plot.subtitle = ggplot2::element_text(size = 16),
plot.caption = ggtext::element_markdown(size = 12),
axis.text = ggplot2::element_text(size = 12),
strip.background = ggplot2::element_blank(),
strip.text = ggplot2::element_text(size = 12, face = "bold", colour = colors["black"])
)

})

Após gerar os elementos visuais, utilize o botão Run Document no RStudio para visualizar o resultado, ou seja, a dashboard propriamente dita.

Os últimos passos envolvem fazer o deploy da dashboard e automatizar a coleta inicial de dados. Consulte as referências inicias para orientação sobre o assunto.

___________________

(*) Para aprofundamento e detalhes confira o Curso de Análise de Conjuntura usando o R.

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

Assinar Gratuitamente