Como criar gráficos interativos no R

9 minuto(s) de leitura

Durante uma análise de dados, é comum apresentarmos gráficos para mostrar padrões nos dados ou para entender como duas variáveis se relacionam, por exemplo. Podemos fazer um gráfico de barras para comparar os valores de diferentes categorias ou poderíamos fazer um gráfico de linhas para observar a evolução de uma certa variável ao longo do tempo, para ilustrar somente duas situações. Uma ótima opção para fazer gráficos no R é o pacote ggplot2. Aqui nesse blog já mostramos inclusive como utilizar o pacote gganimate para fazer animações desse tipo de gráficos e você pode conferir novamente aqui nesse link. Porém, é interessante em algumas situações passar o mouse por cima dos gráficos para descobrir sobre os valores postados, para complementar a informação que gostaríamos de passar com o gráficos. Alguns exemplos podem ser vistos aqui e aqui também.

Esse tipo de gráfico não pode se valer simplesmente de uma imagem estática, que é a maneira usual que o R gera seus gráficos. Nesse caso, vamos utilizar o pacote highcharter, que cria gráficos interativos utilizando rotinas de javascript por trás de suas funções.

Para começar, vamos carregar alguns pacote de interesse:

library(highcharter)     # fazer gráficos interativos
library(dplyr)           # fazer manipulação de banco de dados

Para ilustrar algumas das capacidades desse pacote, vamos reconsiderar os dados utilizados em um post anterior feito pelo Pedro, sobre os casos do novo coronavirus no Estado do Espírito Santo. Para deixar o post mais curto, nós vamos pular a parte de importação dos dados, pois vocês podem ver todas as instruções no seguinte link. Vamos considerar que o banco de dados estará disponível no objeto dados, assim como no post linkado. Notem que as informações discutidas serão relativas ao dia 16/02/2021, dia em que o post foi escrito.

Uma primeira informação importante sobre o pacote highcharter é que o resultado ou saída dos comandos é um objeto html, exatamente para possibilitar essa interatividade entre usuário e gráfico. Nesse sentido, você deve ter em mente que para apresentar esse gráfico você vai precisar de um navegador ou se seu interesse é compartilhar o resultado com outras pessoas, então é necessário disponibilizar o gráfico em algum site. Vamos mostrar como fazer isso aqui também.

Para começar vamos considerar o número de casos das quatro cidades mais populosas do Espírito Santo e fazer um gráfico de barras para comparar as quantidades de cada um dos municípios. Podemos fazer isso da seguinte maneira:

# Filtrando somente os casos confirmados de 
# Vitória, Vila Velha, Serra e Cariacica 
dados_filtrados <- 
  janitor::clean_names(dados) %>% 
  filter(municipio %in% c('VITORIA', 'VILA VELHA', 'SERRA', 'CARIACICA')) %>% 
  filter(classificacao == "Confirmados")


# E obtendo os valores totais
dados_sumarizados <- dados_filtrados %>% 
  group_by(municipio) %>% 
  summarise(total = n())

grafico <- 
  hchart(dados_sumarizados, type = "column", 
       hcaes(x = municipio, y = total)) %>%
  hc_colors("#440154FF") %>%
  hc_title(text = "Número de casos da COVID-19 no ES") %>%
  hc_subtitle(text = "Considerando somente Grande Vitória") %>%
  hc_xAxis(title = list(text = "")) %>%
  hc_yAxis(title = list(text = "Número de casos")) %>%
  hc_credits(
    enabled = TRUE,
    text = "Fonte: Painel COVID-19 Espírito Santo",
    href = "https://coronavirus.es.gov.br/painel-covid-19-es")
grafico

 

 

 

 

 

 

 

 

 

O gráfico nos mostra, por exemplo, que o número de casos confirmados foi maior nas cidades de Vila Velha e Serra. Note que é possível adicionar link das fontes do gráfico, no canto inferior direito. Perceba que ao passar o cursor do mouse por cima dos gráficos é possível obter a informação das quantidades exatas de casos em cada uma das barras. Uma possibilidade bastante interessante desse pacote é poder utilizar temas com paleta de cores e fontes já definidas, de acordo com algum site ou revista. Por exemplo, é possível utilizar o layout do site do estatístico Nate Silver fivethirtyeight, fazendo simplesmente

grafico %>% 
  hc_add_theme(hc_theme_538())

 

 

 

 

 

 

 

 

 

Outra opção é considerar um tema de lousa com fonte que imita a escrita utilizando giz fazendo

grafico %>%
  hc_add_theme(hc_theme_chalk())

 

 

 

 

 

 

 

 

 

A lista de temas disponíveis é bem grande, com opções bem interessantes. Ao longo do post, vamos sempre utilizar um tema diferentes. Vocês podem ver mais informações sobre isso no link https://jkunst.com/highcharter/articles/themes.html, inclusive comdicas inclusive de como criar o seu próprio tema.

Considerando uma outra variável, como raça/cor ou gênero, poderíamos criar para cada um dos municipios a barra correspodente para cada caso. A princípio de ilustração, vamos retirar os valores de pessoas indígenas, por se tratar de um valor pequeno de observações e também dados para os quais não há informação de raça/cor.

