Já faz alguns dias, durante uma conversa casual em que expunha minha insatisfação em relação às dificuldades para se encontrar dados abertos em formato acessível através de sites de órgãos públicos, fiquei sabendo da existência de alguns portais do governo que, apesar de ainda não atenderem a todas as minhas expectativas, já ajudam bastante. O primeiro é o SAGE (Sala de Apoio à Gestão Estratégica/MS). O segundo é o Portal Brasileiro de Dados Abertos.

Um dado interessante que o SAGE possui é o número de casos notificados, em investigação, confirmados e descartados de microcefalia e/ou alterações do sistema nervoso central (SNC), sugestivos de infecção congênita por zika vírus em fetos, abortamentos, natimortos ou recém-nascidos.

O gráfico original disponível no site é este aqui (clique para ver em tamanho maior).

Para encontrá-lo clique na aba ‘Situação de Saúde’ e depois em ‘Microcefalia e Zika’ no menu que irá aparecer.

Pensei em reproduzir esse gráfico tanto em R como em Python pra que vocês possam aprender um tipo de gráfico que tem algumas peculiaridades e não é tão fácil de fazer, além de poder comparar as duas linguagens ao realizarem a mesma tarefa. Deixo claro aqui que a linguagem que uso há mais tempo é o R (que também é minha favorita! hehe). Comecei a aprender Python este ano depois de muita resistência, então é possível que, de repente, o código do Python ainda possa ser otimizado. Você pode baixar o script em R utilizado para esse post aqui. Para saber qual o tamanho real do código necessário para fazer este gráfico olhe o script.

Antes de mergulhar no post… você precisa saber que o que será tratado aqui contém conteúdo de nível intermediário-avançado em R. Portanto, se você nunca viu um código R antes ou se ainda está nos primeiros passos na linguagem, minha recomendação é que você não leia esse post, caso contrário irá se asssutar! hahaha Nesse caso, considere se inscrever no meu curso de introdução à programação com R. Se ainda assim você estiver curioso, pode ir em frente, mas eu avisei! Se você já tem fluência no R e deseja aprimorar suas habilidades com base graphics, esse post é para você!

Mais pra frente divulgarei uma outra versão deste post feita com o pacote ggplot2 também.

Bom, vamos ao trabalho.

A primeira coisa que devemos fazer é baixar o arquivo contendo os dados. Baixe o arquivo ‘file.csv’ clicando na engrenagem e depois no botão indicado.

engrenagem.png

engrenagem2.png

Lendo o arquivo

Iniciamos lendo o arquivo utilizando a função read.csv() estipulando o diretório e o nome do arquivo na máquina. O argumento stringsasfactors=FALSE tem o fim de evitar que colunas contendo caracteres sejam convertidas automaticamente em fatores. Se tudo correr bem você terá carregado os dados em formato de dataframe.

# Lendo o arquivo csv (renomeei o arquivo)
zika <- read.csv('/data/sage_ms_zika_casos_2016.csv',
                 stringsAsFactors = FALSE)

Nessa etapa vale muito usar as funções str(), head(), tail() e is.na() para detectar qualquer problema com os dados e eventuais necessidades de limpeza de dados ou pré-processamento (data cleaning e data wrangling). Não cobrirei aqui o uso dessas funções, mas veja que usando str() podemos ver que o nosso dataframe é composto de 44 observações (linhas) e 5 variáveis (colunas.). Perceba que a primeira coluna é composta de caracteres (strings) e as outras colunas contém o número de casos registrados (notificados, em investigação, confirmados e descartados), ou seja, são valores numéricos inteiros (integers).

# Estrutura dos dados
str(zika)
## 'data.frame':	44 obs. of  5 variables:
##  $ Semana           : chr  "06-2016" "07-2016" "08-2016" "09-2016" ...
##  $ Casos.Notificados: int  5280 5639 5909 6158 6480 6671 6776 6906 7015 7150 ...
##  $ Em.Investigação  : int  3935 4106 4222 4231 4268 4293 4291 4046 3836 3741 ...
##  $ Casos.Confirmados: int  508 583 641 745 863 907 944 1046 1113 1168 ...
##  $ Casos.Descartados: int  837 950 1046 1182 1349 1471 1541 1814 2066 2241 ...
# Primeiros 6 valores de cada coluna
head(zika)
##    Semana Casos.Notificados Em.Investigação Casos.Confirmados
## 1 06-2016              5280            3935               508
## 2 07-2016              5639            4106               583
## 3 08-2016              5909            4222               641
## 4 09-2016              6158            4231               745
## 5 10-2016              6480            4268               863
## 6 11-2016              6671            4293               907
##   Casos.Descartados
## 1               837
## 2               950
## 3              1046
## 4              1182
## 5              1349
## 6              1471

De dataframe a matriz e a função barplot()

Uma das coisas que mais causa dor de cabeça quando se tenta fazer um gráfico de barras no R é que, como no nosso caso, estamos trabalhando com dataframes e acabamos tentando aplicar a função barplot() a um objeto deste tipo e obtemos um erro chato como resposta. No entanto, essa função aceita apenas vetores ou matrizes como argumentos. Sendo assim, vamos copiar as colunas com valores numéricos do nosso dataframe para uma matriz.

# Convertendo o dataframe em matriz, excluindo a primeira coluna (caracteres)
zika_matrix <- as.matrix(zika[,-1])

# Primeiros 6 valores de cada coluna da matriz
head(zika_matrix)
##      Casos.Notificados Em.Investigação Casos.Confirmados Casos.Descartados
## [1,]              5280            3935               508               837
## [2,]              5639            4106               583               950
## [3,]              5909            4222               641              1046
## [4,]              6158            4231               745              1182
## [5,]              6480            4268               863              1349
## [6,]              6671            4293               907              1471

Ok, mas ainda não terminamos com essa etapa. Se você tentar construir o gráfico com esta matriz, veja o que você vai obter.

barplot(zika_matrix)

center

Hmm… não é bem isso que nós desejamos, né. O que aconteceu? O R entendeu que cada valor dentro de uma coluna da matriz era pra ser uma sub-coluna do gráfico. Em outras palavras, ele criou um gráfico de colunas empilhadas. Para obter um gráfico com as colunas lado-a-lado (e não empilhadas) devemos usar o argumento beside=TRUE. Veja a diferença:

barplot(zika_matrix, beside = TRUE)

center

Transpondo a matriz

Mas ainda não é o que queremos. Nós queremos as colunas referentes aos tipos de caso, mas estratificadas pela semana epidemiológica. Para isso, meu amigo, preste atenção, porque a dica que vou passar agora é fundamental e já quebrei muito a cabeça por causa disso. Já que, na hora de gerar o gráfico, o que importa é como as colunas da matriz estão organizadas, nós precisaremos alterar a nossa matriz pra que cada coluna dela seja relativa a uma semana epidemiológica (confira o gráfico original). Para que este conceito fique claro, vou fazer com que os nomes de cada linha da matriz sejam as semanas epidemiológicas, veja:

# Adicionando nomes às linhas da matriz
rownames(zika_matrix) <- zika$Semana

# Primeiras 6 linhas da matriz
head(zika_matrix)
##         Casos.Notificados Em.Investigação Casos.Confirmados
## 06-2016              5280            3935               508
## 07-2016              5639            4106               583
## 08-2016              5909            4222               641
## 09-2016              6158            4231               745
## 10-2016              6480            4268               863
## 11-2016              6671            4293               907
##         Casos.Descartados
## 06-2016               837
## 07-2016               950
## 08-2016              1046
## 09-2016              1182
## 10-2016              1349
## 11-2016              1471

Continuando nossa explicação dessa parte importante, perceba que na matriz acima as linhas estão relativas às semanas. Mas o que queremos é uma matriz com colunas relativas às semanas. O que fazer? Chamar o chapolim? Não! Basta virar essa matriz de lado! Em outras palavras, deveremos transpor essa matriz usando a função t(). Veja como ela fica agora:

# Transpondo a matriz
zika_matrix <- t(zika_matrix)

# Mostrando as 11 primeiras colunas
# da matriz transposta
zika_matrix[,1:11]
##                   06-2016 07-2016 08-2016 09-2016 10-2016 11-2016 12-2016
## Casos.Notificados    5280    5639    5909    6158    6480    6671    6776
## Em.Investigação      3935    4106    4222    4231    4268    4293    4291
## Casos.Confirmados     508     583     641     745     863     907     944
## Casos.Descartados     837     950    1046    1182    1349    1471    1541
##                   13-2016 14-2016 15-2016 16-2016
## Casos.Notificados    6906    7015    7150    7228
## Em.Investigação      4046    3836    3741    3710
## Casos.Confirmados    1046    1113    1168    1198
## Casos.Descartados    1814    2066    2241    2320

Assim a função barplot vai entender que cada coluna da matriz contém quatro valores, que serão as alturas de quatro colunas lado a lado. Veja como fica o gráfico agora:

# Gráfico de barras
barplot(zika_matrix, beside = TRUE, xaxt = "n")

center

Se você for um leitor atento, verá que adicionei o argumento xaxt="n". Isso quer dizer, ‘tipo do eixo X é igual a nenhum’, ou seja, eu suprimi o eixo X. Fiz Isso porque antes eu havia nomeado as linhas da matriz (as linhas se tornaram colunas na matriz transposta) com as semanas epidemiológicas. Se eu deixasse do jeito que estava o R iria produzir um eixo X automaticamente usando essas semanas e eu não quero isso. Siga em frente que você verá eu adicionando um novo eixo X e aí você entenderá o porquê.

Nosso gráfico está ficando melhor, não? Que tal adicionar um pouco de cor para deixá-lo mais parecido com o original?

# Gráfico de barras
barplot(zika_matrix,
        beside = TRUE,
        xaxt = "n",
        col = c('green', 'purple', 'red', 'black'))

center

Utilizando o argumento las=1 na função barplot(), podemos alterar a orientação dos números nos eixos:

# Gráfico de barras
barplot(zika_matrix,
        beside = TRUE,
        xaxt = "n",
        col = c('green', 'purple', 'red', 'black'),
        las = 1)

center

Eixos

Agora nos resta adicionar o eixo X e os títulos dos eixos. Para essa primeira tarefa, usaremos a função axis() e a função text(). Aqui side se refere ao lado em que o eixo deve ser desenhado (1 embaixo, 2 à esquerda, 3 acima e 4 à direita), at às posições das tick marks na linha do eixo, labels é o que deve ser escrito em cada tick mark e line é a posição do eixo (em número de linhas) em relação à margem. O argumento srt=45 utilizado na função text() permite que os textos sejam posicionados com uma angulação de 45 graus.

Perceba que aqui eu também designei o gráfico como uma variável (barp). Por que fiz isso? A função barplot(), além de gerar o gráfico, provê as posições das colunas no eixo X quando designada a uma variável. Por isso, barp agora é um vetor contendo as posições das colunas do gráfico no eixo X. Isso me permite usar essas informações para posicionar corretamente as tick marks e seus textos correspondentes.

Mas antes vamos reservar um espaço extra abaixo para que o eixo X, seu título e tick marks, bem como as legendas (que colocaremos adiante), possam aparecer. Usaremos para este fim a função par() e o seu argumento mar.

# Preparando margens do ambiente gráfico
par(mar = c(13, 5, 2, 2))

# Gráfico de barras
barp <- barplot(zika_matrix,
                beside = TRUE,
                xaxt = "n",
                col = c('green', 'purple', 'red', 'black'),
                las = 1)

# Eixo X
axis(side = 1,
     at = barp[2,],
     labels = rep("",44),
     line = .25)

text(x = barp[2,]-2, y = par('usr')[3]-2000,
     labels = zika$Semana,
     srt=45, xpd = TRUE, cex = .7)

center

Para nomear os eixos utilizamos as funções title() e os argumentos xlab e ylab. O argumento line aqui tem a mesma função de posicionamento relativo que a utilizada anteriormente.

# Títulos dos eixos X e Y
title(xlab = 'Semana Epidemiológica', line = 4)
title(ylab = 'Casos', line = 4)

center

Legendas

Por fim, nos resta adicionar a legenda. Faremos uma legenda de cada vez (ao todo quatro) porque o posicionamento será feito manualmente. Se for feito automaticamente pelo R, não sairá muito legal, vai por mim. Os truques de posicionamento utilizando a função par() e o argumento usr são um tópico bem mais avançado que não cobrirei aqui porque ocuparia muito tempo e espaço. Por enquanto você pode se contentar sabendo que, usando a função legend(), você pode adicionar legendas e que, para atingirmos o máximo de semelhança com o gráfico original da SAGE, precisaremos utilizar os argumentos:

  • x para a posição da legenda no eixo X;
  • y para a posição da lengeda no eixo Y;
  • fill para definir a cor do quadrado;
  • xpd=TRUE para permitir a legenda ser plotada fora dos limites do gráfico;
  • horiz=TRUE para obtermos uma legenda horizontal;
  • bty="n" para eliminarmos a ‘caixa’ que geralmente envolve legendas (bty é uma sigla para box type);
  • cex=.8 para definirmos um tamanho de 80% do original para a fonte da legenda.
# Diferença entre coordenadas máxima e mínima do eixo X
diff <- par('usr')[2]-par('usr')[1]

# Legendas
legend(x = par('usr')[1]+diff/4, y = par('usr')[3]-7000,
       legend = 'Casos Notificados',
       fill = 'green',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8)

legend(x = par('usr')[1]+diff/4+diff/8, y = par('usr')[3]-7000,
       legend = 'Em Investigação',
       fill = 'purple',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8)

legend(x = par('usr')[1]+diff/4+(diff/8)*2, y = par('usr')[3]-7000,
       legend = 'Casos Confirmados',
       fill = 'red',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8)

legend(x = par('usr')[1]+diff/4+(diff/8)*3, y = par('usr')[3]-7000,
       legend = 'Casos Descartados',
       fill = 'black',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8)

center

Conclusão

Que tal? Uff… que trabalheira hein… Pois é. Como disse acima, fazer um gráfico desses não é o trabalho mais trivial do mundo. Mas não deixa de ser muito legal! hehehe

Se você gostou desse post e tem interesse em aprender mais sobre R fique de olho no site Programmed Biology para fazer sua inscrição em algum dos meus cursos. Fique à vontade para me enviar uma mensagem particular também no e-mail pmdbio{at}gmail.com.

Tentarei liberar a segunda parte dessa série com o mesmo gráfico reproduzido em Python até Quinta-Feira dessa semana. Não deixe de checar!

Segredo: se você chegou até o final e for muito atento mesmo você perceberá que o gráfico original não possui linhas envolvendo as colunas e nem envolvendo os quadrados das legendas. Para suprimir essas linhas envoltórias, utilizamos o argumento border=FALSE nas funções barplot() e legend():

# Preparando margens do ambiente gráfico
par(mar = c(13, 5, 2, 2))

# Gráfico de barras
barp <- barplot(zika_matrix,
                beside = TRUE,
                xaxt = "n",
                col = c('green', 'purple', 'red', 'black'),
                las = 1,
                border = FALSE)

# Eixo X
axis(side = 1,
     at = barp[2,],
     labels = rep("",44),
     line = .25)

text(x = barp[2,]-2, y = par('usr')[3]-2000,
     labels = zika$Semana,
     srt=45, xpd = TRUE, cex = .7)

# Títulos dos eixos X e Y
title(xlab = 'Semana Epidemiológica', line = 4)
title(ylab = 'Casos', line = 4)

# Diferença entre coordenadas máxima e mínima do eixo X
diff <- par('usr')[2]-par('usr')[1]

# Legendas
legend(x = par('usr')[1]+diff/4, y = par('usr')[3]-7000,
       legend = 'Casos Notificados',
       fill = 'green',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8,
       border = FALSE)

legend(x = par('usr')[1]+diff/4+diff/8, y = par('usr')[3]-7000,
       legend = 'Em Investigação',
       fill = 'purple',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8,
       border = FALSE)

legend(x = par('usr')[1]+diff/4+(diff/8)*2, y = par('usr')[3]-7000,
       legend = 'Casos Confirmados',
       fill = 'red',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8,
       border = FALSE)

legend(x = par('usr')[1]+diff/4+(diff/8)*3, y = par('usr')[3]-7000,
       legend = 'Casos Descartados',
       fill = 'black',
       xpd = TRUE, horiz = TRUE, bty = "n", cex = .8,
       border = FALSE)

center