Data-Based Labels

Chapter 7 of Edward Tufte’s The Visual Display of Quantitative Information presents advantages of “multifunctioning graphical elements” in data visualiztions. The idea is that features like the shape of points on the plot, grid lines, or even data labels can serve multiple purposes in communicating information.

For example, take the shape of a stem plot:

library(ggplot2)
library(dplyr)
glimpse(starwars)
## Observations: 87
## Variables: 13
## $ name       <chr> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia O…
## $ height     <int> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180, …
## $ mass       <dbl> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, 77…
## $ hair_color <chr> "blond", NA, NA, "none", "brown", "brown, grey", "brown", …
## $ skin_color <chr> "fair", "gold", "white, blue", "white", "light", "light", …
## $ eye_color  <chr> "blue", "yellow", "red", "yellow", "brown", "blue", "blue"…
## $ birth_year <dbl> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.0,…
## $ gender     <chr> "male", NA, NA, "male", "female", "male", "female", NA, "m…
## $ homeworld  <chr> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan", "…
## $ species    <chr> "Human", "Droid", "Droid", "Human", "Human", "Human", "Hum…
## $ films      <list> [<"Revenge of the Sith", "Return of the Jedi", "The Empir…
## $ vehicles   <list> [<"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "I…
## $ starships  <list> [<"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1…
starwars %>%
  mutate(bmi = mass/((height/100)^2)) %>%
  # no jabba ...
  filter(!grepl("Jabba", name)) %>%
  pull(bmi) %>%
  stem(.)
## 
##   The decimal point is 1 digit(s) to the right of the |
## 
##   1 | 3
##   1 | 556777788999
##   2 | 1122233334444444
##   2 | 555555566666667778
##   3 | 123444
##   3 | 5589
##   4 | 
##   4 | 
##   5 | 1

Tufte includes as another example a design originally published in Walter Herdeg’s Graphis/Diagrams (not shown here), which uses data-based labels as a multifunction element. Below is a plot that recreates that concept using the built-in mammalian sleep data from ggplot2:

glimpse(msleep)
## Observations: 83
## Variables: 11
## $ name         <chr> "Cheetah", "Owl monkey", "Mountain beaver", "Greater sho…
## $ genus        <chr> "Acinonyx", "Aotus", "Aplodontia", "Blarina", "Bos", "Br…
## $ vore         <chr> "carni", "omni", "herbi", "omni", "herbi", "herbi", "car…
## $ order        <chr> "Carnivora", "Primates", "Rodentia", "Soricomorpha", "Ar…
## $ conservation <chr> "lc", NA, "nt", "lc", "domesticated", NA, "vu", NA, "dom…
## $ sleep_total  <dbl> 12.1, 17.0, 14.4, 14.9, 4.0, 14.4, 8.7, 7.0, 10.1, 3.0, …
## $ sleep_rem    <dbl> NA, 1.8, 2.4, 2.3, 0.7, 2.2, 1.4, NA, 2.9, NA, 0.6, 0.8,…
## $ sleep_cycle  <dbl> NA, NA, NA, 0.1333333, 0.6666667, 0.7666667, 0.3833333, …
## $ awake        <dbl> 11.9, 7.0, 9.6, 9.1, 20.0, 9.6, 15.3, 17.0, 13.9, 21.0, …
## $ brainwt      <dbl> NA, 0.01550, NA, 0.00029, 0.42300, NA, NA, NA, 0.07000, …
## $ bodywt       <dbl> 50.000, 0.480, 1.350, 0.019, 600.000, 3.850, 20.490, 0.0…
animals <- c("Asian elephant", 
             "Little brown bat",
             "Three-toed sloth", 
             "Rabbit", 
             "Giant armadillo", 
             "Tiger",
             "Chimpanzee",
             "Potoroo",
             "Lion",
             "Domestic cat",
             "Pilot whale",
             "Goat",
             "Gray seal")
msleep %>%
  filter(name %in% animals) %>%
  ggplot(aes(reorder(name,sleep_total), sleep_total)) +
  geom_col(width=0.5, fill = "snow1") +
  geom_text(aes(x = -1.5, y = sleep_total + 0.5, label =  paste0(name, " (", sleep_total, ")")), 
            color = "snow1", 
            hjust = 0, 
            vjust  = 1) +
  geom_segment(aes(x = -1.5, xend = name, y = sleep_total, yend = sleep_total), 
               color = "snow1", 
               lty = "dotted") +
  labs(x = "", y = "", title = "Total Sleep (Hours)") +
  theme(panel.background = element_rect(fill = "black"),
        plot.background = element_rect(fill = "black"),
        panel.grid = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank())

Related