[1] "Winter Weather" "Winter Weather" "Drought" "Drought"
[5] "Drought" "Drought"
Week 1 Lecture 2 (async.)
August 26, 2026
.qmd files to HTML and PDF.ggplot2 basics, and the historical gallery from van Langren through Tufte.Today:
Infographics can combine data, annotation, and structure for a broad audience. They still need clear comparisons, proportional encodings, and an honest claim.
Useful graphics balance truth, function, beauty, and insight.
Two main types:
NOAA examples:
EVENT_TYPE, STATE, SOURCER stores categorical variables as factors when their level order matters. Otherwise, alphabetical order is usually the default—and often not the most useful order.
Observations form a vector \((x_1, \ldots, x_n)\). Each value belongs to one category \(C_1, C_2, \ldots, C_K\).
For NOAA Storm Events, focus first on event_type:
[1] "Winter Weather" "Winter Weather" "Drought" "Drought"
[5] "Drought" "Drought"
How should we summarize those values?
ggplot(area_counts) +
geom_rect(
aes(xmin = -side / 2, xmax = side / 2,
ymin = row - side / 2, ymax = row + side / 2),
fill = deep_gold
) +
scale_y_continuous(
breaks = area_counts$row,
labels = area_counts$event_type
) +
coord_fixed() +
labs(x = NULL, y = NULL, title = "Area is proportional to frequency") +
theme(
axis.text.x = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank()
)
For \(X=\) event type, conditioning on Pennsylvania means \(P(X=C_j\mid\text{state}=\text{PA})\). The denominator is all Pennsylvania records.
For categories \(C_1, \ldots, C_K\), a marginal or conditional distribution must satisfy
\[ p_j \ge 0 \qquad\text{and}\qquad \sum_{j=1}^{K}p_j=1. \]
The empirical probabilities are \(\hat p_j=n_j/n\). Displayed categories have positive mass; in general, zero is allowed but a negative probability is not.
events |>
filter(event_type %in% type_counts$event_type[1:8]) |>
ggplot(aes(event_type)) +
geom_bar(aes(y = after_stat(count) / sum(after_stat(count))), fill = gold) +
scale_y_continuous(labels = percent) +
labs(
title = "Condition on being in the eight most frequent categories",
x = NULL,
y = "Conditional proportion"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))type_display <- type_counts |>
mutate(event_type = if_else(row_number() <= 7, event_type, "Other")) |>
group_by(event_type) |>
summarise(n = sum(n), total = first(total), .groups = "drop") |>
mutate(
prop = n / total,
event_type = fct_reorder(event_type, prop)
)
type_display |>
ggplot(aes(event_type, prop)) +
geom_col(fill = gold) +
scale_y_continuous(labels = percent) +
labs(
title = "The complete empirical distribution sums to 100%",
subtitle = "Seven named categories plus all remaining categories",
x = NULL,
y = "Proportion"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
The bar height \(\hat p_j\) is an estimate from the observed records. A standard error asks how much that estimate would vary under a binomial-style repeated-sampling model.
For a category share, \(SE(\hat p_j)=[\hat p_j(1-\hat p_j)/n]^{1/2}\). The central limit theorem motivates the approximate 95% interval \(\hat p_j \pm 1.96SE(\hat p_j)\), often rounded to \(\hat p_j \pm 2SE(\hat p_j)\).
type_display |>
mutate(
se = sqrt(prop * (1 - prop) / total),
lower = pmax(prop - 2 * se, 0),
upper = pmin(prop + 2 * se, 1),
event_type = fct_reorder(event_type, prop)
) |>
ggplot(aes(event_type, prop)) +
geom_col(fill = gold) +
geom_errorbar(aes(ymin = lower, ymax = upper), width = 0.25, color = charcoal) +
scale_y_continuous(labels = percent) +
labs(
title = "Estimated shares of event records",
subtitle = "Approximate marginal 95% intervals",
x = NULL,
y = "Share of 2024 event records"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Two nearly identical bars can support different conclusions when sample sizes differ. The interval supplies information that bar height alone omits.
For NOAA, these intervals describe binomial-style variability in the observed records. They do not correct reporting bias, missing events, exposure differences, or changes in data-collection procedures.
Rafael Irizarry’s delightfully blunt name for a mean-with-error-bars display is a dynamite plot: the error bar is the fuse, and the bar can conceal the data.
His example concerns group means, not category proportions—but the warning travels: a summary and an error bar are not the distribution.


Example adapted from Rafael Irizarry (2019). For a group mean, \(SE(\bar x)=s/n^{1/2}\); the CLT motivates the approximate 95% interval \(\bar x\pm1.96SE(\bar x)\) under the usual sampling conditions.
forcatsStatistician Andrew Gelman’s diagnostic is more useful than “never draw circles”:
Pies and clock plots show a whole or a cycle, but they often sacrifice aligned positions and a common baseline. Use radial form when the cycle itself matters—not merely because a circle looks interesting.
The data, aesthetic mapping, and geometry stay fixed. Only the coordinate system changes.
race_base <- conditional_counts |>
ggplot(aes(x = state, y = n, fill = event_type)) +
geom_col(width = 0.8, position = "fill") +
scale_y_continuous(labels = percent) +
labs(x = NULL, y = "Within-state proportion", fill = "Event type") +
theme(legend.position = "bottom")
race_base +
coord_cartesian() +
labs(title = "Cartesian bars")

waffle_data <- type_counts |>
slice_head(n = 6) |>
mutate(tiles = pmax(1, round(100 * n / sum(n)))) |>
select(event_type, tiles) |>
uncount(tiles) |>
mutate(tile = row_number(), row = (tile - 1) %/% 10, col = (tile - 1) %% 10)
waffle_data |>
ggplot(aes(col, -row, fill = event_type)) +
geom_tile(color = "white") +
coord_equal() +
labs(fill = "Event type") +
theme_void()
Choose one NOAA categorical variable. Make an ordered bar chart and state whether the bars represent counts or proportions. Then name one source of uncertainty the graph does not capture.
Next: two categorical variables and one quantitative variable.
MaDS Data Visualization · Week 1