EAE1106 - Métodos Computacionais para Economia
Faculdade de
Economia, Administração, Contabilidade e Atuária
Universidade de São
Paulo
Prof. Arthur Viaro
1º semestre de 2026
O ggplot2 é baseado na ideia de gramática dos
gráficos: assim como uma frase em português é construída com
sujeito + verbo + objeto, um gráfico é construído com dados +
mapeamentos + geometria.
Isso significa que você descreve o gráfico que quer ver, em vez de desenhá-lo passo a passo. Na prática:
| O que você quer mostrar? | Componente no ggplot2 |
|---|---|
| De onde vêm os dados? | ggplot(data = ...) |
| Quais variáveis nos eixos? | aes(x = ..., y = ...) |
| Como representar? | geom_point(), geom_line(),
geom_col() … |
| Cor, tamanho, forma? | aes(color = ..., size = ...) ou argumentos fixos |
| Subgráficos? | facet_wrap() / facet_grid() |
| Aparência geral? | theme_minimal(), theme_bw() … |
| Títulos e rótulos? | labs(...) |
## Rows: 344
## Columns: 8
## $ species <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
## $ island <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse…
## $ bill_length_mm <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, …
## $ bill_depth_mm <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, …
## $ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186…
## $ body_mass_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, …
## $ sex <fct> male, female, female, NA, female, male, female, male…
## $ year <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007…
Todo gráfico ggplot2 tem a mesma estrutura: uma chamada
ggplot() seguida de camadas adicionadas com
+.
# Passo 1 — só definir dados e eixos: área em branco com escala correta
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g))# Passo 2 — adicionar uma geometria: os dados ganham forma
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()# Passo 3 — mapear cor para espécie: grupos visualmente distintos
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point()# Passo 4 — adicionar rótulos e tema: gráfico pronto para publicação
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point(alpha = 0.7) +
labs(
title = "Comprimento da nadadeira vs. massa corporal",
x = "Comprimento da nadadeira (mm)",
y = "Massa corporal (g)",
color = "Espécie",
caption = "Fonte: Gorman et al. (2014)"
) +
theme_minimal()Esses quatro passos — dados, eixos, geometria, acabamento — são o ciclo de trabalho de qualquer gráfico ggplot2.
aes)As estéticas são o canal de comunicação entre variáveis nos dados e propriedades visuais do gráfico.
| Estética | O que controla |
|---|---|
x, y |
Posição nos eixos |
color |
Cor de pontos, linhas e contornos |
fill |
Cor de preenchimento (barras, áreas) |
size |
Tamanho de pontos e espessura de linhas |
shape |
Forma dos pontos |
alpha |
Transparência (0 = invisível, 1 = opaco) |
linetype |
Tipo de linha (sólida, tracejada, etc.) |
Esta é uma das confusões mais comuns no ggplot2:
aes(): a estética varia
conforme os dados (mapeia uma variável)aes(): a estética é fixa,
igual para todos os elementos# MAPEADA: cada espécie recebe uma cor diferente automaticamente
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point()# FIXA: todos os pontos têm a mesma cor
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(color = "steelblue", size = 2.5, alpha = 0.6)Estéticas definidas em ggplot() são
herdadas por todos os geoms. Estéticas definidas dentro
de um geom se aplicam apenas a ele.
# GLOBAL: a cor por espécie aparece tanto nos pontos quanto nas linhas
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE)# LOCAL: apenas os pontos têm cor por espécie; a linha é única para todos
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = species)) +
geom_smooth(method = "lm", se = FALSE, color = "gray30")Qual usar? Se a estética faz sentido para todos os geoms, defina globalmente. Se for específica de um geom, defina localmente.
geom_point() — DispersãoIdeal para visualizar relações entre duas variáveis numéricas.
# Linha de tendência por grupo com geom_smooth
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
geom_point(alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE) +
labs(
title = "Comprimento vs. profundidade do bico por espécie",
x = "Comprimento do bico (mm)", y = "Profundidade do bico (mm)", color = "Espécie"
) +
theme_minimal()# O Paradoxo de Simpson — um exemplo clássico de visualização enganosa
# A correlação agregada (linha preta) vai em direção oposta à de cada grupo
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +
geom_point(aes(color = species), alpha = 0.6) +
geom_smooth(method = "lm", color = "black", se = TRUE, linewidth = 1.2) +
geom_smooth(aes(color = species), method = "lm", se = FALSE, linewidth = 0.8) +
labs(
title = "Paradoxo de Simpson",
subtitle = "A correlação agregada (preta) é negativa — o oposto da correlação dentro de cada espécie",
x = "Comprimento do bico (mm)", y = "Profundidade do bico (mm)", color = "Espécie"
) +
theme_minimal()geom_line() — Séries TemporaisIdeal para dados com ordem no eixo x, especialmente séries temporais.
# PIB do Brasil em trilhões de USD (2000–2023)
pib_brasil <- tibble(
ano = 2000:2023,
pib = c(
1.20, 1.32, 1.49, 1.58, 1.67, 1.89, 2.09, 2.19, 2.28, 2.02,
2.21, 2.61, 2.46, 2.47, 2.46, 1.80, 1.80, 2.06, 1.89, 1.87,
1.44, 1.61, 1.92, 2.08
)
)
ggplot(pib_brasil, aes(x = ano, y = pib)) +
geom_line(color = "#1a6eb5", linewidth = 1.1) +
geom_point(color = "#1a6eb5", size = 2) +
scale_x_continuous(breaks = seq(2000, 2023, 4)) +
scale_y_continuous(
name = "Trilhões de USD (nominal)", # Nome do eixo
breaks = seq(1, 3, by = 0.5), # Linhas de grade
limits = c(1, 3), # Valores mínimo e máximo do eixo
labels = scales::label_dollar() # Transforma "1.5" em "$1.5"
) +
labs(
title = "PIB do Brasil (2000–2023)",
x = NULL,
caption = "Fonte: Banco Mundial"
) +
theme_minimal()# Múltiplas séries: note o uso de group para conectar pontos do mesmo país
pib_latam <- tibble(
ano = rep(2015:2022, 3),
pais = rep(c("Brasil", "México", "Colômbia"), each = 8),
pib = c(
1.80, 1.80, 2.06, 1.89, 1.87, 1.44, 1.61, 1.92,
1.17, 1.08, 1.16, 1.15, 1.32, 1.08, 1.28, 1.30,
0.29, 0.28, 0.31, 0.33, 0.33, 0.27, 0.31, 0.34
)
)
ggplot(pib_latam, aes(x = ano, y = pib, color = pais, group = pais)) +
geom_line(linewidth = 1.1) +
geom_point(size = 2.5) +
scale_color_manual(values = c("#125689", "#9E0000", "#F4A261")) +
labs(
title = "PIB nominal — Brasil, México e Colômbia (2015–2022)",
x = NULL, y = "Trilhões de USD", color = NULL
) +
theme_minimal() +
theme(legend.position = "bottom")geom_col() e geom_bar() — Barrasgeom_bar(): conta as observações
automaticamente (sem y)geom_col(): usa valores pré-calculados
(precisa de y)# geom_bar: conta por espécie automaticamente
ggplot(penguins, aes(x = species, fill = species)) +
geom_bar(show.legend = FALSE) +
scale_fill_brewer(palette = "Set2") +
labs(title = "Número de pinguins por espécie", x = NULL, y = "Contagem") +
theme_minimal()# geom_col com reorder: ordenar barras por valor (muito mais legível!)
resumo_pib <- tibble(
pais = c("Brasil", "México", "Argentina", "Colômbia", "Chile", "Peru"),
pib = c(2.08, 1.30, 0.63, 0.34, 0.30, 0.24)
)
ggplot(resumo_pib, aes(x = pib, y = reorder(pais, pib))) +
geom_col(fill = "#2c7bb6", width = 0.7) +
geom_text(
aes(label = scales::label_dollar()(pib)),
hjust = -0.1,
size = 3.5
) +
scale_x_continuous(
expand = expansion(mult = c(0, 0.15)), # Evita que as barras encostem na borda e deixa espaço para os rótulos
labels = scales::label_dollar() # Adiciona $ no eixo horizontal
) +
labs(
title = "PIB nominal — América do Sul e México (2023)",
x = "Trilhões de USD", y = NULL
) +
theme_minimal()# Barras empilhadas e normalizadas para comparar proporções
ggplot(penguins, aes(x = species, fill = sex)) +
geom_bar(position = "fill") + # Transforma contagens em proporções
scale_y_continuous(labels = scales::percent) +
scale_fill_brewer(palette = "Set1", na.value = "gray70") +
labs(title = "Proporção de sexos por espécie", x = NULL, y = NULL, fill = "Sexo") +
theme_minimal()geom_histogram() — Distribuição de Uma Variável# O parâmetro bins (ou binwidth) afeta muito a interpretação — teste diferentes valores
ggplot(penguins, aes(x = body_mass_g, fill = species)) +
geom_histogram(
binwidth = 250,
alpha = 0.7,
position = "identity", # sobreposição de barras para comparar distribuições
color = "white"
) +
scale_fill_brewer(palette = "Set2") +
labs(
title = "Distribuição da massa corporal por espécie",
x = "Massa corporal (g)",
y = "Contagem",
fill = "Espécie"
) +
theme_minimal()geom_boxplot() e geom_violin() —
Comparando DistribuiçõesO boxplot resume a distribuição em 5 números (mínimo, Q1, mediana, Q3, máximo). O violin mostra a distribuição completa.
# Boxplot + pontos reais: você vê o resumo E os dados individuais
ggplot(penguins, aes(x = species, y = body_mass_g, fill = species)) +
geom_boxplot(alpha = 0.5, outlier.shape = NA, show.legend = FALSE) +
geom_jitter(
aes(color = species),
width = 0.15,
alpha = 0.4,
size = 1.5,
show.legend = FALSE
) +
scale_fill_brewer(palette = "Set2") +
scale_color_brewer(palette = "Set2") +
labs(
title = "Massa corporal por espécie",
x = NULL,
y = "Massa corporal (g)"
) +
theme_minimal()# Violin + boxplot: a combinação mais informativa
ggplot(penguins, aes(x = species, y = flipper_length_mm, fill = species)) +
geom_violin(alpha = 0.4, show.legend = FALSE) +
geom_boxplot(width = 0.15, outlier.shape = NA, show.legend = FALSE) +
scale_fill_brewer(palette = "Set2") +
labs(
title = "Comprimento da nadadeira por espécie",
subtitle = "Violin mostra a distribuição completa; boxplot resume as estatísticas",
x = NULL, y = "Comprimento da nadadeira (mm)"
) +
theme_minimal()geom_density() — Curva de DensidadeAlternativa mais suave ao histograma. Útil quando há sobreposição de grupos.
ggplot(penguins, aes(x = body_mass_g, fill = species)) +
geom_density(alpha = 0.45) +
scale_fill_brewer(palette = "Set2") +
labs(
title = "Distribuição de massa corporal por espécie",
x = "Massa corporal (g)", y = "Densidade", fill = "Espécie"
) +
theme_minimal()geom_smooth() — Linha de Tendência# method = "lm": regressão linear com intervalo de confiança
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(alpha = 0.4) +
geom_smooth(method = "lm", color = "#e74c3c", linewidth = 1.2) +
labs(
title = "Comprimento da nadadeira vs. massa corporal",
subtitle = "Linha de regressão linear com intervalo de confiança de 95%",
x = "Comprimento da nadadeira (mm)", y = "Massa corporal (g)"
) +
theme_minimal()A força do ggplot2 está em sobrepor camadas. Cada
geom_*() é uma camada independente que compartilha o mesmo
sistema de coordenadas.
# Série temporal com anotações — cada elemento é uma camada
library(nycflights13)
flights |>
filter(!is.na(arr_delay)) |>
group_by(month) |>
summarize(atraso_med = mean(arr_delay), n = n()) |>
ggplot(aes(x = month, y = atraso_med)) +
# Camada 1: área sombreada
geom_area(fill = "#74b9ff", alpha = 0.3) +
# Camada 2: linha principal
geom_line(color = "#0984e3", linewidth = 1.2) +
# Camada 3: pontos (tamanho proporcional ao volume de voos)
geom_point(aes(size = n), color = "#0984e3", alpha = 0.8) +
# Camada 4: linha de referência no zero
geom_hline(yintercept = 0, linetype = "dashed", color = "gray40") +
# Camada 5: rótulos de valor
geom_text(aes(label = round(atraso_med, 1)), vjust = -1.2, size = 3) +
scale_x_continuous(breaks = 1:12, labels = month.abb) +
labs(
title = "Atraso médio de chegada por mês — NYC (2013)",
x = NULL, y = "Minutos", size = "Nº de voos"
) +
theme_minimal()Facetamento cria pequenos múltiplos — o mesmo gráfico repetido para subconjuntos dos dados. É uma das formas mais eficazes de mostrar padrões em múltiplos grupos.
# facet_wrap: grade automática com uma variável
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = sex)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE) +
facet_wrap(~species) +
scale_color_brewer(palette = "Set1", na.value = "gray60") +
labs(
title = "Massa corporal vs. nadadeira por espécie e sexo",
x = "Comprimento da nadadeira (mm)", y = "Massa corporal (g)", color = "Sexo"
) +
theme_minimal()# facet_grid: grade com duas variáveis (linhas ~ colunas)
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +
geom_point(alpha = 0.5, color = "steelblue") +
facet_grid(sex ~ species) +
labs(
title = "Medidas do bico por espécie (colunas) e sexo (linhas)",
x = "Comprimento do bico (mm)", y = "Profundidade do bico (mm)"
) +
theme_minimal()# scales = "free": cada painel tem sua própria escala — use com cuidado!
ggplot(pib_latam, aes(x = ano, y = pib)) +
geom_line(color = "steelblue", linewidth = 1) +
geom_point(color = "steelblue") +
facet_wrap(~pais, scales = "free_y") +
labs(
title = "PIB por país (escalas independentes)",
subtitle = "Atenção: eixos y diferentes dificultam comparação direta entre países",
x = NULL, y = "Trilhões de USD"
) +
theme_minimal()As escalas controlam como os dados são mapeados para propriedades visuais — e como os eixos e legendas são formatados.
# scale_color_manual: cores definidas por você
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point(alpha = 0.7) +
scale_color_manual(values = c(
"Adelie" = "#e74c3c",
"Chinstrap" = "#9b59b6",
"Gentoo" = "#27ae60"
)) +
labs(x = "Nadadeira (mm)", y = "Massa (g)", color = "Espécie") +
theme_minimal()# scale_color_brewer: paletas testadas e acessíveis a daltônicos
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point(alpha = 0.7) +
scale_color_brewer(palette = "Dark2") +
labs(x = "Nadadeira (mm)", y = "Massa (g)", color = "Espécie") +
theme_minimal()# scale_color_viridis_c: para variáveis contínuas (boa para impressão)
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = body_mass_g)) +
geom_point(size = 2.5, alpha = 0.8) +
scale_color_viridis_c(option = "plasma", name = "Massa (g)") +
labs(
title = "Medidas do bico coloridas por massa corporal",
x = "Comprimento do bico (mm)", y = "Profundidade do bico (mm)"
) +
theme_minimal()# Controle de eixos: breaks, labels, limites
ggplot(pib_brasil, aes(x = ano, y = pib)) +
geom_line(color = "steelblue", linewidth = 1) +
scale_x_continuous(breaks = seq(2000, 2023, by = 4)) +
scale_y_continuous(
labels = scales::label_dollar(accuracy = 0.01),
limits = c(0, NA) # Forçar zero no eixo y
) +
labs(title = "PIB do Brasil (2000–2023)", x = NULL, y = NULL) +
theme_minimal()# Escala logarítmica — essencial quando os dados cobrem várias ordens de grandeza
top_paises <- tibble(
pais = c("EUA", "China", "Alemanha", "Japão", "Brasil", "Índia"),
pib = c(25.46, 17.96, 4.07, 4.23, 2.08, 3.39),
pop = c(335, 1412, 84, 125, 215, 1429)
)
ggplot(top_paises, aes(x = pop, y = pib, label = pais)) +
geom_point(aes(size = pib), color = "steelblue", alpha = 0.7, show.legend = FALSE) +
geom_text(vjust = -0.9, size = 3.5, fontface = "bold") +
scale_x_log10(labels = scales::comma) +
scale_y_log10(labels = scales::label_dollar(accuracy = 0.01)) +
labs(
title = "PIB vs. População — escala log-log",
subtitle = "Na escala log, distâncias iguais representam razões iguais",
x = "População (milhões, escala log)", y = "PIB (trilhões USD, escala log)"
) +
theme_minimal()O tema controla toda a aparência que não é dado: fundo, grade, fontes, margens.
p_base <- ggplot(penguins, aes(x = species, y = body_mass_g, fill = species)) +
geom_boxplot(show.legend = FALSE) +
scale_fill_brewer(palette = "Set2") +
labs(title = "Massa corporal por espécie", x = NULL, y = "Massa corporal (g)")
# Os temas mais usados
p_base + theme_minimal() + labs(subtitle = "theme_minimal()")# Personalizações manuais com theme()
p_base +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 15),
plot.subtitle = element_text(color = "gray50"),
axis.text = element_text(size = 11),
panel.grid.major = element_line(color = "gray90"),
panel.grid.minor = element_blank(),
legend.position = "bottom"
)# geom_text e geom_label para anotar pontos
ggplot(top_paises, aes(x = pop, y = pib)) +
geom_point(aes(size = pib), color = "#2980b9", alpha = 0.7, show.legend = FALSE) +
geom_label(aes(label = pais),
vjust = -0.8, size = 3.2, fill = "white",
label.padding = unit(0.15, "lines")
) +
scale_size_continuous(range = c(3, 12)) +
labs(
title = "Maiores economias do mundo (2023)",
x = "População (milhões)", y = "PIB (trilhões USD)"
) +
theme_minimal()# annotate() para anotações livres (não amarradas a dados)
ggplot(pib_brasil, aes(x = ano, y = pib)) +
geom_line(color = "#1a6eb5", linewidth = 1.1) +
annotate("rect",
xmin = 2015, xmax = 2016, ymin = -Inf, ymax = Inf,
fill = "#e74c3c", alpha = 0.15
) +
annotate("text",
x = 2015.5, y = 2.55,
label = "Recessão\n2015–16", color = "#e74c3c", size = 3.2, hjust = 0.5
) +
annotate("rect",
xmin = 2020, xmax = 2020.5, ymin = -Inf, ymax = Inf,
fill = "#8e44ad", alpha = 0.15
) +
annotate("text",
x = 2020.25, y = 2.55,
label = "COVID\n2020", color = "#8e44ad", size = 3.2, hjust = 0.5
) +
scale_x_continuous(breaks = seq(2000, 2023, 4)) +
labs(
title = "PIB do Brasil (2000–2023)",
x = NULL, y = "Trilhões de USD (nominal)"
) +
theme_minimal()p_final <- ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point(alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE) +
scale_color_brewer(palette = "Set2") +
labs(
title = "Pinguins das Ilhas Palmer",
x = "Comprimento da nadadeira (mm)",
y = "Massa corporal (g)",
color = "Espécie"
) +
theme_minimal(base_size = 12)
# PNG para apresentações e web (dpi = 300 para impressão)
ggsave("pinguins.png", plot = p_final, width = 8, height = 5, dpi = 300)
# PDF vetorial para artigos e relatórios
ggsave("pinguins.pdf", plot = p_final, width = 8, height = 5)| Conceito | ggplot2 (R) | Python |
|---|---|---|
| Filosofia | Declarativo (descreva o gráfico) | Imperativo (matplotlib) / misto (seaborn) |
| Dispersão | geom_point() |
sns.scatterplot() |
| Linha | geom_line() |
sns.lineplot() |
| Barras | geom_col() / geom_bar() |
sns.barplot() |
| Histograma | geom_histogram() |
sns.histplot() |
| Boxplot | geom_boxplot() |
sns.boxplot() |
| Cor por grupo | aes(color = variavel) |
hue = "variavel" (seaborn) |
| Subgráficos | facet_wrap() / facet_grid() |
sns.FacetGrid() |
| Temas | theme_minimal() etc. |
sns.set_theme() / plt.style.use() |
1. Usando o dataset penguins, crie um
gráfico de dispersão de bill_length_mm
vs. bill_depth_mm. Mapeie a cor para species e
o tamanho para body_mass_g. Adicione linhas de tendência
linear por espécie e títulos em português.
2. Com flights, crie um gráfico de
barras horizontais mostrando os 10 destinos mais frequentes. Ordene as
barras por frequência com reorder().
3. Crie um histograma do atraso de partida
(dep_delay) dos voos com facet_wrap() por
origem (EWR, JFK, LGA). Use
scales = "free_y" e explique por que as distribuições
diferem.
Material baseado em: Wickham, H., Çetinkaya-Rundel, M. & Grolemund, G. (2023). R for Data Science, 2ª ed. — O’Reilly.