Skip to main content

Introduction to Modern Statistics Second Edition

Chapter 6 Applications: Explore

Section 6.1 Case study: Effective communication of exploratory results

Graphs can powerfully communicate ideas directly and quickly. We all know, after all, that “a picture is worth 1000 words.” Unfortunately, however, there are times when an image conveys a message which is inaccurate or misleading.
This chapter focuses on how graphs can best be utilized to present data accurately and effectively. Along with data modeling, creative visualization is somewhat of an art. However, even with an art, there are recommended guiding principles. We provide a few best practices for creating data visualizations.

Subsection 6.1.1 Keep it simple

When creating a graphic, keep in mind what it is that you’d like your reader to see. Colors should be used to group items or differentiate levels in meaningful ways. Colors can be distracting when they are only used to brighten up the plot.
Consider a manufacturing company that has summarized its costs into five different categories. In the two graphics provided in Figure 6.2, notice that the magnitudes in the pie chart in Figure 6.2(a) are difficult for the eye to compare. That is, can your eye tell how different “Buildings and administration” is from “Workplace materials” when looking at the slices of pie? Additionally, the colors in the pie chart do not mean anything and are therefore distracting. Lastly, the three-dimensional aspect of the image does not improve the reader’s ability to understand the data presented.
As an alternative, a bar plot has been provided in Figure 6.2(b). Notice how much easier it is to identify the magnitude of the differences across categories while not being distracted by other aspects of the image. Typically, a bar plot will be easier for the reader to digest than a pie chart, especially if the categorical data being plotted has more than just a few levels.
Listing 6.1. R code for creating pie chart and bar plot comparison
expenses <- tribble(
  ~category,                     ~value,
  "Cutting tools"                , 0.03,
  "Buildings and Administration" , 0.22,
  "Labor"                        , 0.31,
  "Machinery"                    , 0.27,
  "Workplace materials"          , 0.17
) |>
  mutate(value =  value * 100) |>
  uncount(weights = value)

# Pie chart (3D image)
knitr::include_graphics("images/pie-3d.jpg")

# Bar plot
ggplot(expenses, aes(x = fct_infreq(category))) +
  geom_bar() +
  theme_minimal() +
  coord_flip() +
  labs(x = NULL, y = NULL)
A three dimensional pie chart showing that the biggest sources of cost are labor, machinery, and buildings and administration, in that order. Very little of the costs are due to cutting tools.
(a) A three-dimensional pie chart.
A bar plot showing that the biggest sources of cost are labor, machinery, and buildings and administration, in that order. Very little of the costs are due to cutting tools.
(b) A bar plot.
Figure 6.2. Same information displayed with two very different visualizations.

Subsection 6.1.2 Use color to draw attention

There are many reasons why you might choose to add color to your plots. An important principle to keep in mind is to use color to draw attention. Of course, you should still think about how visually pleasing your visualization is, and if you’re adding color for making it visually pleasing without drawing attention to a particular feature, that might be fine. However, you should be critical of default coloring and explicitly decide whether to include color and how. Notice that in Figure 6.4(b) the coloring is done in such a way to draw the reader’s attention to one particular piece of information. The default coloring in Figure 6.4(a) can be distracting and makes the reader question, for example, is there something similar about the red and purple bars? Also note that not everyone sees color the same way, often it’s useful to add color and one more feature (e.g., pattern) so that you can refer to the features you’re drawing attention to in multiple ways, as shown in Figure 6.5.
Listing 6.3. R code for demonstrating effective use of color to draw attention
# Default coloring
ggplot(expenses, aes(y = fct_infreq(category), fill = category)) +
  geom_bar(show.legend = FALSE) +
  scale_fill_openintro("five") +
  labs(x = NULL, y = NULL) +
  scale_y_discrete(labels = label_wrap(14))

# Highlighting Buildings and Administration with color
expenses |>
  mutate(highlight = if_else(category == "Buildings and Administration", "yes", "no")) |>
  ggplot(aes(y = fct_infreq(category), fill = highlight)) +
  geom_bar(show.legend = FALSE) +
  scale_fill_manual(values = c(IMSCOL["lgray", "full"], IMSCOL["red", "full"])) +
  labs(x = NULL, y = NULL) +
  scale_y_discrete(labels = label_wrap(14))

# Highlighting with color and linetype
expenses |>
  mutate(highlight = if_else(category == "Buildings and Administration", "yes", "no")) |>
  ggplot(aes(y = fct_infreq(category), fill = highlight, color = highlight, linetype = highlight, size = highlight)) +
  geom_bar(show.legend = FALSE) +
  scale_fill_manual(values = c(IMSCOL["lgray", "full"], IMSCOL["red", "full"])) +
  labs(x = NULL, y = NULL) +
  scale_color_manual(values = c(IMSCOL["lgray", "full"], "white")) +
  scale_size_manual(values = c(0, 0.8)) +
  scale_y_discrete(labels = label_wrap(14))
Bar chart with each bar (Cutting tools, Workspace materials, Buildings and Administration, Machinery, and Labor) colored differently using default colors.
(a) Default coloring does nothing for the understanding of the data.
Bar chart where Buildings and Administration is highlighted in red and the rest of the bars are grey.
(b) Color draws attention directly to the bar on Buildings and Administration.
Bar chart where Buildings and Administration is highlighted in red with a white border, and the rest of the bars are grey.
Figure 6.5. Color and linetype draw attention directly to the bar on Buildings and Administration.
Figure 6.4. Three bar charts visualizing the same information with different coloring to highlight different aspects.

Subsection 6.1.3 Tell a story

For many graphs, an important aspect is the inclusion of information which is not provided in the dataset that is being plotted. The external information serves to contextualize the data and helps communicate the narrative of the research.
In Figure 6.7, the graph on the right is annotated with information about the start of the university’s fiscal year which contextualizes the information provided by the data. Sometimes the additional information may be a diagonal line given by \(y = x\text{,}\) points above the line quickly show the reader which values have a \(y\) coordinate larger than the \(x\) coordinate; points below the line show the opposite.
Listing 6.6. R code for creating time series plots with effective storytelling
duke_hires_raw <- read_csv("data/duke-hires.csv")

set.seed(1234)

duke_hires <- duke_hires_raw |>
  mutate(
    `2011` = `2010` + round(runif(12, min = -30, max = 15)),
    `2011` = `2010` + round(runif(12, min = -12, max = 10) * 1.02),
    `2012` = `2010` + round(runif(12, min = -11, max = 31) * 0.95),
    `2013` = `2010` + round(runif(12, min = -37, max = 20) * 1.1),
    `2014` = `2010` + round(runif(12, min = -10, max = 42) * 0.8),
    `2015` = `2010` + round(runif(12, min = -23, max = 18) * 1.5),
    `2012` = if_else(month == 6, 600, `2012`),
    `2013` = if_else(month == 6, 480, `2013`),
    `2014` = if_else(month == 6, 550, `2014`),
    `2015` = if_else(month == 6, 430, `2015`),
    `2012` = if_else(month == 8, 600 + 10, `2012`),
    `2013` = if_else(month == 8, 480 + 80, `2013`),
    `2014` = if_else(month == 8, 550 + 90, `2014`),
    `2015` = if_else(month == 8, 430 + 140, `2015`),
    `2015` = if_else(month == 7, 800, `2015`)
  ) |>
  pivot_longer(
    cols = !month,
    names_to = "year",
    values_to = "n"
  )

# Colored by year
ggplot(duke_hires, aes(x = month, y = n, color = year)) +
  geom_line(linewidth = 1) +
  scale_color_openintro("five") +
  scale_x_continuous(breaks = 1:12, labels = month.abb, minor_breaks = NULL) +
  scale_y_continuous(breaks = seq(0, 800, 100), minor_breaks = NULL) +
  labs(
    title = "Duke hires by month",
    y = NULL, x = NULL, color = NULL
  ) +
  theme(
    legend.position = c(0.12, 0.65),
    legend.background = element_rect(color = "gray")
  )

# Same color with annotation
ggplot(duke_hires, aes(x = month, y = n, group = year)) +
  geom_segment(x = 7, xend = 7, y = 0, yend = 800) +
  geom_line(linewidth = 0.8, color = IMSCOL["blue", "f2"]) +
  scale_x_continuous(breaks = 1:12, labels = month.abb, minor_breaks = NULL) +
  scale_y_continuous(breaks = seq(0, 800, 200), minor_breaks = NULL) +
  labs(
    title = "Duke hires by month",
    y = NULL, x = NULL
  ) +
  annotate(
    "label",
    x = 7, y = 250,
    label = "Hires always peak in\nJuly, which is the start\nof Duke's fiscal year.",
    hjust = -0.1
  )
Time series plot showing monthly Duke University hiring trends over five calendar years. Different colors represent different years. Hiring spikes in July.
(a) Colored by year
Time series plot showing monthly Duke University hiring trends with all years in the same color. A vertical line at July is annotated with text explaining that hires always peak in July, which is the start of Duke’s fiscal year.
(b) Same color for all years, annotation summarizing trend
Figure 6.7. Time series plot showing monthly Duke University hiring trends over five calendar years.

Subsection 6.1.4 Order matters

Most software programs have built-in methods for some of the plot details — some order levels alphabetically, some provide functionality for arranging them in a custom order. As seen in Figure 6.9(a), the alphabetical ordering isn’t particularly meaningful for describing the data. Sometimes it makes sense to order the bars from tallest to shortest (or vice versa), as shown in Figure 6.9(b). But in this case, the best ordering is probably the one in which the questions were asked, as shown in Figure 6.10. An ordering which does not make sense in the context of the problem (e.g., alphabetically here), can mislead the reader who might take a quick glance at the axes and not read the bar labels carefully.
In September 2019, YouGov survey asked 1,639 Great Britain adults the following question
 1 
Source: YouGov Survey Results, retrieved Oct 7, 2019.
:
How well or badly do you think the government are doing at handling Britain’s exit from the European Union?
Listing 6.8. R code for demonstrating different ordering strategies for bar plots
brexit <- tibble(
  opinion = c(
    rep("Very well", 123), rep("Fairly well", 219), rep("Fairly badly", 332),
    rep("Very badly", 818), rep("Don't know", 151)
    ),
  region = c(
    rep("london", 10), rep("rest_of_south", 49), rep("midlands_wales", 32), rep("north", 24), rep("scot", 8),
    rep("london", 18), rep("rest_of_south", 93), rep("midlands_wales", 50), rep("north", 48), rep("scot", 10),
    rep("london", 33), rep("rest_of_south", 126), rep("midlands_wales", 74), rep("north", 76), rep("scot", 23),
    rep("london", 118), rep("rest_of_south", 246), rep("midlands_wales", 152), rep("north", 212), rep("scot", 90),
    rep("london", 16), rep("rest_of_south", 38), rep("midlands_wales", 46), rep("north", 40), rep("scot", 11)
    )
)

# Alphabetic order
ggplot(brexit, aes(y = fct_rev(opinion))) +
  geom_bar() + 
  labs(y = "Opinion", x = "Count")

# Frequency order
ggplot(brexit, aes(y = fct_rev(fct_infreq(opinion)))) +
  geom_bar() + 
  labs(y = "Opinion", x = "Count")

# Survey question order
brexit |>
  mutate(opinion = fct_relevel(opinion, "Very well", "Fairly well", "Fairly badly", "Very badly", "Don't know")) |>
  ggplot(aes(y = opinion)) +
  geom_bar() + 
  labs(y = "Opinion", x = "Count")
Bar plot with the bars (Very well, Fairly well, Fairly badly, Very badly, Don’t know) arranged in alphabetical order.
(a) Alphabetic order
Bar plot with the bars arranged in frequency order from highest (Very badly) to lowest (Very well).
(b) Frequency order
Bar plot with the bars arranged in the same order as presented in the survey question.
Figure 6.10. Same order as presented in the survey question
Figure 6.9. Three bar charts visualizing the same information with arrangement of levels.

Subsection 6.1.5 Make the labels as easy to read as possible

The Brexit survey results were additionally broken down by region in Great Britain. The stacked bar plot allows for comparison of Brexit opinion across the five regions. In Figure 6.13 the bars are vertical and in Figure 6.14 they are horizontal. While the quantitative information in the two graphics is identical, flipping the graph and creating horizontal bars provides more space for the axis labels. The easier the categories are to read, the more the reader will learn from the visualization. Remember, the goal is to convey as much information as possible in a succinct and clear manner.
Listing 6.11. R code for creating readable labels and horizontal bar plots
brexit <- brexit |>
  mutate(
    region = fct_relevel(
      region,
      "london", "rest_of_south", "midlands_wales", "north", "scot"
    ),
    region = fct_recode(
      region,
      London = "london", 
      `Rest of South` = "rest_of_south", 
      `Midlands / Wales` = "midlands_wales", 
      North = "north", 
      Scotland = "scot"
    )
  ) |>
  mutate(Opinion = fct_relevel(opinion, c("Very well", "Fairly well", "Fairly badly", "Very badly", "Don't know")))

# Vertical bars
ggplot(brexit, aes(x = region, fill = Opinion)) +
  geom_bar(show.legend = FALSE) +
  scale_fill_openintro("five") +
  labs(x = "Region", y = "Count")

# Horizontal bars
ggplot(brexit, aes(y = region, fill = Opinion)) +
  geom_bar() +
  labs(x = "Count", y = NULL) +
  scale_fill_openintro("five") +
  theme(legend.position = "bottom")
Stacked bar plot of region and opinion, where vertical bars are on the x-axis.
Figure 6.13. Vertical bars across the x-axis.
Stacked bar plot of region and opinion, where horizontal bars are on the y-axis.
Figure 6.14. Horizontal bars across the y-axis.
Figure 6.12. Stacked bar plots. Horizontal orientation makes the region labels easier to read.

Subsection 6.1.6 Pick a purpose

Every graphical decision should be made with a purpose. As previously mentioned, sticking with default options is not always best for conveying the narrative of your data story. Stacked bar plots tell one part of a story. Depending on your research question, they may not tell the part of the story most important to the research.
Figure 6.16 provides three different ways of representing the same information. If the most important comparison across regions is proportion, you might prefer Figure 6.17. If the most important comparison across regions also considers the total number of individuals in the region, you might prefer Figure 6.18. If a separate bar plot for each region makes the point you’d like, use Figure 6.19, which has been faceted by region. Figure 6.19 also provides full titles and a succinct URL with the data source. Other deliberate decisions to consider include using informative labels and avoiding redundancy.
Listing 6.15. R code for comparing different visualization approaches
# Stacked bar plot showing percentages
ggplot(brexit, aes(y = region,  fill = Opinion)) +
  geom_bar(position = "fill") +
  labs(x = "Percent", y = NULL) +
  scale_fill_openintro("five") +
  scale_x_continuous(labels = label_percent()) +
  theme(legend.position = "top")

# Stacked bar plot showing counts
ggplot(brexit, aes(y = region, fill = Opinion)) +
  geom_bar() + 
  labs(x = "Count", y = NULL) +
  scale_fill_openintro("five") +
  theme(legend.position = "top")

# Faceted bar plot
ggplot(brexit, aes(y = Opinion)) +
  geom_bar() +
  facet_grid(. ~ region, labeller = label_wrap_gen(width = 12)) +
  scale_x_continuous(breaks = c(0, 100, 200)) +
  labs(
    title = "How well or badly do you think the government are doing at handling Britain's exit\nfrom the European Union?",
    subtitle = "YouGov Survey Results, 2-3 September 2019",
    caption = "Source: bit.ly/2lCJZVg", 
    x = NULL, y = NULL
  ) +
  theme(plot.title.position = "plot")
Stacked bar plot of region and opinion, showing percentages.
Figure 6.17. Stacked bar plot of region and opinion, showing percentages.
Stacked bar plot of region and opinion, showing counts.
Figure 6.18. Stacked bar plot of region and opinion, showing counts.
Dodged bar plot of region and opinion, showing counts, faceted by region.
Figure 6.19. Dodged bar plot of region and opinion, showing counts.
Figure 6.16. Three different representations of two variables from the survey, region and opinion.

Subsection 6.1.7 Select meaningful colors

One last consideration for building graphs is to consider color choices. Default or rainbow colors are not always the choice which will best distinguish the level of your variables. Much research has been done to find color combinations which are distinct and which are clear for differently sighted individuals. The cividis scale works well with ordinal data [3]. Figure 6.21 shows the same plot with two different color themes.
Listing 6.20. R code for demonstrating color scale selection
p <- ggplot(brexit, aes(y = region, fill = Opinion)) +
  geom_bar(position = "fill") +
  labs(
    title = "How well or badly do you think the government are doing at handling Britain's exit\nfrom the European Union?",
    subtitle = "YouGov Survey Results, 2-3 September 2019",
    caption = "Source: bit.ly/2lCJZVg",
    x = NULL, y = NULL, fill = NULL
  ) +
  theme(plot.title.position = "plot")

# Default color scale
p +
  scale_fill_openintro("five")

# Cividis scale
p +
  scale_fill_viridis_d(option = "E")
Bar plot with default color scale showing Brexit survey results by region and opinion.
(a) Default color scale
Bar plot with cividis color scale showing Brexit survey results by region and opinion.
(b) Cividis scale
Figure 6.21. Identical bar plots with two different coloring options.
In this chapter different representations are contrasted to demonstrate best practices in creating graphs. The fundamental principle is that your graph should provide maximal information succinctly and clearly. Labels should be clear and oriented horizontally for the reader. Don’t forget titles and, if possible, include the source of the data.

Section 6.2 Interactive R tutorials

Navigate the concepts you’ve learned in this part in R using the following self-paced tutorials. All you need is your browser to get started!
You can also access the full list of tutorials supporting this book at https://openintrostat.github.io/ims-tutorials.

Section 6.3 R labs

Further apply the concepts you’ve learned in this part in R with computational labs that walk you through a data analysis case study.
You can also access the full list of labs supporting this book at https://www.openintro.org/go?id=ims-r-labs.