
21 Overlapping dot plots
theme_goosebumps()
Graphs in this chapter use fmggp2::theme_goosebumps(), modeled on the Goosebumps books: a black background, a slime green frame and title, a dark purple plot area, slime green, purple, pale green, orchid, and moss marks, and a Trattatello title.
21.1 Description
Overlapping dot plots display distributions of a continuous variable across the levels of a categorical variable.
To adjust the dot plot display to look similar to a histogram, change the method and binpositions arguments. "histodot" uses fixed width bins, while the default "dotdensity" lets the bin centers follow the data.
21.2 Set up
PACKAGES:
Install packages.
show/hide
library(ggplot2)
library(dplyr) # for tibble() and data manipulationDATA:
R.L. Stine’s Goosebumps books were everywhere in the mid 90s, and older kids tended to tear through more of them. We’ll simulate how many Goosebumps books 40 students in each of three grades read in one school year. rpois() draws each student’s count from a Poisson distribution with a different average (lambda) for each grade: 3, 6, and 9 books. Setting the seed with set.seed() means you’ll get the same 120 students every time you run the code below.
show/hide
set.seed(1992) # same 120 students every time the code runs
goosebumps_readers <- tibble(
student_id = 1:120, # 120 different students, one row each
grade = rep(c("3rd grade", "4th grade", "5th grade"), each = 40) # 40 students per grade
) |>
mutate(
grade = factor(grade,
levels = c("3rd grade", "4th grade", "5th grade")), # youngest to oldest
books_read = rpois(n(),
lambda = c(`3rd grade` = 3, `4th grade` = 6, # average books read
`5th grade` = 9)[as.character(grade)] # (looked up for each grade)
)
)
dplyr::glimpse(goosebumps_readers)
#> Rows: 120
#> Columns: 3
#> $ student_id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10…
#> $ grade <fct> 3rd grade, 3rd grade, 3rd gra…
#> $ books_read <int> 3, 5, 4, 3, 3, 4, 2, 4, 3, 5,…21.3 Distribution
Dot plots are a natural fit for small counts: every student gets one dot, and every number of books gets its own stack. With three grades stacked together, we can see two things change from grade to grade: where the stacks are centered, and how far they spread out. Below we’ll move from the students, to a picture, to the formula.
COUNT IT:
Start with the first ten students (all in 3rd grade).
show/hide
head(goosebumps_readers$books_read, 10)
#> [1] 3 5 4 3 3 4 2 4 3 5These 3rd graders read between two and five books. The summary below compares each grade’s mean and variance. For counts like these, the variance grows along with the mean, so the older students’ counts are more spread out.
show/hide
goosebumps_readers |>
group_by(grade) |>
summarise(mean = mean(books_read), variance = var(books_read),
min = min(books_read), max = max(books_read))DRAW IT:
The graph below gives each grade its own row of dot stacks (with facet_wrap()), and the dashed lines mark the average we gave each grade in rpois(). The 3rd grade stacks are tall and bunched together near three books, and the 5th grade stacks are short and spread from three books to well past a dozen.
show/hide
ggplot(goosebumps_readers, aes(x = books_read, fill = grade)) +
geom_dotplot(method = "histodot", binwidth = 1, dotsize = 0.55,
show.legend = FALSE) +
geom_vline(data = tibble(grade = factor(c("3rd grade", "4th grade",
"5th grade")), lambda = c(3, 6, 9)),
aes(xintercept = lambda), linetype = "dashed", color = "#F2E8CF") +
facet_wrap(~ grade, ncol = 1) +
scale_y_continuous(NULL, breaks = NULL) +
labs(x = "Books read")
NAME IT:
Each grade’s counts come from a Poisson distribution:
\[ X_{\text{grade}} \sim \text{Poisson}(\lambda_{\text{grade}}), \quad \lambda = (3, 6, 9) \]
For a Poisson distribution, the variance equals the mean (\(\lambda\)), so the standard deviation is \(\sqrt{\lambda}\): about 1.7 books for 3rd graders and 3 books for 5th graders. Tripling the average book count only spreads the stacks out by \(\sqrt{3} \approx 1.7\) times. The gap is a little bigger in our simulated students (1.4 books for 3rd graders and 3.3 books for 5th graders, about 2.3 times), but the 5th grade stacks still aren’t three times wider.
When we stack the three grades into one dot plot, the middle grade overlaps both of its neighbors. All three colors already share the stacks at 3, 4, 5, and 7 books. Try giving the three grades averages that are closer together in rpois() and more of the stacks will mix all three colors.
21.4 Grammar
binwidth= When method is “histodot”, this specifies bin width. Defaults to 1/30 of the range of the databinpositions= “all” determines positions of the bins with all the data taken together; this is used for aligning dot stacks across multiple groups.
CODE:
Create labels with
labs()Initialize the graph with
ggplot()and providedataMap
books_readtoxMap
gradetofillInside
geom_dotplot, setmethodto"histodot",binwidthto1(one stack per count),dotsizeto0.4(so the tallest stack fits),binpositionsto"all", andstackgroupstoTRUE(so the grades stack on top of each other instead of overplotting)Remove the
yaxis withscale_y_continuous(NULL, breaks = NULL)(its 0 to 1 scale isn’t a count of students)
show/hide
labs_ovrlp_dotplot <- labs(
title = "Goosebumps books read",
caption = "Simulated data",
subtitle = "40 students in each grade, one school year",
x = "Books read",
fill = "Grade")
ggp2_ovrlp_dotplot <- ggplot(data = goosebumps_readers,
aes(x = books_read,
fill = grade)) +
geom_dotplot(
method = "histodot",
binwidth = 1,
dotsize = 0.4,
binpositions = "all",
stackgroups = TRUE) +
scale_y_continuous(NULL, breaks = NULL)
ggp2_ovrlp_dotplot +
labs_ovrlp_dotplotGRAPH:
