11.6 ggplot2

O ggplot2 é uma implementação para o da Gramática de Gráficos (Wilkinson et al. 2005) (GG). A GG estabelece princípios fundamentais para a construção de gráficos. Um gráfico consiste no mapeamento dos dados a partir de atributos estéticos (posição, cor, forma, tamanho) de objetos geométricos (pontos, linhas, barras, caixas). Os principais aspectos de um gráfico são os dados, o sistema de coordenadas, os rótulos e as anotações, os quais podem ser combinados em camadas para elaboração do gráfico. Esse é a ideia central do {ggplot2}.

A documentação do ggplot2 está disponível aqui.

Para conhecer mais extensões do {ggplot2} consulte a galeria https://exts.ggplot2.tidyverse.org/gallery/.

11.6.1 Exemplo de aplicação

11.6.2 Dados

Como exemplo de aplicação usaremos os dados históricos do INMET da estação meteorológica convencional (EMC) de Santa Maria-RS. Os dados utilizados podem ser acessados carregando o pacote {ADARdata}.

#devtools::install_github("lhmet/ADARdata")
library(ADARdata)

O pacote disponibiliza os seguinte conjunto de dados formado por 3 quadro de dados que serão usados a seguir:

  • clima_sm contém estatísticas e as observações históricas de temperatura do ar (tar) e precipitação diária (prec) para cada dia do ano em Santa Maria-RS

  • tempo_sm contém os dados meteorológicos do último ano

  • recordes_atual_sm contém os recordes de temperatura e precipitação do último ano

Você pode obter mais informações sobre cada um dos quadro de dados consultado o help.

?dados_sm

Para construção do gráfico precisaremos das posições dos dia 15 e do último dia de cada mês. Esta informação será útil para especificar onde colocar os nomes dos meses no eixo x. Por isso vamos criar abaixo um tibble com o dia do ano correspondente aquelas datas.

ultimo_ano <- unique(year(tempo_sm$date))
datas_atual <- seq(
  from = as.Date(paste0(ultimo_ano, '-01-01')), 
  to = as.Date(paste0(ultimo_ano, '-12-31')), 
  by = "day"
)

library(scales)

# dda do meio do mês
meio_mes <- yday(datas_atual)[day(datas_atual) == 15]

# dda do final do mês
fim_mes <- tibble(datas_atual, 
       mes = month(datas_atual), 
       day = day(datas_atual),
       yday = yday(datas_atual)) %>%
  group_by(mes) %>%
  summarise(ult_dia = yday[which.max(day)], .groups = "drop") %>%
  pull(ult_dia)

# junção em tibble
marcas_x <- tibble(
  metade = meio_mes,
  final = fim_mes,
  labels = c(
    "Janeiro", "Fevereiro", "Março", "Abril",
    "Maio", "Junho", "Julho", "Agosto", "Setembro",
    "Outubro", "Novembro", "Dezembro"
  )
)
marcas_x
#> # A tibble: 12 x 3
#>    metade final labels   
#>     <dbl> <dbl> <chr>    
#>  1     15    31 Janeiro  
#>  2     46    60 Fevereiro
#>  3     75    91 Março    
#>  4    106   121 Abril    
#>  5    136   152 Maio     
#>  6    167   182 Junho    
#>  7    197   213 Julho    
#>  8    228   244 Agosto   
#>  9    259   274 Setembro 
#> 10    289   305 Outubro  
#> 11    320   335 Novembro 
#> 12    350   366 Dezembro

PAREI AQUI

11.6.3 Gráfico do tempo de Santa Maria-RS em 2020

Para ilustrar a abordagem de construção de gráficos por camadas do {ggplot2} vamos visualizar a variação temperatura diária do ar para o ano corrente e compará-la com a climatologia e estatísticas baseadas em 58 anos de dados. Nós vamos explorar diferentes geometrias (linhas, pontos, intervalos)

Nós criamos um gráfico a partir da função ggplot(), especificando os dados de entrada.

ggplot(data = clima_sm)

O resultado é um painel em branco. A construção de um gráfico com o {ggplot2} envolve a especificação de atributos estéticos (eixos x e y), adição de elementos geométricos aos nossos dados, operações estatísticas, escalas, coordenadas e várias utras componentes.

Podemos adicionar geometrias usando o operador +, conforme mostrado abaixo. Para criar o gráfico da temperatura do ar (tar) de Santa Maria-RS, precisamos mapear as variáveis de interesse dos dados de entrada para o eixo y e x.

O mapeamento das variáveis nos eixos é feito pela função aes() (de aesthetics, estética em inglês). Ela indica a relação entre os dados, a variável que será representada no eixo x, a que será representada no eixo y, a cor, o tamanho dos componentes geométricos etc.

Os aspectos que podem ou devem ser mapeados dependem do tipo de gráfico que você está construindo.

Nossa 1ª camada de geometria será uma linha vertical que se estende dos valores mínimo até o máximo absoluto de tar (variáveis tar_min e tar_max) registrado no dia do ano (dda).

O dda é especificado no eixo x. Para inserir uma linha vertical a geom_linerange() requer os argumentos x, ymin e ymax.

ggplot(data = clima_sm) +
  geom_linerange(
    mapping = aes(x = dda, ymin = tar_min, ymax = tar_max),
    color = "wheat2"
   # alpha = .1
  ) 

Além de identificar as variáveis de cada eixo nós adicionamos o argumento color para distinguir os intervalos variação entre os máximos e mínimos absolutos registrados.

Uma versatilidade do {ggplot2} é de podermos armazenar os gráficos em objetos. Então o gráfico básico acima pode ser armazenado numa váriavel. A estrutura do gráfico é uma lista com todas componentes necessárias para construção do gráfico.

graf_base <- 
  ggplot( data = clima_sm) +
  #geom_point(size = 0.01, colour = "green") +
  geom_linerange(
    mapping = aes(x = dda, ymin = tar_min, ymax = tar_max),
    color = "wheat2"
   # alpha = .1
  ) +
   theme_void()

#glimpse(graf_base, max.level = 1)
graf_base


 # ggplot( data = clima_sm) +
 #  #geom_point(size = 0.01, colour = "green") +
 #  geom_linerange(
 #    mapping = aes(x = dda, ymin = tar_min, ymax = tar_max),
 #    color = "wheat2"
 #   # alpha = .1
 #  ) +
 #   #theme_void()
 #   theme(#plot.background = element_blank(),
 #          panel.grid.minor.x = element_blank(),
 #          panel.grid.major.y = element_blank(),
 #          panel.border = element_blank(),
 #          panel.background = element_blank(),
 #          axis.ticks = element_blank(),
 #          #axis.text = element_blank(),  
 #          axis.title = element_blank()
 #  )

Na sequência vamos adicionar uma segunda camada com os dados que representam o intervalo de confiança de 95% da temperatura média diária para o período de 1952-2019.

ggp_sm <- graf_base + 
geom_linerange(
    mapping = aes(x = dda, ymin = tar_med_min, ymax = tar_med_max),
    color = "wheat4"
  )
ggp_sm

Agora vamos incorporar ao gráfico os dados de temperatura do ano atual e uma linha vertical (geom_vline) na borda esquerda do eixo y.

ggp_sm <- ggp_sm +
  geom_line(
    data = tempo_sm,
    mapping = aes(x = dda, y = tar),
    # size = 1
  ) +
  geom_vline(
    xintercept = 0,
    color = "wheat4",
    linetype = 1,
    size = 1
  )
ggp_sm

Agora vamos ajustar a escala do eixo y ao intervalo de variação da dos extremos e definir 8 marcas para os labels.

int_var <- range(c(clima_sm$tar_max, clima_sm$tar_min))
int_var <- c(floor(int_var[1]), ceiling(int_var[2]))
int_var
#> [1]  3 35
ggp_sm <- ggp_sm +
  scale_y_continuous(
    limits = int_var,
    breaks = scales::pretty_breaks(n = 8),
    labels = scales::label_number(suffix = " °C", accuracy = 5)
  ) + 
  scale_x_continuous(expand = c(0, 0), 
                     breaks = marcas_x$metade, 
                     labels = marcas_x$labels,
                     )
ggp_sm

Podemos adicionar linhas de grade horizontais e verticais como referência. As linhas verticais serão adicionadas no último dia de cada mês. As horizontais serão espaçadas de 5°C.

ggp_sm <- ggp_sm + 
  geom_hline(
    yintercept = seq(int_var[1], int_var[2], by = 5), 
    color = "white", 
    linetype = 1
    ) + 
  geom_vline(
    xintercept = marcas_x$final, 
    color = "wheat4", 
    linetype = 3,
    size = 0.5
    ) 
ggp_sm

Neste ponto, vamos identificar os dias que em 2020 ultrapassaram os recordes históricos de temperatura.

ggp_sm <- ggp_sm + 
   geom_point(
     data = filter(recordes_atual_sm, record_min == "S"),
     aes(x = dda, y = tar), color = "blue3") + 
   geom_point(
     data = filter(recordes_atual_sm, record_max == "S"),
     aes(x = dda, y = tar), color = "firebrick3")
ggp_sm

Com todos dados plotados agora podemos incrementá-lo com texto apropriado. Primeiramente vamos adicionar um título e subtítulo.

ggp_sm <- ggp_sm +
  ggtitle("Tempo em Santa Maria, 2020") +
  theme(
    plot.title = element_text(
      face = "bold",
      hjust = .012,
      vjust = .8,
      colour = "#3C3C3C",
      size = 20
    )
  ) +
  annotate("text",
    x = 138,
    y = 36,
    label = "Temperatura",
    size = 4,
    fontface = "bold"
  ) 
ggp_sm
#> Warning: Removed 1 rows containing missing values (geom_text).

Nós podemos adicionar um parágrafo abaixo do subtítulo para dar uma pequena explanação dobre os dados. O texto será separado em 4 anotações.

texto <- "Os dados representam as médias diárias de temperatura. Os dados iniciam efetivamente em 1951. Dados para 2020 são disponíveis somente até Julho. A Temperatura média anual até este mês foi de 22°C, caracterizando 2020 como o 8° ano mais quente."

ggp_sm <- ggp_sm +
  annotate("text",
    x = 120,
    y = 32.5,
    label = str_wrap(texto, width = 70),
    size = 3,
    colour = "gray30",
    hjust = 0
  )
ggp_sm
#> Warning: Removed 1 rows containing missing values (geom_text).

Anotações que explicam os pontos representando os dias nos quais ocorreram recordes de temperatura máxima e mínima.

x_rec_tmin <- filter(recordes_atual_sm, record_min == "S") %>% 
  pull(dda)
y_rec_tmin <- filter(recordes_atual_sm, record_min == "S") %>% 
  pull(tar_min)

x_rec_tmax <- filter(recordes_atual_sm, record_max == "S") %>% 
  pull(dda) %>% 
  nth(3)
y_rec_tmax <- filter(recordes_atual_sm, record_max == "S") %>% 
  pull(tar_max) %>%
  nth(3)


ggp_sm <- ggp_sm +
  annotate("segment",
           x = x_rec_tmin - 2, xend = x_rec_tmin - 12,
           y = y_rec_tmin - 1, yend = y_rec_tmin - 3,
           color = "blue3"
           ) + 
  annotate("text", 
           x = x_rec_tmin - 12, 
           y = y_rec_tmin - 3 -0.5, 
           label = "Recorde de Tmin",
           size=2.9, 
           colour="blue3"
           ) +
    annotate("segment",
           x = x_rec_tmax + 2, xend = x_rec_tmax + 12,
           y = y_rec_tmax + 1, yend = y_rec_tmax + 2,
           color = "firebrick3"
           ) + 
  annotate("text", 
           x = x_rec_tmax + 13, 
           y = y_rec_tmax + 3, 
           label = "Recordes de Tmax",
           size = 2.9, 
           colour="firebrick3"
           )
ggp_sm
#> Warning: Removed 1 rows containing missing values (geom_text).

leg_dados <- data.frame(x = seq(53, 61), y = rnorm(9, mean = 12, 1))

ggp_sm +
  # limites extremos
  annotate("segment",
    x = marcas_x$final[2],
    xend = marcas_x$final[2],
    y = 9,
    yend = 15,
    colour = "wheat2",
    size = 3
  ) +
  # intervalo intermediário
  annotate("segment",
    x = marcas_x$final[2],
    xend = marcas_x$final[2],
    y = 10.5,
    yend = 13.5,
    colour = "wheat4",
    size = 3
  ) +
  geom_line(data = leg_dados, aes(x = x, y = y)) +
  # labels intermediários
  annotate("text",
    x = 36,
    y = 12,
    label = "TEMPERATURA 2020",
    size = 2,
    colour = "gray30"
  ) +
  annotate("text",
    x = 84,
    y = 12,
    label = "INTERVALO NORMAL",
    size = 2,
    colour = "gray30"
  ) +
  # labels extremos
  annotate("text",
    x = 70,
    y = 15,
    label = "MÁXIMO",
    size = 2,
    colour = "gray30"
  ) +
  annotate("text",
    x = 70,
    y = 9.5,
    label = "MÌNIMO",
    size = 2,
    colour = "gray30"
  ) +
  # segmentos do intervalo normal
  annotate("segment", 
           x = 63, 
           xend = 65, 
           y = 10.5,
           yend = 10.5,
           colour = "wheat4", 
           size=.5
           ) +
  annotate("segment", 
           x = 63, 
           xend = 65, 
           y = 13.5,
           yend = 13.5,
           colour = "wheat4", 
           size=.5
           ) +
  annotate("segment", 
           x = 65, 
           xend = 65, 
           y = 10.5,
           yend = 13.5,
           colour = "wheat4", 
           size=.5
           )
#> Warning: Removed 1 rows containing missing values (geom_text).

11.6.4 Histograma e colunas

11.6.5 Imagem 2D

11.6.6 Facetas