2026-02-24
The goal of this chapter is to understand the visual encoding design choices for how to arrange tabular data spatially.
factor variables in R.Two values, no keys
| Idiom | Scatterplots |
|---|---|
| What: Data | Table: two quantitative value attributes |
| How: Encode | Express values with horizontal and vertical spatial position and point marks |
| Why: Task | Find trends, outliers, distribution, correlation, locate clusters |
| Scale | Items: hundreds |
One key, one value

| Idiom | Bar Charts |
|---|---|
| What: Data | Table: one quantitative value attribute, one categorical key attribute |
| How: Encode | Line marks, express values with aligned position, separate values with region |
| Why: Task | Lookup and compare values |
| Scale | Key attribute: dozens to hundreds of levels |
starwars %>%
filter(mass < 500,
!is.na(gender),
hair_color %in% c("black", "blond", "brown")) %>%
mutate(gender = factor(gender),
hair_color = factor(hair_color)) %>%
group_by(gender, hair_color) %>%
summarize(mass = sum(mass)) %>%
ggplot(aes(x = gender, y = mass, fill = hair_color)) +
geom_col() +
xlab("Gender") +
ylab("Total Mass") +
scale_fill_discrete(name = "Hair Color") +
coord_cartesian(expand = c(bottom = FALSE)) +
ggtitle("Stacked Bar Chart", subtitle = "Star Wars data") +
theme_bw()| Idiom | Stacked Bar Charts |
|---|---|
| What: Data | Multidimensional table: one quantitative value attribute, two categorical key attributes |
| How: Encode | Bar glyph with length-coded subcomponents of value attribute for each category of secondary key attribute; separate bars by category of primary key attribute |
| Why: Task | Part-to-whole relationship, lookup values, find trends |
| Scale | Key attribute (main axis): dozens to hundreds of levels, Key attribute (stacked glyph axis): several to one dozen |
| Idiom | Dot Charts |
|---|---|
| What: Data | Table: one quantitative value attribute, one ordered key attribute |
| How: Encode | Express value attribute with aligned vertical position and point marks; separate/order into horizontal regions by key attribute |
| Idiom | Line Charts |
|---|---|
| What: Data | Table: one quantitative value attribute, one ordered key attribute |
| How: Encode | Dot chart with connection marks between dots |
| Why: Task | Show trend |
| Scale | Key attribute: hundreds of levels |
Two keys, one value
hm <- starwars %>%
filter(mass < 500,
!is.na(gender),
hair_color %in% c("black", "blond", "brown")) %>%
mutate(gender = factor(gender),
hair_color = factor(hair_color)) %>%
group_by(gender, hair_color, .drop = FALSE) %>%
summarize(n = n()) %>%
ggplot(aes(x = gender, y = hair_color, fill = n)) +
geom_tile() +
coord_cartesian(expand = FALSE) +
scale_fill_viridis_b() +
ggtitle("Heat Map", subtitle = "Star Wars data") +
theme_classic()
print(hm)| Idiom | Heat Maps |
|---|---|
| What: Data | Table: two categorical key attributes, one quantitative value attribute |
| How: Encode | 2D matrix alignment of area marks, diverging color map |
| Why: Task | Find clusters, outliers; summarize |
| Scale | Items: one million; categorical attribute levels: hundreds; quantitative attribute levels: 3 - 11 |
Many keys, many values
| Idiom | SPLOMs |
|---|---|
| What: Data | Table |
| What: Derived | Ordered key attribute; list of original attributes |
| How: Encode | Scatter Plots in 2D matrix alignment |
| Why: Task | Find correlations, trends, outliers |
| Scale | Attributes: one dozen; items: dozens to hundreds |
Many keys, many values
starwars %>%
filter(mass < 500,
!is.na(gender),
hair_color %in% c("black", "blond", "brown")) %>%
mutate(gender = factor(gender),
hair_color = factor(hair_color)) %>%
ggplot(aes(x = height, y = mass)) +
facet_grid(gender ~ hair_color) +
geom_point() +
coord_cartesian(expand = FALSE) +
ggtitle("Faceted Scatter Plot", subtitle = "Star Wars data") +
theme_bw()library(patchwork)
cyclic <- data.frame(x = seq(0, 2*pi, length.out = 300)) %>%
mutate(y = sin(4*x))
a <- ggplot(cyclic, aes(x = x, y = y)) +
geom_line() +
ggtitle("Rectilinear") +
theme_bw()
b <- a +
coord_polar() +
scale_x_continuous(breaks = c(0, pi/2, pi, 3*pi/2, 2*pi),
labels = c("0", "π/2", "π", "3π/2", "2π")) +
ggtitle("Radial")
a + bSide note, the polar transformation is a key idea behind the Fast Fourier Transform
| Idiom | Radial Bar Charts |
|---|---|
| What: Data | Table: one quantitative attribute, one categorical attribute |
| How: Encode | Length coding of line marks; radial layout |

pie <- starwars %>%
filter(hair_color %in% c("black", "blond", "brown")) %>%
group_by(hair_color) %>%
summarize(n = n()) %>%
ggplot(aes(x = "", y = n, fill = hair_color)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start = 0) +
xlab(NULL) +
ylab(NULL) +
ggtitle("Pie Chart", subtitle = "Star Wars data") +
theme_classic() +
theme(axis.text.x = element_blank())
print(pie)| Idiom | Pie Charts |
|---|---|
| What: Data | Table: one quantitative attribute, one categorical attribute |
| Why: Task | Part-whole relationship |
| How: Encode | Area marks (wedges) with angle channel; radial layout |
| Scale | One dozen categories |
pa <- starwars %>%
filter(hair_color %in% c("black", "blond", "brown")) %>%
group_by(hair_color) %>%
summarize(n = n()) %>%
ggplot(aes(x = hair_color, y = n, fill = hair_color)) +
geom_col(width = 1) +
coord_polar() +
xlab(NULL) +
ylab(NULL) +
ggtitle("Polar Area Chart", subtitle = "Star Wars data") +
theme_classic() +
theme(axis.text.x = element_blank())
print(pa)| Idiom | Polar Area Charts |
|---|---|
| What: Data | Table: one quantitative attribute, one categorical attribute |
| Why: Task | Part-whole relationship |
| How: Encode | Area marks (wedges) with angle channel; radial layout |
| Scale | One dozen categories |