# Creates a boxplot of those values
boxplot(values)Gráficos no R
0 Introdução
Os gráficos podem ser considerados parte da estatística descritiva, resumindo os nossos dados de forma visual (enquanto estatísticas descritivas como a média e desvio-padrão, resumem os dados de forma numérica).
Os livros de estatística tradicionais (e.g., Marôco, 2020) costumam especificar qual o tipo de gráficos que é adequado para cada tipo de escala de medida das variáveis (e.g., qualitativa ordinal, quantitativa contínua, etc.). Por vezes salientam que há representações gráficas que costumam ser sobrepostas num só gráfico (e.g., que a curva da distribuição normal é frequentemente sobreposta a um histograma). Contudo, as capacidades gráficas dos computadores e dos programas estatísticos evoluíram muito rapidamente e creio que os manuais estatísticos nem sempre os acompanharam. Penso também que é importante distinguir Estatística (enquanto área do saber) de análise de dados, que é algo mais prático/técnico. Quando olhamos para recursos mais direccionados a analistas de dados, creio que se percebe o destaque e importância que dão à visualização dos dados.
Um bom exemplo disto são os capítulos de visualização de dados de Wicham e colaboradores (2023).
Ao longo deste tutorial verá caixas destacadas como estas, de diversos tipos, que tentam destacar algumas notas e avisos.
Este tutorial foi escrito em Quarto e tem activada uma funcionalidade que criou automaticamente hiperligações nos pacotes e funções do R (ver code-link: true). Por essa razão, poderá clicar nas funções presentes no código para consultar a sua documentação online.
Experimente carregar nesta função help().
A Importância de Visualizar os Dados
R “Puro”
Numa instalação típica, o R já traz consigo várias funcionalidades de representação gráfica. Dito isto, as funções de base do R que geram gráficos tendem a ser menos flexíveis e menos esteticamente apelativas do que as disponíveis noutros pacotes, em particular no ggplot2. Os exemplos de código abaixo ilustram estas funcionalidades. O resto do tutorial irá focar-se na geração de gráficos com o ggplot2.
1 Básicos do ggplot2
Instalação
Como qualquer pacote, o ggplot2 deve ser instalado antes de ser usado pela primeira vez.
# Install `ggplot2`
install.packages("ggplot2")Na função install.packages() escrevemos os nome do pacote entre aspas, para que o R o possa procurar no repositório oficial de pacotes (CRAN).
Este comando só precisa de ser executado uma vez na consola—não deve fazer parte do script, muito menos dum documento quarto!
Importação
Assim como qualquer pacote, o ggplot2 também precisa de ser importado com library(package_name) para termos as suas funções disponíveis no nosso ambiente.
Na função library() escrevemos o nome do pacote directamente (i.e., sem aspas), dado que o pacote já deverá ter sido instalado.
Podemos importar um pacote com library() em qualquer linha de código que seja executada antes de algum objecto do pacote ser usado. Contudo, por uma questão de organização, é boa prática importar todos os pacotes, por ordem alfabética no início do script.
É possível no entanto usar funções dum pacote instalado sem o invocar com library(package_name) se escrevermos package_name::function_from_package(), por exemplo, ggplot2::ggplot().
Dito isto, no caso do ggplot2 isto não será muito prático, porque usaremos muitas funções que vêm desse pacote sempre que quisermos fazer um gráfico e estar sempre a escrever ggplot2:: é chato e torna o código menos legível.
Gramática Base
O “gg” em ggplot2 significa “grammar of graphics” e ilustra o conceito fundamental do ggplot2, a composição de gráficos recorrendo a várias camadas e várias funções (por oposição a uma única função que gere cada tipo de gráfico).
Para criarmos um gráfico começamos por invocar a função ggplot() e depois vamos adicionando várias camadas com elementos gráficos—os geoms (e.g., geom_histogram())—ou opções de personalização da aparência do gráfico (e.g., theme_classic()).
A função que usamos é ggplot() apesar do pacote se chamar ggplot2
Não espero que compreendam o código abaixo, apenas quero que comecem a ganhar alguma familiaridade com usos típicos do ggplot2.
O código abaixo é pseudocódigo e não deverá correr na vossa máquina. Mais à frente apresentarei exemplos que poderão tentar replicar e adaptar.
# Creates empty graph defining aesthetics
ggplot(data = ds, aes(x = x_var, y = y_var, color = color_var, fill = fill_var)) +
# Geoms are addeed next
geom_violin() +
# Multiple geoms can be combined
geom_point() +
# We can customize the axis, scales, and legends
ylim(0, 6500) +
xlab("X axis label") +
# We can also customize the appearance with different themes
theme_classic() +
# And we can even customize the theme itself
theme(text = element_text(family = "Times New Roman"))Dados de Exemplo
Para podermos representar graficamente os nossos dados é importante que os tenhamos…ou então desenhamos só os eixos e isso não tem muita piada…
Neste tutorial vamos utilizar a base de dados penguins do pacote palmerpenguins, que contém dados de três espécies de pinguins do Pólo Sul.
Se pretender executar o código deste tutorial na sua máquina é importante que siga os passos abaixo.
Para aprender mais sobre estes dados e a motivação para a criação do pacote basta carregar neste excerto de código: palmerpenguins::penguins.
Instalação
Para termos acesso aos dados do pacote palmerpenguins basta-nos instalar o pacote como qualquer outro pacote.
install.packages("palmerpenguins")Criação da Variável ds
Para facilitar o nosso trabalho ao longo deste tutorial, vamos criar uma variável ds (abreviatura de dataset) que terá os nossos dados.
| species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | year |
|---|---|---|---|---|---|---|---|
| Adelie | Torgersen | 39.1 | 18.7 | 181 | 3750 | male | 2007 |
| Adelie | Torgersen | 39.5 | 17.4 | 186 | 3800 | female | 2007 |
| Adelie | Torgersen | 40.3 | 18.0 | 195 | 3250 | female | 2007 |
| Adelie | Torgersen | NA | NA | NA | NA | NA | 2007 |
| Adelie | Torgersen | 36.7 | 19.3 | 193 | 3450 | female | 2007 |
| Adelie | Torgersen | 39.3 | 20.6 | 190 | 3650 | male | 2007 |
Remoção de Casos Incompletos
Para facilitar o nosso trabalho e evitar termos de lidar com edge cases mais tarde, vamos seleccionar apenas as linhas sem valores em falta em nenhuma das colunas.
ds <- ds[complete.cases(ds), ]Aesthetics
À função aes() damos como argumentos os nomes das colunas da base de dados (o argumento data = ds que demos à função gpplot()), sem aspas.
-
x: a variável que irá ser colocada no eixo do x -
y: a variável que irá ser colocada no eixo do y -
fill: a variável que define a cor do preenchimento dos objectos -
color: a variável que define a cor dos pontos, linhas e contornos dos objectos
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()Geoms
Função
Exemplos
Peso corporal dos pinguins em função do tamanho da suas barbatanas.
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()Peso corporal dos pinguins em função do tamanho da suas barbatanas, tendo os pontos cores diferentes consoante o sexo.
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g, color = sex)) +
geom_point()Peso corporal dos pinguins em função do tamanho da suas barbatanas, tendo os pontos cores diferentes consoante o sexo e formas diferentes consoante a espécie.
ggplot(ds,
aes(x = flipper_length_mm, y = body_mass_g,
color = sex, shape = species)) +
geom_point()À medida que vamos explorando as capacidades gráficas avançadas do R e do ggplot2 é importante irmos também treinando as nossas capacidades de interpretação dos gráficos.
O que se conclui neste caso?
Reparem que os pontos azuis tendem a estar mais acima dos vermelhos—ou seja, os machos aparentam ser mais pesados que as fêmeas. Os quadrados estão visivelmente acima dos triângulos e bolas—ou seja, os Gentoo, parecem ser a espécie mais pesada.
Função
Exemplos
Boxplot do peso corporal dos pinguins.
ggplot(ds, aes(y = body_mass_g)) +
geom_boxplot()Boxplots do peso corporal dos pinguins divididos por espécie.
ggplot(ds, aes(x = sex, y = body_mass_g)) +
geom_boxplot()Boxplots do peso corporal dos pinguins divididos por espécie e sexo, tendo cada sexo uma cor de preenchimento diferente.
ggplot(ds, aes(x = species, y = body_mass_g, fill = sex)) +
geom_boxplot()Conseguem interpretar o padrão?
Função
Exemplos
Podemos usar a função geom_violin() da mesma forma que usámos a geom_boxplot() (ver separador anterior).
Aqui temos de especificar um valor do x, caso contrário teremos erros na geração do gráfico.
Tirando essa excepção verão que nos restantes blocos de código apenas temos de substituir geom_boxplot() por geom_violin().
ggplot(ds, aes(x = "Mass", y = body_mass_g)) +
geom_violin()Violinos do peso corporal dos pinguins divididos por espécie.
ggplot(ds, aes(x = sex, y = body_mass_g)) +
geom_violin()ggplot(ds, aes(x = species, y = body_mass_g, fill = sex)) +
geom_violin()Na realidade, nem temos de optar entre geom_boxplot() e geom_violin()…
Podemos sobrepôr um violino a um boxplot com facilidade.
ggplot(ds, aes(x = species, y = body_mass_g, fill = sex)) +
geom_violin() +
geom_boxplot()Infelizmente parece que os violinos e os gráficos não ficaram bem alinhados. Temos então de experimentar usar a função position_dodge() para ajustar a posição de ambos, de forma a que fiquem alinhados.
ggplot(ds, aes(x = species, y = body_mass_g, fill = sex)) +
geom_violin(position = position_dodge(1)) +
geom_boxplot(position = position_dodge(1))Vemos assim um exemplo de como o ggplot2 nos permite ir compondo os gráficos por camadas.
Conseguem interpretar o padrão?
Função
Exemplos
Este geom é usado sem um y ou sem um x porque essa escala será a contagem dos casos (ou a densidade).
ggplot(ds, aes(x = body_mass_g)) +
geom_histogram()ggplot(ds, aes(y = body_mass_g)) +
geom_histogram()ggplot(ds, aes(x = body_mass_g, fill = sex)) +
# alpha = 0.5: 50% transparency to show the distributions overlaping
geom_histogram(alpha = 0.5)Função
Exemplos
O geom_density() pode ser usado nos mesmos casos em que usaríamos o histograma (formalmente seria para variáveis contínuas, ou discretas com tantos níveis que possamos tartar como contínuas).
Assim sendo basta pegar nos exemplos anteriores e trocar a função geom_histogram() pela geom_density().
Este geom é usado sem um y ou sem um x porque essa escala será a da densidade.
ggplot(ds, aes(x = body_mass_g)) +
geom_density()ggplot(ds, aes(y = body_mass_g)) +
geom_density()ggplot(ds, aes(x = body_mass_g, fill = sex)) +
# alpha = 0.5: 50% transparency to show the distributions overlaping
geom_density(alpha = 0.5)Função
Exemplos
Quando queremos dividir as barras por grupos dentro de grupos (e.g., em delineamentos 2X2), devemos usar position = position_dodge2() ou position = position_dodge() colocando um valor apropriado dentro da função se necessário.
ggplot(ds, aes(x = species, fill = sex)) +
geom_bar(position = position_dodge2())Por default a função vai somar o número de casos, mas podemos usar stat = identity para dizer que a altura da barra é dada pelo valor das células (e.g., no caso de termos uma tabela com os valores das médias de cada grupo e querermos que as barras tenha essa altura).
A título de exemplo, vamos imaginar que tínhamos uma tabela de contagens por espécie e sexo e queríamos usá-la para fazer o gráfico de barras.
# Tables species by sex
counts <- table(ds$species, ds$sex)
# Converts `counts` to a `data.frame()`
counts <- data.frame(counts)
# Let's see how it looks
head(counts)| Var1 | Var2 | Freq |
|---|---|---|
| Adelie | female | 73 |
| Chinstrap | female | 34 |
| Gentoo | female | 58 |
| Adelie | male | 73 |
| Chinstrap | male | 34 |
| Gentoo | male | 61 |
ggplot(counts, aes(x = Var1, fill = Var2, y = Freq)) +
geom_bar(stat = "identity", position = position_dodge2())2 Facets
O ggplot2 só gera um gráfico por objecto. Contudo, existem pacotes que permitem juntar vários gráficos criados pelo ggplot2 num só objecto. Dito isto, se soubermos usar bem as funções facet_wrap() e fracet_grid() conseguimos separar o nosso gráfico em painéis, conseguindo evitar recorrer a pacotes adicionais.
facet_wrap()
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
facet_wrap(~ sex)ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
facet_wrap(~ species * sex, ncol = 2)facet_grid()
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
facet_grid(~ sex)Com facet_grid() também usamos a sintaxe das fórmulas para criar os painéis, mas reparem que agora passamos uma das VIs para o lado esquerdo da fórmula.
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
facet_grid(species ~ sex)ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
facet_grid(sex ~ species)ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
facet_grid(sex ~ species + island)3 Aparência
O ggplot2 tem vários temas que afectam toda a aparência do gráfico. Na secção abaixo listo os dois temas que mais recomendo, em particular se planearem usar os gráficos numa publicação científica.
Dito isto, podem também espreitar o capítulo sobre temas do livro oficial do ggplot2.
Existem também outros temas disponíveis, que normalmente requerem a instalação de pacotes adicionais.
Temas Recomendados
# Creates variable `g`, which is a `ggplot2` we can evoke later
g <- ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point() +
geom_smooth()
# Prints default plot
print(g)theme_classic()
g + theme_classic()theme_minimal()
g + theme_minimal()Personalização do Tema
Há demasiadas opções de personalização do tema/aparência do gráfico para incluir neste tutorial. Abaixo está um exemplo de como alterar o tipo e tamanho de letra de todos os elementos textuais do gráfico.
g +
theme_classic() +
theme(text = element_text(family = "Times New Roman", size = 30))Aconselho a leitura da referência da função theme() para mais informações.
Como tudo em 2025, o site oficial do pacote ggplot2 já contem uma AI a quem podemos fazer perguntas (um chatbot RAG com acesso a toda a documentação oficial). Se tiverem dúvidas específicas sobre personalização da aparência do gráfico que não consigam responder facilmente olhando para o manual da função theme() aconselho a que experimentem colocar as vossas questões a essa AI.
4 Exemplos Mais Complexos
ggplot(ds, aes(x = species, y = body_mass_g, fill = sex)) +
geom_violin(position = position_dodge(1)) +
geom_boxplot(position = position_dodge(1), show.legend = FALSE) +
geom_point(position = position_jitterdodge(jitter.width = 0.25, dodge.width = 1),
size = 1, show.legend = FALSE) +
stat_summary(fun = "mean", geom = "point", position = position_dodge(1),
size = 3, color = "red", show.legend = FALSE) +
theme_classic()# fill sets the fill for the histogram bars and density graph
# color sets the color for the line that contours the densit graph
ggplot(ds, aes(x = body_mass_g, fill = sex, color = sex)) +
geom_histogram(aes(y = ..density..)) +
geom_density(alpha = 0.25, linetype = "dashed") +
facet_wrap(~ species, ncol = 1) +
theme_classic()ggplot(ds, aes(x = body_mass_g, fill = sex)) +
geom_density(alpha = 0.25, linetype = "dashed") +
geom_histogram(aes(y = ..density..)) +
facet_grid(species ~ sex) +
theme_classic()# Import packages
# For estimating marginal means.
library(emmeans)
# Changes default options (use constrasts that sum to zero coding scheme)
options(contrasts = c("contr.sum", "contr.poly"))
# Fits model
m <- lm(body_mass_g ~ species, ds)
# Gets estimated marginal mean for each species
emms <- emmeans(m, ~ species)
# Forces `emms` to become a `data.frame()`
emms <- data.frame(emms)
anova(m)| Df | Sum Sq | Mean Sq | F value | Pr(>F) | |
|---|---|---|---|---|---|
| species | 2 | 145190219 | 72595109.6 | 341.8949 | 0 |
| Residuals | 330 | 70069447 | 212331.7 | NA | NA |
head(emms)| species | emmean | SE | df | lower.CL | upper.CL |
|---|---|---|---|---|---|
| Adelie | 3706.164 | 38.13563 | 330 | 3631.145 | 3781.184 |
| Chinstrap | 3733.088 | 55.87955 | 330 | 3623.163 | 3843.013 |
| Gentoo | 5092.437 | 42.24097 | 330 | 5009.341 | 5175.533 |
ggplot(emms, aes(x = species, y = emmean, fill = species)) +
geom_bar(stat = "identity", position = position_dodge2()) +
geom_errorbar(aes(ymin = lower.CL, ymax = upper.CL), width = 0.3) +
geom_point(color = "red", show.legend = FALSE) +
labs(x = "Species", y = "Estimated Marginal Mean", fill = "Species") +
theme_classic()ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point() +
geom_smooth(method = lm, se = FALSE) +
theme_classic()ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point() +
geom_smooth(method = lm, se = FALSE) +
facet_wrap(~ species) +
theme_classic()ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point() +
geom_smooth(method = lm, se = FALSE) +
facet_grid(species ~ sex) +
theme_classic()# Import packages
# Used for Anova() tables with type III SS
library(car)
# For estimated marginal means and multiple comparisons
library(emmeans)
# Changes default options (use constrasts that sum to zero coding scheme)
options(contrasts = c("contr.sum", "contr.poly"))
# Fits model
m <- lm(body_mass_g ~ species * sex, ds)
# Gets estimated marginal means to decompose the interaction
emms <- emmeans(m, ~ sex * species)
# Forces `emms` to become a `data.frame()`
emms <- data.frame(emms)
Anova(m, type = 3)| Sum Sq | Df | F value | Pr(>F) | |
|---|---|---|---|---|
| (Intercept) | 5232595969 | 1 | 54661.827958 | 0.0000000 |
| species | 143001222 | 2 | 746.924492 | 0.0000000 |
| sex | 29851220 | 1 | 311.838003 | 0.0000000 |
| species:sex | 1676557 | 2 | 8.756997 | 0.0001973 |
| Residuals | 31302628 | 327 | NA | NA |
head(emms)| sex | species | emmean | SE | df | lower.CL | upper.CL |
|---|---|---|---|---|---|---|
| female | Adelie | 3368.836 | 36.21222 | 327 | 3297.597 | 3440.074 |
| male | Adelie | 4043.493 | 36.21222 | 327 | 3972.255 | 4114.731 |
| female | Chinstrap | 3527.206 | 53.06120 | 327 | 3422.821 | 3631.590 |
| male | Chinstrap | 3938.971 | 53.06120 | 327 | 3834.586 | 4043.355 |
| female | Gentoo | 4679.741 | 40.62586 | 327 | 4599.820 | 4759.662 |
| male | Gentoo | 5484.836 | 39.61427 | 327 | 5406.905 | 5562.767 |
Agora vamos gerar os gráficos de pontos e linhas típicos da interacção.
Espécie (eixo do x) por Sexo (linhas)
ggplot(emms, aes(x = species, y = emmean, shape = sex, color = sex)) +
geom_line(aes(group = sex)) +
geom_errorbar(aes(ymin = lower.CL, ymax = upper.CL), width = 0.25) +
geom_point(size = 2) +
ylab("Estimated Marginal Mean") +
theme_classic()Sexo (eixo do x) por Espécie (linhas)
ggplot(emms, aes(x = sex, y = emmean, shape = species, color = species)) +
geom_line(aes(group = species)) +
geom_errorbar(aes(ymin = lower.CL, ymax = upper.CL), width = 0.25) +
geom_point(size = 2) +
ylab("Estimated Marginal Mean") +
theme_classic()5 Exercícios
Replique um ou mais dos exemplos no seu computador, evitando fazer copy-paste.
Experimente fazer alguma alteração a um ou mais exemplos (e.g., adicionar um elemento, alterar a aparência, etc…).
[AVANÇADO] Utilize alguma função ou técnica que tenha aprendido neste tutorial para fazer um gráfico dos dados dum dos seus estudos.
6 Epílogo: Interactividade
O shiny permite-nos apresentar dashboards com visualizações interactivas nos nossos relatórios. Se decidirmos aprender a usar o plotly poderemos até fazer gráficos 3D interactivos.