install.packages(c("ggplot2", "palmerpenguins"))Gráficos no R
0 Gráficos: Tornar uma Pergunta Visível
Um gráfico é uma pergunta desenhada e parte da estatística descritiva: torna visíveis padrões que uma média, uma tabela ou um modelo podem esconder. Não é, contudo, uma máquina de conclusões — felizmente, ainda não há um botão para isso. Neste tutorial seguimos sempre o mesmo percurso:
pergunta → variáveis e delineamento → escolha gráfica → código → leitura → ajustes
Escolha o gráfico pela pergunta: quer observar uma distribuição, comparar grupos, explorar uma relação ou acompanhar mudanças na mesma unidade? O tipo de variável também importa, mas não determina sozinho qual o gráfico mais útil. Os manuais apresentam habitualmente barras para categorias, histogramas para distribuições e gráficos de dispersão para duas variáveis quantitativas. Este vocabulário é útil e vamos usá-lo; a escolha final depende do que queremos ver.
Use esta página com o R aberto. Escreva e altere os exemplos em vez de os coleccionar como receitas. Não precisa de já saber modelos para começar: cada secção diz que pergunta o gráfico serve e o que a figura não permite concluir. Se chegou aqui depois de resumir dados, o tutorial de descritivas explica os números que estes gráficos complementam. A teoria das descritivas ajuda a decidir o que cada resumo mostra, e Delineamento da Investigação ajuda quando a unidade de observação ou a replicação não são óbvias. Para estimativas e incerteza de um modelo, siga para modelos lineares.
Wickham e colaboradores (2023) têm uma introdução extensa e interactiva à visualização. Vale a pena consultá-la depois de experimentar os exemplos daqui.
Se não estiver a ver o capítulo acima desta nota, o navegador ou o próprio site pode ter bloqueado o enquadramento. Use o acesso directo ao capítulo de visualização do R4DS. O conteúdo continua disponível sem o iframe.
Uma linha pode representar um pinguim, uma pessoa, uma medição repetida ou a contagem de muitos casos. Antes de escolher geom_point(), escreva o que cada marca representa. Mais pontos não criam mais unidades independentes. Se a mesma unidade foi medida várias vezes, veja medidas repetidas.
1 Preparação
Vamos usar ggplot2 e os pinguins do pacote palmerpenguins. Instale cada pacote uma vez na consola, não dentro de um script ou documento Quarto.
tibble [344 × 5] (S3: tbl_df/tbl/data.frame)
$ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
$ body_mass_g : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
$ species : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
$ sex : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
$ island : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
flipper_length_mm body_mass_g species sex
2 2 0 11
island
0
ds <- ds[complete.cases(ds[, variaveis_grafico]), variaveis_grafico,
drop = FALSE]A cópia ds remove apenas linhas incompletas nas variáveis que estes exemplos usam. Não é uma regra geral para dados em falta: numa análise real, a decisão depende da pergunta e deve ser documentada.
2 Da Pergunta ao Primeiro Gráfico
Pergunta. O comprimento da barbatana e a massa corporal variam juntos, e o padrão parece igual entre espécies e sexos?
Variáveis e delineamento. São duas medidas quantitativas por pinguim. espécie e sexo identificam grupos observados, não tratamentos aleatoriamente atribuídos. Cada ponto deve, portanto, representar um pinguim.
No ggplot2, um gráfico é construído por camadas. Em vez de saltarmos logo para a figura final, vamos acrescentar uma decisão de cada vez. Os separadores permitem comparar o código sem empilhar quatro figuras quase iguais na página.
Começamos apenas com posição: uma medida no eixo x e outra no eixo y.
ggplot(ds, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()Agora mapeamos a espécie simultaneamente para cor e forma. O gráfico passa a perguntar se a relação parece semelhante entre espécies.
ggplot(
ds,
aes(
x = flipper_length_mm,
y = body_mass_g,
colour = species,
shape = species
)
) +
geom_point()Em vez de acrescentar mais uma estética aos mesmos pontos, usamos painéis para comparar os sexos mantendo as escalas.
ggplot(
ds,
aes(
x = flipper_length_mm,
y = body_mass_g,
colour = species,
shape = species
)
) +
geom_point() +
facet_wrap(~ sex)Só no fim ajustamos transparência, tamanho, tema e rótulos. Estas alterações melhoram a leitura. Não acrescentam uma nova variável.
ggplot(
ds,
aes(
x = flipper_length_mm,
y = body_mass_g,
colour = species,
shape = species
)
) +
geom_point(alpha = 0.65, size = 2) +
facet_wrap(~ sex) +
theme_classic() +
labs(
x = "Comprimento da barbatana (mm)",
y = "Massa corporal (g)",
colour = "Espécie",
shape = "Espécie"
)Esta sequência mostra a ideia central da gramática de gráficos: começamos pelos dados e pelos mapeamentos e acrescentamos camadas quando elas respondem a uma pergunta. Dentro de aes(), uma coluna é mapeada para uma propriedade visual e surge normalmente uma legenda. Fora de aes(), a propriedade fica fixa para todos os casos:
ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
geom_point(colour = "grey35", alpha = 0.65)Compare com o segundo separador: colour = species mapeia informação. colour = "grey35" apenas escolhe a aparência.
O que está visível? Tendências, sobreposição, intervalos sem dados e possíveis diferenças entre grupos. O que fica escondido? A incerteza de um declive, idade, ilha, amostragem e quaisquer pinguins excluídos por valores em falta. O que é inferível? Uma associação descritiva, não o efeito causal de alterar uma barbatana.
3 Como É a Distribuição?
A forma de uma variável também beneficia de construção gradual. Histograma e densidade não são rivais que obrigam a escolher um vencedor: mostram a mesma distribuição com decisões diferentes de agrupamento ou suavização.
Pergunta. A massa corporal tem assimetria, lacunas, grupos ou mais de um pico?
Começamos com uma largura de classe explícita.
ggplot(ds, aes(body_mass_g)) +
geom_histogram(
binwidth = 250,
boundary = 0,
fill = "grey70",
colour = "white"
) +
theme_classic() +
labs(x = "Massa corporal (g)", y = "Número de pinguins")Mudamos uma única decisão. Um padrão que desaparece quando passamos de 250 para 500 g por classe merece cautela.
ggplot(ds, aes(body_mass_g)) +
geom_histogram(
binwidth = 500,
boundary = 0,
fill = "grey70",
colour = "white"
) +
theme_classic() +
labs(x = "Massa corporal (g)", y = "Número de pinguins")A densidade suaviza a distribuição. É útil para ver a forma, mas a altura já não é uma contagem de observações.
ggplot(ds, aes(body_mass_g)) +
geom_density(fill = "grey80", alpha = 0.6) +
theme_classic() +
labs(x = "Massa corporal (g)", y = "Densidade")Por fim combinamos histograma e densidade e separamos espécie e sexo em painéis. A complexidade só aparece depois de sabermos o que cada camada faz.
ggplot(ds, aes(body_mass_g)) +
geom_histogram(
aes(y = after_stat(density)),
binwidth = 250,
fill = "grey75",
colour = "white"
) +
geom_density(colour = "black", linewidth = 0.7) +
facet_grid(sex ~ species) +
theme_classic() +
labs(x = "Massa corporal (g)", y = "Densidade")Compare os separadores. A escolha de binwidth muda detalhes do histograma. A densidade introduz suavização. os painéis revelam que parte da forma global resulta da mistura de grupos. Nenhuma destas figuras, sozinha, diz quantas populações biológicas existem ou estabelece uma explicação causal.
Quando o tamanho dos grupos importa, conte-o ou mostre os pontos. Uma densidade normalizada pode parecer igualmente alta para grupos com números de observações muito diferentes.
4 Grupos: Diferenças, Variação e Incerteza
Uma barra é um gráfico convencional para contagens. Barras de médias são comuns, mas a sua base em zero e a ocultação dos dados individuais tornam-nas frequentemente uma escolha fraca para uma resposta contínua. Comecemos por ver distribuição e casos.
Comparar Grupos Contínuos
Pergunta. Como varia a massa entre espécies e sexos, e quanta sobreposição existe?
Escolha. Começamos pelos pontos, que mostram os casos. Depois podemos acrescentar uma caixa para resumir quartis e, se a forma da distribuição for importante, um violino para representar uma densidade aproximada. Os separadores mostram esta construção por camadas sem obrigar a percorrer três figuras completas na vertical.
ggplot(ds, aes(species, body_mass_g, shape = sex)) +
geom_jitter(width = 0.12, alpha = 0.5) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)", shape = "Sexo")ggplot(ds, aes(species, body_mass_g, fill = sex)) +
geom_boxplot(position = position_dodge(width = 0.8), width = 0.5,
outlier.shape = NA, alpha = 0.55) +
geom_point(aes(shape = sex),
position = position_jitterdodge(jitter.width = 0.10,
dodge.width = 0.8),
alpha = 0.35, size = 1.5) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)",
fill = "Sexo", shape = "Sexo")ggplot(ds, aes(species, body_mass_g, fill = sex)) +
geom_violin(position = position_dodge(width = 0.8), alpha = 0.55,
colour = "grey30") +
geom_boxplot(position = position_dodge(width = 0.8), width = 0.18,
outlier.shape = NA, alpha = 0.85) +
geom_point(aes(shape = sex),
position = position_jitterdodge(jitter.width = 0.10,
dodge.width = 0.8),
alpha = 0.35, size = 1.5) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)",
fill = "Sexo", shape = "Sexo")Compare os três separadores. Em qual é mais fácil ver cada pinguim, os quartis ou a forma aproximada? Há alguma camada que não esteja a ajudar a sua leitura? Visível: conforme a versão, valores individuais, mediana, quartis, densidade aproximada e sobreposição. Escondido: incerteza sobre uma população e causas das diferenças. Inferível: diferenças descritivas nos pinguins observados. Limite: violinos são instáveis em grupos pequenos. O boxplot não decide se um valor é «mau» nem se deve ser removido.
Incerteza: Dados e Estimativa Não São a Mesma Camada
Pergunta. Qual é a média de cada espécie e quão precisa é essa estimativa?
Uma média com intervalo pode responder a uma pergunta sobre uma estimativa, mas deve dizer que estatística e que intervalo desenha. Abaixo calculamos, de forma transparente, intervalos de confiança t para a média de cada espécie, com os graus de liberdade de cada grupo. Este cálculo pressupõe observações independentes dentro de cada grupo e uma amostragem e distribuição que tornem o intervalo t apropriado. Não substitui um modelo quando o delineamento exige covariáveis ou outra estrutura de dependência.
resumo_especie <- aggregate(body_mass_g ~ species, data = ds,
FUN = function(x) c(n = length(x), media = mean(x),
se = sd(x) / sqrt(length(x))))
resumo_especie <- data.frame(
species = resumo_especie$species,
resumo_especie$body_mass_g
)
# Use a t critical value separately for each group's degrees of freedom.
resumo_especie$df <- resumo_especie$n - 1
resumo_especie$t_critico <- qt(.975, df = resumo_especie$df)
resumo_especie$limite_inferior <- resumo_especie$media -
resumo_especie$t_critico * resumo_especie$se
resumo_especie$limite_superior <- resumo_especie$media +
resumo_especie$t_critico * resumo_especie$se
# Observed points in the background; estimates and intervals on top.
ggplot(ds, aes(species, body_mass_g)) +
geom_jitter(width = 0.12, alpha = 0.25, colour = "grey35") +
geom_errorbar(data = resumo_especie,
aes(x = species, ymin = limite_inferior, ymax = limite_superior),
width = 0.12, linewidth = 0.7, inherit.aes = FALSE) +
geom_point(data = resumo_especie, aes(x = species, y = media),
inherit.aes = FALSE, size = 2.8, shape = 21, fill = "white") +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)")O intervalo quantifica a precisão da média sob as condições declaradas. Não é a amplitude dos valores individuais nem um intervalo de previsão para um novo pinguim. Compare a largura dos intervalos: que espécie tem a média menos precisa, e que característica dos dados poderá explicá-lo? O intervalo também não deve ser lido, sem especificar uma abordagem bayesiana, como a probabilidade de uma média populacional fixa estar dentro do intervalo. E o cálculo, por si só, não identifica a população para a qual estes pinguins permitem generalizar. Para médias marginais estimadas, contrastes e intervalos de um modelo, prossiga para modelos lineares.
5 Relações: Linhas, Grupos e Sobreposição
Pergunta. A associação massa–barbatana parece linear e igual entre espécies e sexos?
Uma linha ajustada é mais uma camada, portanto convém perceber o que mudou em cada passo. Os separadores seguintes recuperam a lógica incremental: dados, modelo simples, grupos e painéis.
ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
geom_point(alpha = 0.5) +
theme_classic() +
labs(
x = "Comprimento da barbatana (mm)",
y = "Massa corporal (g)"
)Agora acrescentamos uma regressão linear global. Ela resume todos os pinguins como se uma única relação fosse suficiente.
ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
geom_point(alpha = 0.45) +
geom_smooth(method = "lm", se = TRUE) +
theme_classic() +
labs(
x = "Comprimento da barbatana (mm)",
y = "Massa corporal (g)"
)Mapear a espécie muda a pergunta: passamos a deixar que cada espécie tenha a sua própria linha no gráfico.
ggplot(
ds,
aes(
flipper_length_mm,
body_mass_g,
colour = species,
shape = species
)
) +
geom_point(alpha = 0.45) +
geom_smooth(
aes(linetype = species),
method = "lm",
se = TRUE,
linewidth = 0.8,
show.legend = FALSE
) +
theme_classic() +
labs(
x = "Comprimento da barbatana (mm)",
y = "Massa corporal (g)",
colour = "Espécie",
shape = "Espécie",
linetype = "Espécie"
)Finalmente separamos simultaneamente sexo e espécie. Ganhamos legibilidade dentro dos grupos, mas perdemos alguma visão da sobreposição entre eles.
ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
geom_point(alpha = 0.45, colour = "grey35") +
geom_smooth(method = "lm", se = TRUE, colour = "black") +
facet_grid(sex ~ species) +
theme_classic() +
labs(
x = "Comprimento da barbatana (mm)",
y = "Massa corporal (g)"
)A primeira recta pode resumir sobretudo diferenças entre espécies, enquanto as rectas por espécie mostram associações dentro de cada grupo. É uma boa razão para não tratar uma camada de modelo como decoração. A linha é uma previsão condicional ao modelo linear, não uma trajectória percorrida por cada pinguim.
Uma estética discreta mapeada, como colour = species, habitualmente fornece o agrupamento que geom_smooth() deve usar. Precisamos de group explicitamente quando o agrupamento desejado não está representado por outra estética, como nas linhas que ligam medições repetidas pelo ID.
Visível: curvatura aparente, dispersão, diferenças de declive e zonas sem dados. Escondido: confundidores, dependência e causalidade. Inferível: hipóteses para um modelo. Limite: painéis com poucos casos e escalas livres podem criar comparações ilusórias. Mantenha escalas comuns quando a altura ou inclinação entre painéis é a comparação.
Quando Há Demasiados Pontos
Pergunta. Onde se concentram as observações quando já não conseguimos ver cada ponto?
Sobreposição (overplotting) pode transformar centenas de observações numa mancha muito convincente e pouco informativa. Transparência ajuda. geom_bin2d() conta observações em células e revela densidade sem fingir que todos os pontos são visíveis.
ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
geom_bin2d(bins = 18) +
scale_fill_viridis_c(name = "N por célula") +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)")A posição e a legenda continuam a comunicar: não use a cor, por si só, para concluir onde há mais casos. Altere bins e veja se o padrão é robusto.
6 Mudança Dentro de Unidades Repetidas
Pergunta. Como mudou o resultado de cada participante entre dois momentos?
Linhas são frequentemente lidas como mudança no tempo. Essa leitura só é apropriada quando os pontos ligados pertencem à mesma unidade e a ordem de x tem significado. O exemplo seguinte é simulado, apenas para mostrar a decisão gráfica.
set.seed(2025)
repetidos <- expand.grid(
participante = factor(sprintf("P%02d", 1:18)),
momento = factor(c("pré", "pós"), levels = c("pré", "pós"))
)
efeito_pessoa <- rnorm(18, 50, 7)
repetidos$score <- efeito_pessoa[as.integer(repetidos$participante)] +
ifelse(repetidos$momento == "pós", 4, 0) +
rnorm(nrow(repetidos), 0, 3)
ggplot(repetidos, aes(momento, score, group = participante)) +
geom_line(alpha = 0.35) +
geom_point(alpha = 0.65) +
theme_classic() +
labs(x = "Momento", y = "Resultado")Visível: direcção e heterogeneidade das mudanças de cada participante. Escondido: uma estimativa populacional, a incerteza e se a ordem temporal é causal. Inferível: que as linhas estão emparelhadas e que um modelo deve reconhecer a dependência. Limite: ligar grupos diferentes com linhas cria uma falsa história individual. Para a análise, consulte medidas repetidas.
7 Contagens e Proporções: O Denominador Também É Informação
As barras são especialmente naturais quando a altura representa contagens ou proporções. Também aqui compensa construir a figura por etapas.
Quantos pinguins observados há por espécie?
ggplot(ds, aes(species)) +
geom_bar(fill = "grey55") +
theme_classic() +
labs(x = "Espécie", y = "Número de pinguins")Agora queremos ver as contagens de cada sexo dentro de cada espécie. As barras lado a lado preservam o denominador em contagens.
ggplot(ds, aes(species, fill = sex)) +
geom_bar(position = "dodge") +
theme_classic() +
labs(x = "Espécie", y = "Número de pinguins", fill = "Sexo")A mesma tabela pode ser normalizada para que cada espécie tenha altura 1. Agora a figura responde à composição relativa, não ao tamanho da amostra.
ggplot(ds, aes(species, fill = sex)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = function(x) paste0(round(100 * x), "%")) +
theme_classic() +
labs(x = "Espécie", y = "Proporção dentro da espécie", fill = "Sexo")Se quisermos controlar directamente o cálculo, construímos primeiro a tabela. Isto torna visível qual é o denominador usado em cada proporção.
contagens <- as.data.frame(table(ds$species, ds$sex))
names(contagens) <- c("species", "sex", "n")
contagens$total_especie <- ave(contagens$n, contagens$species, FUN = sum)
contagens$proporcao <- contagens$n / contagens$total_especie
contagens$rotulo <- paste0(round(100 * contagens$proporcao), "%")
ggplot(contagens, aes(species, proporcao)) +
geom_col(fill = "grey55") +
geom_text(aes(label = rotulo), vjust = -0.35, size = 3.2) +
facet_wrap(~ sex) +
scale_y_continuous(
labels = function(x) paste0(round(100 * x), "%"),
expand = expansion(mult = c(0, 0.12))
) +
theme_classic() +
labs(x = "Espécie", y = "Proporção dentro da espécie")geom_bar() conta linhas. Para dados já resumidos, use geom_col() e uma coluna y. A transição dos dois primeiros separadores para os dois últimos é também uma mudança de pergunta: quantos casos? deixa de ser que fracção do grupo?. Uma proporção sem denominador é informação incompleta.
Estas figuras são descritivas da amostra. Para respostas binárias, proporções, contagens, exposição e incerteza, veja GLMs para proporções e contagens.
8 Escalas, Coordenadas, Rótulos e Anotações
Transformar a Escala sem Apagar Dados
Pergunta. Uma escala em que distâncias iguais representam razões torna o padrão mais legível sem alterar as observações incluídas?
Uma escala logarítmica é útil quando razões (duplicar, metade) são mais interpretáveis do que diferenças ou quando poucos valores grandes comprimem o resto. Não a use para fazer um padrão «mais bonito». Diga que transformação usou e evite-a para valores zero ou negativos sem uma decisão justificada.
ggplot(ds, aes(flipper_length_mm, body_mass_g, colour = species,
shape = species)) +
geom_point(alpha = 0.65) +
scale_y_log10() +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)",
y = "Massa corporal (g; escala log10)", colour = "Espécie", shape = "Espécie")coord_cartesian(ylim = c(3000, 6500)) faz zoom sem remover observações do cálculo das camadas. scale_y_continuous(limits = c(3000, 6500)) descarta valores fora dos limites antes de estatísticas como uma linha suavizada. Esta diferença evita que um ajuste visual altere também os dados usados no cálculo.
Destacar sem Esconder o Resto
Pergunta. Que observações pertencem à região de barbatanas com pelo menos 210 mm e massa a partir de 5000 g?
Uma anotação deve responder a uma pergunta, não competir pelo leitor. Aqui marcamos essa região e dizemos explicitamente qual foi a regra.
ggplot(ds, aes(flipper_length_mm, body_mass_g, colour = species,
shape = species)) +
geom_point(alpha = 0.6) +
annotate("rect", xmin = 210, xmax = Inf, ymin = 5000, ymax = Inf,
fill = "gold", alpha = 0.15, colour = NA) +
annotate("text", x = 211, y = 6250, label = "Região destacada",
hjust = 0, colour = "black") +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)",
colour = "Espécie", shape = "Espécie")Visível: a regra de destaque e os pontos que a satisfazem. Escondido: por que esses valores existem e se são erros. Inferível: apenas que foram seleccionados por esta regra gráfica. Limite: não rotule observações como «outliers» para as apagar sem verificar a recolha e a pergunta.
Um Tema É Parte da Comunicação
theme_classic() e theme_minimal() reduzem ruído visual. Comece por rótulos com unidades, uma legenda com nome e texto legível. Só depois ajuste detalhes. Evite gráficos 3D, texto minúsculo, grelhas decorativas, paletas sem contraste e eixos truncados que exagerem diferenças. Para comparar magnitudes, mantenha um zero significativo em barras. Para pontos e intervalos, um recorte pode ser legítimo se for declarado e não esconder dados relevantes.
Antes do ggplot2, e ainda hoje em muitos scripts, encontrará funções base como plot(), hist(), boxplot(), qqnorm() e qqline(). Não precisa de aprender aqui uma segunda gramática completa: reconheça que estas funções desenham directamente a partir de vectores ou fórmulas, e consulte ?plot ou a documentação de gráficos base quando encontrar esse código. qqline() acrescenta uma linha de referência a um gráfico criado por qqnorm(). A linha não transforma o gráfico num teste automático de normalidade.
Ao encontrar estas figuras num script, consegue dizer que variável ou relação cada uma mostra e que informação precisaria antes de a interpretar?
coord_flip() melhora categorias com nomes longos. facet_wrap(~ island) permite procurar heterogeneidade por ilha. Use facet_grid(sex ~ species) quando ambas as dimensões devem manter uma grelha comparável. Escalas livres podem revelar forma dentro de painéis, mas tornam comparações de altura ou inclinação perigosas.
# ggplot(ds, aes(species, body_mass_g)) +
# geom_boxplot() + coord_flip() + theme_classic()
#
# ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
# geom_point(alpha = .5) + facet_wrap(~ island) + theme_classic()Que comparação fica mais fácil em cada exemplo, e qual deixa de estar visível? Para alterar uma paleta ou a posição da legenda, consulte a documentação de escalas do ggplot2 e confirme sempre contraste e impressão a preto e branco.
9 Exercícios: Decidir Antes de Desenhar
Tente resolver sem copiar literalmente os exemplos. Em cada resposta, registe as variáveis, a unidade observacional, a escolha gráfica e uma limitação da leitura. As soluções estão recolhidas no fim da secção; em exercícios abertos, a solução mostra uma decisão possível e explica por que razão ela é adequada.
- Escolha uma variável quantitativa de
ds. Faça dois histogramas com larguras de classe diferentes. Que padrão aparece nos dois, qual muda e que decisão tomaria antes de o descrever? - Redesenhe a comparação de massa por espécie sem
fill; use posição, forma, painéis ou rótulos para que continue legível a alguém que não distingue cores. Explique o que cada versão esconde. - Um colega propõe barras de médias para
body_mass_g. Faça uma versão que inclua os dados individuais e outra que inclua uma estimativa com intervalo. Que pergunta cada uma responde? Que denominador ou delineamento ainda falta conhecer? - Faça um gráfico de dispersão de duas medidas dos pinguins e acrescente uma linha apenas se souber explicar o modelo que ela representa. Identifique uma alternativa causal plausível que o gráfico não separa.
- Crie uma versão com
facet_wrap(~ island). Compare-a com uma versão com cor por ilha: em qual encontra mais facilmente grupos pequenos, e em qual compara mais facilmente a mesma escala? - Produza uma barra de contagens e uma barra de proporções para
speciesesex. Escreva uma frase que não confunda composição com tamanho da amostra. - No gráfico simulado de medidas repetidas, remova
group = participantee observe que as linhas deixam de representar participantes. Depois, experimentegroup = 1: porque é que uma linha única que liga os grupos pode ser enganadora? Adapte o gráfico aos seus próprios dados apenas depois de identificar a coluna ID. - Escolha um gráfico desta página e faça uma auditoria: o que está visível, escondido, inferível e limitado pelo delineamento? Depois mude uma camada para responder a uma dessas limitações.
Mais Exercícios
Estes exercícios são deliberadamente abertos: servem para praticar a adaptação, não para produzir uma única figura «correcta».
- Reproduza um ou mais exemplos no seu computador, evitando fazer copy-paste. Antes de começar, escreva que decisão visual o exemplo está a demonstrar.
- Altere um ou mais exemplos — por exemplo, acrescente uma camada, mude a aparência ou substitua uma faceta por uma codificação de grupo. Explique que nova pergunta a alteração torna mais fácil responder e que informação pode ter ficado menos visível.
- Avançado. Use uma função ou técnica deste tutorial para fazer um gráfico dos dados de um dos seus estudos. Identifique a unidade observacional, justifique as codificações e escreva o que o gráfico não permite concluir.
Exercício 1
Usamos body_mass_g, porque é quantitativa e tem unidades claras. A largura menor mostra mais detalhe; a maior torna a forma geral mais estável para uma leitura rápida.
ggplot(ds, aes(body_mass_g)) +
geom_histogram(binwidth = 250, boundary = 0, colour = "white")
ggplot(ds, aes(body_mass_g)) +
geom_histogram(binwidth = 500, boundary = 0, colour = "white")Nos dois gráficos procuramos a concentração principal e possíveis lacunas, não um número exacto de grupos. Antes de descrever um pico, alteraria ainda a largura e verificaria se ele persiste. O histograma mostra a distribuição dos pinguins observados, mas não resolve a dependência, a amostragem ou a causa de qualquer diferença.
Exercício 2
Uma solução que não depende de cor usa a posição para a espécie e a forma para o sexo:
ggplot(ds, aes(species, body_mass_g, shape = sex)) +
geom_jitter(width = 0.12, alpha = 0.6) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)", shape = "Sexo")A posição permite comparar a massa entre espécies e a forma acrescenta o sexo. O jitter torna casos sobrepostos visíveis. Esta versão pode ficar difícil de ler com muitas categorias de forma e não mostra tão bem a comparação contínua entre sexos como painéis separados. Para privilegiar essa comparação, podemos usar:
ggplot(ds, aes(species, body_mass_g)) +
geom_jitter(width = 0.12, alpha = 0.6) +
facet_wrap(~ sex) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)")Os painéis reduzem a sobreposição entre sexos, mas tornam mais difícil ver todos os casos numa única posição e não substituem informação sobre o delineamento.
Exercício 3
Primeiro mostramos cada pinguim. Assim podemos ver dispersão, assimetria e casos extremos, em vez de deixar a média falar sozinha:
ggplot(ds, aes(species, body_mass_g)) +
geom_jitter(width = 0.12, alpha = 0.35) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)")Depois calculamos uma média e um intervalo de confiança t de 95% por espécie. O intervalo descreve a incerteza da média sob as hipóteses indicadas; não é a amplitude dos pinguins nem um intervalo de previsão para um novo pinguim.
resumo <- aggregate(body_mass_g ~ species, ds, function(x) {
media <- mean(x)
erro <- sd(x) / sqrt(length(x))
c(n = length(x), media = media, erro = erro)
})
resumo <- data.frame(species = resumo$species, resumo$body_mass_g)
resumo$limite_inferior <- resumo$media - qt(.975, resumo$n - 1) * resumo$erro
resumo$limite_superior <- resumo$media + qt(.975, resumo$n - 1) * resumo$erro
ggplot(ds, aes(species, body_mass_g)) +
geom_jitter(width = 0.12, alpha = 0.25) +
geom_errorbar(data = resumo,
aes(x = species, ymin = limite_inferior, ymax = limite_superior),
width = 0.12, inherit.aes = FALSE) +
geom_point(data = resumo, aes(x = species, y = media), inherit.aes = FALSE,
shape = 21, fill = "white", size = 2.8) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)")A primeira figura responde «como estão distribuídos os casos?»; a segunda acrescenta «quão precisa é a média estimada?». Ainda precisamos de saber como os pinguins foram amostrados, qual é a unidade independente e para que população se pretende generalizar.
Exercício 4
Usamos duas medidas quantitativas e uma recta linear por espécie:
ggplot(ds, aes(flipper_length_mm, body_mass_g, colour = species)) +
geom_point(alpha = 0.5) +
geom_smooth(method = "lm", se = TRUE) +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)",
colour = "Espécie")geom_smooth(method = "lm") estima uma relação linear entre massa e comprimento da barbatana dentro de cada espécie; a faixa mostra a incerteza da média prevista pelo ajuste. A figura descreve associações nos dados observados. Por exemplo, tamanho corporal, idade ou condições da ilha podem contribuir para as duas medidas. Sem delineamento e análise apropriados, o gráfico não separa essas alternativas nem demonstra que alterar uma medida causaria a outra.
Exercício 5
As duas versões usam as mesmas variáveis, mas distribuem a informação de modo diferente:
ggplot(ds, aes(flipper_length_mm, body_mass_g)) +
geom_point(alpha = 0.6) +
facet_wrap(~ island) +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)")
ggplot(ds, aes(flipper_length_mm, body_mass_g, colour = island)) +
geom_point(alpha = 0.6) +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)",
colour = "Ilha")As facetas mantêm uma escala comum e tornam um grupo pequeno mais fácil de localizar dentro do seu painel, embora possam esconder a sobreposição entre ilhas. A cor conserva essa sobreposição e facilita a comparação directa de posições, mas grupos pequenos podem desaparecer por baixo de outros pontos e a figura fica dependente da leitura das cores.
Exercício 6
geom_bar() conta linhas, enquanto position = "fill" normaliza cada barra. Aqui o denominador da proporção é o total de pinguins de cada espécie:
ggplot(ds, aes(species)) +
geom_bar(fill = "grey55") +
theme_classic() +
labs(x = "Espécie", y = "Número de pinguins")
ggplot(ds, aes(species, fill = sex)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = function(x) paste0(round(100 * x), "%")) +
theme_classic() +
labs(x = "Espécie", y = "Proporção dentro da espécie", fill = "Sexo")Assim, uma frase adequada é: «A composição por sexo difere entre as espécies observadas; isto não significa que as espécies tenham o mesmo número de pinguins.» A primeira figura mostra tamanho da amostra por espécie; a segunda mostra composição e pode esconder diferenças no número total de casos.
Exercício 7
No exemplo simulado, participante é a unidade repetida e momento tem ordem pré–pós. A versão correcta liga apenas as duas observações da mesma pessoa:
ggplot(repetidos, aes(momento, score, group = participante)) +
geom_line(alpha = 0.35) +
geom_point(alpha = 0.65) +
theme_classic() +
labs(x = "Momento", y = "Resultado")Para observar o problema, podemos retirar o agrupamento e depois forçar um único grupo:
ggplot(repetidos, aes(momento, score)) +
geom_line() +
geom_point()
ggplot(repetidos, aes(momento, score, group = 1)) +
geom_line() +
geom_point()Sem group = participante, o ggplot2 não recebe a identidade que deve permanecer ligada. Com group = 1, todas as observações formam uma única linha, que liga arbitrariamente pessoas diferentes e inventa uma trajectória. Antes de adaptar o código a dados próprios, confirmaria que cada ID identifica a mesma unidade em todos os momentos, que há uma observação por momento e que a ordem de momento é a pretendida. O gráfico mostra direcções individuais, mas não estima por si só uma mudança populacional nem estabelece causalidade.
Exercício 8
Auditamos a figura de dispersão por espécie do exercício 4. São visíveis as medidas de cada pinguim, a sobreposição entre espécies e as rectas ajustadas. Ficam escondidos os pinguins excluídos por valores em falta, a incerteza sobre a amostragem e variáveis como idade ou ilha. Podemos inferir associações lineares descritivas dentro das espécies, mas não um efeito causal. Uma única alteração útil é acrescentar facet_wrap(~ sex) para tornar visível uma possível diferença entre sexos:
ggplot(ds, aes(flipper_length_mm, body_mass_g, colour = species)) +
geom_point(alpha = 0.5) +
geom_smooth(method = "lm", se = TRUE) +
facet_wrap(~ sex) +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)",
colour = "Espécie")A faceta revela heterogeneidade por sexo, mas reduz o número de casos por painel e não resolve confundimento ou dependência.
Exercício 9
Uma reprodução completa é reconstruir o gráfico de contagens sem copiar o bloco anterior. A decisão visual é contar linhas, não comparar médias:
grafico_contagens <- ggplot(ds, aes(species)) +
geom_bar(fill = "grey55") +
theme_classic() +
labs(x = "Espécie", y = "Número de pinguins")
grafico_contagensO código usa uma linha de ds por pinguim, por isso a unidade observacional é o pinguim. A altura é uma contagem da amostra observada. Não permite inferir que as espécies tenham a mesma abundância na população sem um plano de amostragem que sustente essa generalização.
Exercício 10
Acrescentamos pontos a uma caixa por espécie e sexo. A nova camada torna a variação e a sobreposição dos casos mais fáceis de ver, enquanto a figura fica mais carregada do que uma caixa isolada:
ggplot(ds, aes(species, body_mass_g, fill = sex)) +
geom_boxplot(outlier.shape = NA, alpha = 0.55) +
geom_point(aes(shape = sex),
position = position_jitterdodge(jitter.width = 0.10,
dodge.width = 0.75),
alpha = 0.35) +
theme_classic() +
labs(x = "Espécie", y = "Massa corporal (g)",
fill = "Sexo", shape = "Sexo")A caixa resume quartis; os pontos devolvem os casos individuais. O gráfico responde melhor à pergunta «há sobreposição e valores extremos?» e responde pior a uma leitura rápida de apenas quatro medianas. Continua a ser descritivo dos pinguins observados.
Exercício 11
Uma solução avançada, que pode servir de modelo para dados próprios, usa uma relação entre duas medidas e identifica a unidade na própria explicação:
ggplot(ds, aes(flipper_length_mm, body_mass_g, colour = species,
shape = species)) +
geom_point(alpha = 0.65) +
theme_classic() +
labs(x = "Comprimento da barbatana (mm)", y = "Massa corporal (g)",
colour = "Espécie", shape = "Espécie")Aqui uma linha representa um pinguim, flipper_length_mm ocupa o eixo x, a massa ocupa o eixo y e a espécie é codificada por cor e forma. Usar duas codificações redundantes ajuda leitores que não distinguem cores, embora possa tornar a legenda mais pesada. O gráfico não permite concluir causalidade, nem que os grupos representem populações com tamanhos conhecidos, nem que cada linha seja independente se o mesmo pinguim tiver sido medido mais do que uma vez. Num estudo próprio, substituiria ds pela tabela original, confirmaria a coluna que identifica a unidade e registaria a proveniência dos dados antes de publicar.
10 Conclusão
Não existe um gráfico correcto para um tipo de variável isolado. Existe uma escolha que torna uma pergunta e as suas limitações legíveis. Comece pelos dados individuais quando puder, acrescente resumos, modelos ou anotações só quando esclarecem, e deixe sempre claro o que o delineamento não permite concluir.