dados_sumarizados_2 <- dados_filtrados %>% 
  filter(raca_cor %in% c("Amarela", "Branca", "Parda", "Preta")) %>% 
  group_by(municipio, raca_cor) %>% 
  summarise(total = n())

grafico2 <- 
  hchart(dados_sumarizados_2, type = "column", 
       hcaes(x = municipio, y = total, group = raca_cor)) %>%
  hc_title(text = "Número de casos da COVID-19 no ES por raça/cor") %>%
  hc_subtitle(text = "Considerando somente Grande Vitória") %>%
  hc_xAxis(title = list(text = "")) %>%
  hc_yAxis(title = list(text = "Número de casos")) %>%
  hc_credits(
    enabled = TRUE,
    text = "Fonte: Painel COVID-19 Espírito Santo",
    href = "https://coronavirus.es.gov.br/painel-covid-19-es") %>% 
  hc_add_theme(hc_theme_elementary())
grafico2

 

 

 

 

 

 

 

 

 

Apenas para Vitória o número de casos entre pessoas brancas e pessoas parada é similar, enquanto nas outras cidades o número de casos de pessoas pardas é sempre maior. Podemos fazer o mesmo gráfico, porém agora considerando o número de mortes.

dados_sumarizados_3 <- dados_filtrados %>% 
  filter(raca_cor %in% c("Amarela", "Branca", "Parda", "Preta")) %>% 
  filter(evolucao == "Óbito pelo COVID-19") %>% 
  group_by(municipio, raca_cor) %>% 
  summarise(total = n())

grafico3 <- 
  hchart(dados_sumarizados_3, type = "column", 
       hcaes(x = municipio, y = total, group = raca_cor)) %>%
  hc_title(text = "Número de mortes da COVID-19 no ES por raça/cor") %>%
  hc_subtitle(text = "Considerando somente Grande Vitória") %>%
  hc_xAxis(title = list(text = "")) %>%
  hc_yAxis(title = list(text = "Número de mortes")) %>%
  hc_credits(
    enabled = TRUE,
    text = "Fonte: Painel COVID-19 Espírito Santo",
    href = "https://coronavirus.es.gov.br/painel-covid-19-es") %>% 
  hc_add_theme(hc_theme_google())
grafico3

 

 

 

 

 

 

 

 

 

Ainda sobre os dados da pandemia, poderíamos estar interessados em fazer gráficos de linha com o número de casos por dia por município e mostrar esses valores lado a lado, para entender a evolução da doença e também para falar sobre 1ª e 2ª onda de casos. Podemos fazer isso da seguinte forma:

# Filtrando somente os casos confirmados de 
# Vitória, Vila Velha, Serra e Cariacica 
# E obtendo os valores totais
dados_sumarizados_4 <- dados_filtrados %>% 
  mutate(data_notificacao = as.Date(data_notificacao)) %>% 
  group_by(municipio, data_notificacao) %>% 
  summarise(total = n())

grafico4 <- 
  hchart(dados_sumarizados_4, type = "line", 
       hcaes(x = data_notificacao, y = total, group = municipio)) %>%
  hc_title(text = "Número de casos pela COVID-19 no ES") %>%
  hc_subtitle(text = "Considerando somente Grande Vitória") %>%
  hc_xAxis(title = list(text = "")) %>%
  hc_yAxis(title = list(text = "Número de casos")) %>%
  hc_credits(
    enabled = TRUE,
    text = "Fonte: Painel COVID-19 Espírito Santo",
    href = "https://coronavirus.es.gov.br/painel-covid-19-es") %>% 
  hc_add_theme(hc_theme_alone())
grafico4

 

 

 

 

 

 

 

 

 

Para facilitar a visualização, poderíamos considerar uma média móvel de 7 dias, fazendo o seguinte.

menor_data <- min(as.Date(dados$DataNotificacao))
maior_data <- max(as.Date(dados$DataNotificacao))
dados_sumarizados_5 <- dados_filtrados %>% 
  mutate(data_notificacao = as.Date(data_notificacao)) %>% 
  group_by(municipio, data_notificacao) %>% 
  summarise(total = n()) %>% 
  tidyr::complete(tidyr::nesting(municipio), 
                  data_notificacao = seq.Date(menor_data, maior_data, 
                                        by = "day"), 
                  fill = list(total = 0)) %>%
  arrange(municipio, data_notificacao) %>% 
  ungroup() %>% 
  mutate(total_menos1 = lag(total), 
         total_menos2 = lag(total, 2), 
         total_menos3 = lag(total, 3),
         total_menos4 = lag(total, 4), 
         total_menos5 = lag(total, 5), 
         total_menos6 = lag(total, 6)) %>% 
  mutate(media_movel = (total + total_menos1 + total_menos2 + total_menos3 + total_menos4 + total_menos5 + total_menos6) / 7) %>% 
  filter(data_notificacao > as.Date("2020-04-01"))

grafico5 <- 
  hchart(dados_sumarizados_5, type = "line", 
       hcaes(x = data_notificacao, y = media_movel, group = municipio)) %>%
  hc_title(text = "Número de casos pela COVID-19 no ES") %>%
  hc_subtitle(text = "Considerando somente Grande Vitória") %>%
  hc_xAxis(title = list(text = "")) %>%
  hc_yAxis(title = list(text = "Número de casos")) %>%
  hc_credits(
    enabled = TRUE,
    text = "Fonte: Painel COVID-19 Espírito Santo",
    href = "https://coronavirus.es.gov.br/painel-covid-19-es") %>% 
  hc_add_theme(hc_theme_superheroes())
grafico5

 

 

 

 

 

 

 

 

 

Veja como para as quatro cidades parece haver dois picos do número de casos. Podemos fazer o mesmo tipo de análise, porém considerando uma forma diferente de visualização. Podemos guardar para cada município o maior valor de número de casos num certo dia. Nesse sentido, podemos verificar em todos os dias da pandemia, o quão próximo estamos desse valor máximo. Isso pode ser visualizado em um gráfico do tipo de tiles (ou azulejos). Veja o exemplo a seguir:

dados_todos <- dados_filtrados %>% 
  mutate(data_notificacao = as.Date(data_notificacao)) %>% 
  group_by(municipio, data_notificacao) %>% 
  summarise(total = n()) %>% 
  tidyr::complete(tidyr::nesting(municipio), 
                  data_notificacao = seq.Date(menor_data, maior_data, 
                                        by = "day"), 
                  fill = list(total = 0)) %>%
  arrange(municipio, data_notificacao) %>% 
  ungroup() %>% 
  mutate(total_menos1 = lag(total), 
         total_menos2 = lag(total, 2), 
         total_menos3 = lag(total, 3),
         total_menos4 = lag(total, 4), 
         total_menos5 = lag(total, 5), 
         total_menos6 = lag(total, 6)) %>% 
  mutate(media_movel = (total + total_menos1 + total_menos2 + total_menos3 +
                            total_menos4 + total_menos5 + total_menos6) / 7) %>%
  group_by(municipio) %>% 
  mutate(perc_total = media_movel/max(media_movel, na.rm = TRUE)) %>% 
  filter(data_notificacao > as.Date("2020-04-01")) %>% 
  mutate(municipio = forcats::fct_drop(municipio))



grafico6 <- hchart(
  dados_todos, 
  "heatmap", 
  hcaes(
    x = data_notificacao,
    y = municipio, 
    value = perc_total
    )
  ) %>%
  hc_colorAxis(
    stops = color_stops(10, viridisLite::inferno(10))) %>%
  hc_yAxis(
    title = list(text = ""),
    reversed = TRUE, 
    offset = -10,
    tickLength = 0,
    gridLineWidth = 0, 
    minorGridLineWidth = 0,
    labels = list(style = list(fontSize = "9px"))
  ) %>%
  hc_xAxis(
    title = list(text = ""), 
    gridLineWidth = 0,
    offset = 20,
    labels = list(style = list(fontSize = "5px"))
  ) %>%
  hc_title(
    text = "Número de casos relativo ao total observado na pandemia"
    ) %>%
  hc_subtitle(
    text = "Número de casos dividido pelo maior valor de número de casos"
  ) %>% 
  hc_legend(
    layout = "horizontal",
    verticalAlign = "top",
    align = "left",
    valueDecimals = 0
  )
grafico6

 

 

 

 

 

 

 

 

 

O gráfico nos mostra que os picos das 4 cidades não ocorreram exatamente nos mesmos momentos, mas que parecem estar relacionados. Não foi possível melhorar o texto do eixo x de forma satisfatória para esse gráfico de heatmap, ao que deixamos dessa forma depois de algumas tentativas frustradas de melhorar o resultado final. Poderíamos fazer o mesmo gráfico para as 30 cidades com maior número de casos no Estado, com resultado final mostrado a seguir.

grafico7

 

 

 

 

 

 

 

 

 

Conforme falamos no início, esses gráficos para ter esse comportamento interativo precisam ser disponibilizados em formato html. Para fazer isso, podemos utilizar outro pacote como o htmlwidgets. Por exemplo, para cada gráfico criado, tendo em vista que salvamos o gráfico em um objeto como grafico, poderíamos fazer o seguinte para gerar um arquivo html com o gráfico gerado:

htmlwidgets::saveWidget(grafico, file = 'grafico.html', 
                        libdir = "aux_files", selfcontained = FALSE)

As opções selfcontained = FALSE e libdir com algum caminho definido ajudam a deixar o arquivo html menor, o que pode ser útil em algumas situações.

O código ao longo deste post não foi organizado da melhor maneira possível. Isto foi feito exatamente para lembrar que existem boas práticas que devem ser seguidas. O post anterior no blog retrata exatamente isto e vocês podem ler este texto clicando aqui.

Este post demonstrou alguns exemplos com o pacote highcharter mas existem muitas outras opções de gráficos e temas que não foram explorados aqui. Terminamos aqui para não estender o post ainda mais. Mas deixamos alguns links com mais exemplos do pacote:

Esperamos que estejam gostando e que continuem acompanhando nossos posts por aqui.

Atualizado em: