Uma recente edição da revista inglesa The Economist exibe uma série de listras coloridas em sua capa. Elas formam um degradê que vai de um azul escuro até um vermelho intenso. Cada listra representa a temperatura de um ano e a linha do tempo vai desde o presente até 1850. A mensagem é bastante clara: o planeta esta cada ano mais quente e é nos anos recentes que estão concentradas as maiores altas de temperatura. Esta imagem é creditada a Ed Hawkings, editor do Climate Lab Book.

Para ser preciso, a imagem não plota a temperatura de cada ano, mas sim o quanto cada ano se desvia da temperatura média do período 1971-2000. Isto é, anos acima dessa média tem um valor positivo, valores abaixo dessa média, valores negativos. Esta é uma forma bastante comum de representar este tipo de dado climático. De imediato, quando vi a imagem me ocorreu que seria bastante simples reproduzir um versão aproximada dela usando o R. Este post - para falar a verdade - surgiu inicialmente neste tweet meu. A ideia é de detalhar melhor o processo para quem ainda não tem muita familiaridade com o R ou o pacote ggplot2.

Recomendo a leitura do meu tutorial de ggplot2 (ainda em construção) para aqueles que tiverem mais interesse.

O código

Primeiro, segue abaixo o código que coloquei no tweet: como se vê, ele é bem enxuto e direto. Primeiro carrego dois pacotes (linhas 1, 2), depois a série de temperatura (linha 3), faço algumas transformações nos dados (linhas 4, 5) e, por fim, ploto os dados (linhas 6, 7, 8). O resultado inicial já é bastante satisfatório e a partir destas poucas linhas de código pode-se chegar num resultado muito próximo ao da imagem original. Vale notar que a imagem fica um pouco diferente da original porque eu uso uma base de dados diferente.

library("ggplot2")
library("astsa")
data("globtemp")
df = data.frame(ano = time(globtemp),
                temp = as.numeric(globtemp))
ggplot(data = df, aes(x = ano, y = 0, fill = temp)) +
  geom_tile() +
  scale_fill_gradient2(low = "#104e8b", mid = "#b9d3ee", high = "#ff0000")
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous.

Os detalhes do código

Vou explicar cada linha de código para ser didático. O R funciona, grosso modo, como um repositório de pacotes: cada pacote contem funções e, às vezes, bases de dados. O primeiro pacote que carrego é o ggplot2. Ele serve para fazer visualizações de dados. O pacote astsa traz várias funções para fazer análise de séries de tempo, mas eu carrego ele somente para usar a base de dados globtemp, que traz informação sobre a temperatura anual da terra coletada pela NASA.

O objeto globtemp é uma série de tempo (um objeto da classe ts), que tem alguns atributos especiais. Um deles pode ser acesado pela função time que extrai um vetor numérico com as datas desta série de tempo. No código abaixo mostro os primeiros dez valores do time(globtemp).

class(globtemp)
## [1] "ts"
time(globtemp)[1:10]
##  [1] 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889

Para extrair somente os valores da série, uso a função as.numeric, que converte o vetor de ts para numeric (numérico). Este tipo de função é bastante comum já que frequentemente é preciso trocar a classe de um objeto. O objetivo destes primeiros passos é de inserir as informações do globtemp num data.frame em que a data aparece na primeira coluna e os valores da série são armazenados na segunda coluna. O procedimento pode parecer um tanto trabalhoso (e acho que é mesmo), mas é o jeito. Um data.frame é como uma tabela com dados. Este é um objeto bastante típico em análise de dados e é necessário para usar a função ggplot que vai fazer o gráfico. Abaixo pode-se ver as primeiras linhas desta tabela.

head(df)
##    ano  temp
## 1 1880 -0.20
## 2 1881 -0.11
## 3 1882 -0.10
## 4 1883 -0.20
## 5 1884 -0.28
## 6 1885 -0.31

Agora que tenho os dados no formato apropriado posso usar o ggplot. O argumento que pode ser um pouco confuso é o aes. Nele especifica-se quais dados serão mapeados no gráfico. Depois disso adicionamos um geom. Há vários tipos de geom (geom_line, geom_bar, geom_histogram, etc.) e cada um deles produz uma imagem diferente. O geom_tile faz um pequeno quadrado. Para que a função consiga desenhar o quadrado é preciso informar uma variável x e uma variável y. Além disso, também especifico fill = temp. O fill se refere à cor que vai preencher (fill) o quadrado. Como especifico fill = temp a cor do quadrado vai representar a variável temp (temperatura).

ggplot(data = df, aes(x = ano, y = 0, fill = temp)) +
  geom_tile()
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous.

O resultado é exatamente como o esperado, mas ainda é preciso mudar a escala de cores. Faço isto com o scale_fill_gradient2. Aqui cada termo tem um signficado: scale_fill pois estamos mudando a escala do fill (outra opção seria scale_color que muda a escala do color). scale_fill_gradient pois queremos um gradiente (degradê) de cores. Por fim, o 2 é adicionado no final pois queremos um escala que diferencie dois grupos distintos: temperaturas acima da média em vermelho, temperaturas abaixo da média em azul. A escala de cores é determinada pelos argumentos low, mid e high. Os valores negativos serão coloridos pelo low, os próximos de zero pelo mid e os valores grandes pelo high. Abaixo escrevo as cores em hexa-decimal, mas elas podem ser lidas, essencialmente, como: azul-escuro, cinza-azulado-claro e vermelho-escuro.

ggplot(data = df, aes(x = ano, y = 0, fill = temp)) +
  geom_tile() +
  scale_fill_gradient2(low = "#104e8b", mid = "#b9d3ee", high = "#ff0000")
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous.

Como comentei acima, pode-se melhorar o gráfico acima adicionando outros elementos e detalhes. A versão final que fiz do gráfico fica no código abaixo.

library("scales")
library("extrafont")
df_aux_title = data.frame(x = 1930, y = 0, label = "The Climate Issue")
df_aux_anos = data.frame(label = c(1880, 1920, 1960, 2000),
                       x = c(1888, 1925, 1960, 1995))
ggplot() +
  geom_tile(data = df, aes(x = ano, y = 0, fill = temp)) +
  geom_text(data = df_aux_anos,
            aes(x = x, y = 0, label = label),
            vjust = 1.5,
            colour = "white",
            size = 6,
            family = "Georgia") +
  geom_text(data = df_aux_title,
            aes(x = 1950, y = 0.05, label = label),
            family = "Georgia",
            size = 11,
            colour = "white") +
  geom_hline(yintercept = 0, colour = "white", size = 1) +
  scale_fill_gradientn(
    colours = c("#104e8b", "#c6e2ff", "#FFFFFF", "#FEE0D2", "#ff0000"), 
    values = rescale(c(-1, 0-.Machine$double.eps, 0, 0+.Machine$double.eps, 1)))+
  guides(fill = FALSE) +
  labs(x = "", y = "") +
  theme(axis.title = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_rect(fill = NA),
        plot.margin = margin(c(0, 0, 0, 0)))
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous.