2  Histograms

Graphs in this chapter use fmggp2::theme_oksoda(), built from OK Soda cans (1993 to 1995): a brushed silver background, a black frame, a gray comic panel, OK red marks and title, slate and brick accents, and a Courier New title.

Requirements:

✅ a numeric (continuous) variable


2.1 Description

A histogram cuts the x axis into boxes1 and shows how many numbers fall in each box. Each bar stands over one box, and the bar is as high as the number of values in that box. Other bar graphs have space between the bars, but the bars in a histogram touch each other, because the numbers run on without a break from one box to the next.2 The x axis shows the numbers, and the y axis shows how many values fall in each box.3

A histogram also lets us see the form the numbers take,4 which most bar graphs can’t do. A hill5 is a line that is high in the middle and low on both sides. We can ask if the bars make a hill that is the same on both sides6 (are the bars high in the middle of the x axis and low on both sides, with one high point?7 Or do the bars make more than one high point?). We can also ask if one side of the hill runs out much farther than the other8 (is there a long line of bars that get shorter and shorter near one end of the x axis?).

In ggplot2, we can make histograms with geom_histogram().

2.2 Set up

PACKAGES:

Bring in the code we’ll use.9

show/hide
library(ggplot2)
library(dplyr) # for tibble() and data manipulation

DATA:

OK Soda was a sweet drink that Coca-Cola made in 1993 for young people. It came in cans with pictures on them like the ones in a funny book,10 and words like “Things are going to be OK.” If you don’t remember it, it’s worth reading this story about it or watching these very funny short shows made to sell it.

We’ll make up11 one day of sales at 300 stores where OK Soda was tried out first,12 using rnorm(),13 and round each store’s sales to a whole number of cans. Starting with set.seed()14 means you’ll get the same 300 values every time you run the code below.

show/hide
set.seed(1014) # same 300 stores every time the code runs
ok_soda_sales <- tibble(
  store_id = 1:300, # 300 different stores, one row each
  cans = round(
    rnorm(n = 300, mean = 45, sd = 12) # cans sold in one day (centered on 45)
    )
)
glimpse(ok_soda_sales)
#> Rows: 300
#> Columns: 2
#> $ store_id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, …
#> $ cans     <dbl> 28, 48, 16, 45, 52, 59, 23, 42,…

2.3 Distribution

You don’t have to be very good with numbers15 to read a histogram, but it helps to know what form to look for. Because we made up cans, we already know the answer: every value came from one hill16 with a mean of 45 cans, and a store usually falls about 12 cans from that mean.17 Below we’ll move from the numbers, to a graph, to the rule behind it.18

COUNT IT:

Start with the first ten stores.

show/hide
head(ok_soda_sales$cans, 10)
#>  [1] 28 48 16 45 52 59 23 42 42 42

Most of these values land within 12 cans of 45, and a few (like 16 and 23) fall farther out. If we put all 300 stores into three groups (more than 12 cans below the mean, within 12 cans of the mean, and more than 12 cans above the mean),19 the middle group holds most of the stores.

To find the ends of these groups, we take 12 cans away from the mean and add 12 cans to it:

  • Low end: \(45 - 12 = 33\) cans

  • High end: \(45 + 12 = 57\) cans

A store that sold fewer than 33 cans is in the low group, a store that sold 33 to 57 cans is in the middle group, and a store that sold 58 or more cans is in the high group. In the code below, cut() puts the breaks half a can past each end (32.5 and 57.5), so a store with exactly 33 or 57 cans is in the middle group.

show/hide
ok_soda_ranges <- ok_soda_sales |>
  mutate(range = cut(cans,
    breaks = c(-Inf, 32.5, 57.5, Inf),
    labels = c("under 33", "33 to 57", "58 and over"))) |>
  count(range)
ok_soda_ranges

DRAW IT:

A bar model shows the three groups as parts of one whole. The full bar is all 300 stores, and the middle part takes up about two thirds of it.

show/hide
ggplot(data = ok_soda_ranges,
    aes(x = n, y = "300 stores", fill = range)) +
  geom_col(position = position_stack(reverse = TRUE),
    color = "white") +
  geom_text(aes(label = n), color = "white",
    position = position_stack(vjust = 0.5, reverse = TRUE)) +
  labs(x = "Number of stores", y = NULL,
    fill = "Cans sold")

A histogram is the same idea, cut into 30 smaller pieces and stood on end. The broken lines below mark 33 and 57 cans, the ends of the middle part of the bar model.

show/hide
ggplot(data = ok_soda_sales,
    aes(x = cans)) +
  geom_histogram() +
  geom_vline(xintercept = c(33, 57), linetype = "dashed") +
  labs(x = "Cans sold")

NAME IT:

The rnorm() step that made up the data comes down to one short rule:20

\[ X \sim \text{Normal}(\mu = 45, \sigma = 12) \]

The mean (\(\mu\)) sets where the highest bars stand on the x axis. The other number (\(\sigma\)) is how far a store usually falls from the mean, so it sets how far out the bars reach. For a hill like this, about 68% of values fall within one \(\sigma\) of \(\mu\), and 95% fall within two. Our made-up stores come close: 66% are within one \(\sigma\) and 95% are within two.

The small difference between the rule and our stores comes from chance alone.21 Change the number in set.seed() and the bars move, but the hill keeps its middle near 45 cans.

2.4 Grammar

CODE:

  1. Make the words around the graph22 with labs()

  2. Start the graph with ggplot() and give it data

  3. Put cans on the x axis

  4. Add geom_histogram() on top23

  5. Make each box 3 cans wide (binwidth), with one box in the middle at 45 cans (center)24

show/hide
labs_histogram <- labs(
  title = "OK Soda",
  caption = "Simulated data",
  subtitle = "Cans sold per store in one day",
  x = "Cans sold")

ggp2_hist <- ggplot(data = ok_soda_sales,
     aes(x = cans)) + 
     geom_histogram(binwidth = 3, center = 45) 

ggp2_hist + 
  labs_histogram

GRAPH:

We set binwidth because, unless we pick a number, there are 30 boxes,25 and ‘you should always override this value, exploring multiple widths to find the best to illustrate the stories in your data.’

show/hide
ggp2_hist_bins45 <- ggplot(data = ok_soda_sales,
     aes(x = cans)) + 
     geom_histogram(bins = 45) 

ggp2_hist_bins45 + 
  labs_histogram + 
  labs(subtitle = "45 bins")

ggp2_hist_bins15 <- ggplot(data = ok_soda_sales,
     aes(x = cans)) + 
     geom_histogram(bins = 15) 

ggp2_hist_bins15 + 
  labs_histogram + 
  labs(subtitle = "15 bins")


  1. Bins. Each bin is an interval of values on the x axis.↩︎

  2. The variable is continuous, so there are no gaps between the bins.↩︎

  3. The frequency (count) of observations in each bin.↩︎

  4. The shape of the variable’s distribution.↩︎

  5. A bell shaped curve, like the normal distribution.↩︎

  6. Normality: a symmetric, bell shaped distribution.↩︎

  7. A peak, or mode. A distribution with more than one peak is multimodal.↩︎

  8. Skewness: a long tail of decreasing bars on one end of the x axis.↩︎

  9. Install and load the packages.↩︎

  10. Comic book art.↩︎

  11. Simulate.↩︎

  12. Test market stores, where a new product is sold before a wider launch.↩︎

  13. rnorm() draws values from a normal distribution with mean = 45 and sd = 12.↩︎

  14. Setting the random seed.↩︎

  15. A math whiz or statistician.↩︎

  16. A normal distribution.↩︎

  17. A standard deviation of 12 cans.↩︎

  18. The formula.↩︎

  19. Below, within, and above one standard deviation of the mean.↩︎

  20. A single statement.↩︎

  21. Sampling variation.↩︎

  22. The labels (title, subtitle, caption, and axis titles).↩︎

  23. Add the geom_histogram() layer.↩︎

  24. The bin width. With binwidth = 3 and center = 45, each bin holds three whole numbers of cans (for example, 44, 45, and 46), so no value falls on a bin edge.↩︎

  25. The default number of bins is 30.↩︎