A custom ggplot2 theme for Cornell

Suggested answers

Application exercise
Answers
Modified

February 20, 2024

Important

These are suggested answers. This document should be used as reference only, it’s not designed to be an exhaustive key.

Note

To ensure the fonts are correctly rendered in the charts, you need to add the following code to the YAML header:

knitr:
  opts_chunk:
      dev: "ragg_png"
library(tidyverse)
library(scales)
Install the required font files first!

Run _install-spectral.sh from the terminal first before attempting to complete the AE. You need to install the Spectral font files to ensure the fonts are correctly rendered in the charts. After running this shell script, restart your R session and it should then be available for use.

Cornell University brand identity

Organizational branding is a set of visual and verbal elements that represent an organization. It is “how your audience perceives you” and created through “many elements, including your name, logo, tagline, website, colors, collateral, messaging, positioning, graphic elements, social media, and other outreach platforms.”1

Cornell University maintains detailed guidelines for its brand identity. The design center provides explicit instructions for using the Cornell logo, color palette, and typography, as well as downloadable materials such as a PowerPoint template.

Suppose we wish to create a series of statistical charts to be used in a Cornell Bowers CIS2 presentation, but we want to ensure they are both reproducible as well as consistent with the university’s branding. We can use the ggplot2 package to create a custom theme that adheres to the Cornell brand identity.

Basic bar chart

Let’s create a basic chart to assist us with generating an appropriate ggplot2 theme. Here we will use graduation trends from Cornell University from College Scorecard. In this instance, we will use a bar chart to visualize the percentage of degrees awarded for three fields of study from 2017-21.

cornell_degrees <- read_csv("data/cornell-degrees.csv")
cornell_degrees
# A tibble: 105 × 3
   field_of_study  year    pct
   <chr>          <dbl>  <dbl>
 1 Computer        2001 0.0859
 2 Computer        2002 0.0745
 3 Computer        2003 0.0463
 4 Computer        2004 0.0327
 5 Computer        2005 0.032 
 6 Computer        2006 0.0221
 7 Computer        2007 0.0263
 8 Computer        2008 0.0262
 9 Computer        2009 0.0264
10 Computer        2010 0.0297
# ℹ 95 more rows
cornell_degrees_plot <- cornell_degrees |>
  filter(year >= 2017) |>
  # prep data for specific bar plot
  mutate(
    year = factor(year),
    field_of_study = fct_reorder2(.f = field_of_study, .x = year, .y = pct)
  ) |>
  ggplot(mapping = aes(x = field_of_study, y = pct, fill = year)) +
  # ensure padding between dodged bar segments
  geom_col(position = position_dodge2(padding = 0.2)) +
  scale_x_discrete(labels = label_wrap(width = 15)) +
  # format y axis labels
  scale_y_continuous(labels = label_percent()) +
  # optimal labels for chart
  labs(
    title = "Computer degrees have surpassed Engineering at Cornell",
    subtitle = "Percentage of degrees awarded by field of study",
    x = NULL,
    y = NULL,
    fill = NULL,
    caption = "Source: College Scorecard"
  )
cornell_degrees_plot

Develop a custom Cornell theme for ggplot2

Based on Cornell’s brand identity, we want to ensure our theme adheres to the following requirements:

In addition, we want to

Note this final requirement is not part of the theme() as defined by ggplot2, but requires us to modify the appropriate scale for the chart.

Your turn: Implement a Cornell-branded theme for ggplot2 that meets the requirements outlined above.

# cornell color palette - accent colors
cornell_pal <- c("#006699", "#6EB43F", "#F8981D", "#EF4035", "#073949")

cornell_degrees_plot +
  scale_fill_manual(values = cornell_pal) +
  theme_minimal(
    base_family = "Spectral",
    base_size = 12
  ) +
  theme(
    plot.title.position = "plot",
    plot.title = element_text(hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom",
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y = element_line(color = "grey", linewidth = 0.3),
    panel.grid.minor.y = element_line(color = "grey", linewidth = 0.15),
    axis.text = element_text(size = rel(1.2)),
    legend.text = element_text(size = rel(1.0))
  )
1
Use theme_minimal() as a starting point instead of building everything from scratch. Ensures a white background.
2
Align the title using the entire plot, not just the plot panel.
3
Horizontally align the title and subtitle centered over the plot.
4
Move the legend to the bottom of the plot.
5
No grid lines on the \(x\)-axis.
6
Increase visibility of grid lines on the \(y\)-axis.
7
Increase font size of axis and legend labels.
8
Use the Google font “Spectral” as the base font family (open-source alternative to Palantino).
9
Increase the size of all text elements of the theme().
10
Use the Cornell color palette.

Turn into a reusable function

Demo: Convert your Cornell theme into a reusable theme_cornell() function and test it on two different charts.

# define theme as its own function
theme_cornell <- function(
    base_size = 12,
    base_family = "Spectral",
    base_line_size = base_size / 22,
    base_rect_size = base_size / 22
    ) {
  # start with minimal theme
  theme_minimal(
    base_family = base_family,
    base_size = base_size,
    base_line_size = base_line_size,
    base_rect_size = base_rect_size
  ) +
    # customize required elements
    theme(
      plot.title = element_text(hjust = 0.5),
      plot.subtitle = element_text(hjust = 0.5),
      plot.title.position = "plot",
      legend.position = "bottom",
      panel.grid.major.x = element_blank(),
      panel.grid.minor.x = element_blank(),
      panel.grid.major.y = element_line(color = "grey", linewidth = 0.3),
      panel.grid.minor.y = element_line(color = "grey", linewidth = 0.15),
      axis.text = element_text(size = rel(1.2)),
      legend.text = element_text(size = rel(1.0))
    )
}
1
Set default values based on theme_minimal() plus our customizations. Ensures they can always be overriden for individual plots if needed.
2
Pass along function arguments to theme_minimal().
# existing bar chart
cornell_degrees_plot +
  scale_fill_manual(values = cornell_pal) +
  theme_cornell()

# line graph
cornell_degrees |>
  mutate(
    field_of_study = fct_reorder2(.f = field_of_study, .x = year, .y = pct)
  ) |>
  ggplot(aes(x = year, y = pct, color = field_of_study)) +
  geom_point() +
  geom_line() +
  scale_x_continuous(limits = c(2000, 2021), breaks = seq(2000, 2020, 4)) +
  scale_color_manual(values = cornell_pal) +
  scale_y_continuous(labels = label_percent()) +
  labs(
    x = "Graduation year",
    y = "Percent of degrees awarded",
    color = "Field of study",
    title = "Cornell University degrees awarded from 2001-2021",
    subtitle = "Only the top five fields as of 2021",
    caption = "Source: College Scorecard"
  ) +
  # use the Cornell theme
  theme_cornell() +
  # legend does work better on the right for this chart
  theme(
    legend.position = "right"
  )

sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.3.2 (2023-10-31)
 os       macOS Ventura 13.5.2
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/New_York
 date     2024-02-22
 pandoc   3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)

─ Packages ───────────────────────────────────────────────────────────────────
 package     * version date (UTC) lib source
 bit           4.0.5   2022-11-15 [1] CRAN (R 4.3.0)
 bit64         4.0.5   2020-08-30 [1] CRAN (R 4.3.0)
 cli           3.6.2   2023-12-11 [1] CRAN (R 4.3.1)
 colorspace    2.1-0   2023-01-23 [1] CRAN (R 4.3.0)
 crayon        1.5.2   2022-09-29 [1] CRAN (R 4.3.0)
 digest        0.6.34  2024-01-11 [1] CRAN (R 4.3.1)
 dplyr       * 1.1.4   2023-11-17 [1] CRAN (R 4.3.1)
 evaluate      0.23    2023-11-01 [1] CRAN (R 4.3.1)
 fansi         1.0.6   2023-12-08 [1] CRAN (R 4.3.1)
 farver        2.1.1   2022-07-06 [1] CRAN (R 4.3.0)
 fastmap       1.1.1   2023-02-24 [1] CRAN (R 4.3.0)
 forcats     * 1.0.0   2023-01-29 [1] CRAN (R 4.3.0)
 generics      0.1.3   2022-07-05 [1] CRAN (R 4.3.0)
 ggplot2     * 3.4.4   2023-10-12 [1] CRAN (R 4.3.1)
 glue          1.7.0   2024-01-09 [1] CRAN (R 4.3.1)
 gtable        0.3.4   2023-08-21 [1] CRAN (R 4.3.0)
 here          1.0.1   2020-12-13 [1] CRAN (R 4.3.0)
 hms           1.1.3   2023-03-21 [1] CRAN (R 4.3.0)
 htmltools     0.5.7   2023-11-03 [1] CRAN (R 4.3.1)
 htmlwidgets   1.6.4   2023-12-06 [1] CRAN (R 4.3.1)
 jsonlite      1.8.8   2023-12-04 [1] CRAN (R 4.3.1)
 knitr         1.45    2023-10-30 [1] CRAN (R 4.3.1)
 labeling      0.4.3   2023-08-29 [1] CRAN (R 4.3.0)
 lifecycle     1.0.4   2023-11-07 [1] CRAN (R 4.3.1)
 lubridate   * 1.9.3   2023-09-27 [1] CRAN (R 4.3.1)
 magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.3.0)
 munsell       0.5.0   2018-06-12 [1] CRAN (R 4.3.0)
 pillar        1.9.0   2023-03-22 [1] CRAN (R 4.3.0)
 pkgconfig     2.0.3   2019-09-22 [1] CRAN (R 4.3.0)
 purrr       * 1.0.2   2023-08-10 [1] CRAN (R 4.3.0)
 R6            2.5.1   2021-08-19 [1] CRAN (R 4.3.0)
 ragg          1.2.7   2023-12-11 [1] CRAN (R 4.3.1)
 readr       * 2.1.5   2024-01-10 [1] CRAN (R 4.3.1)
 rlang         1.1.3   2024-01-10 [1] CRAN (R 4.3.1)
 rmarkdown     2.25    2023-09-18 [1] CRAN (R 4.3.1)
 rprojroot     2.0.4   2023-11-05 [1] CRAN (R 4.3.1)
 rstudioapi    0.15.0  2023-07-07 [1] CRAN (R 4.3.0)
 scales      * 1.2.1   2024-01-18 [1] Github (r-lib/scales@c8eb772)
 sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.3.0)
 stringi       1.8.3   2023-12-11 [1] CRAN (R 4.3.1)
 stringr     * 1.5.1   2023-11-14 [1] CRAN (R 4.3.1)
 systemfonts   1.0.5   2023-10-09 [1] CRAN (R 4.3.1)
 textshaping   0.3.7   2023-10-09 [1] CRAN (R 4.3.1)
 tibble      * 3.2.1   2023-03-20 [1] CRAN (R 4.3.0)
 tidyr       * 1.3.0   2023-01-24 [1] CRAN (R 4.3.0)
 tidyselect    1.2.0   2022-10-10 [1] CRAN (R 4.3.0)
 tidyverse   * 2.0.0   2023-02-22 [1] CRAN (R 4.3.0)
 timechange    0.2.0   2023-01-11 [1] CRAN (R 4.3.0)
 tzdb          0.4.0   2023-05-12 [1] CRAN (R 4.3.0)
 utf8          1.2.4   2023-10-22 [1] CRAN (R 4.3.1)
 vctrs         0.6.5   2023-12-01 [1] CRAN (R 4.3.1)
 vroom         1.6.5   2023-12-05 [1] CRAN (R 4.3.1)
 withr         2.5.2   2023-10-30 [1] CRAN (R 4.3.1)
 xfun          0.41    2023-11-01 [1] CRAN (R 4.3.1)
 yaml          2.3.8   2023-12-11 [1] CRAN (R 4.3.1)

 [1] /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library

──────────────────────────────────────────────────────────────────────────────

Footnotes

  1. Source: What is Organizational Branding?↩︎

  2. According to the Cornell Bowers CIS brand guidelines, always refer to it as Cornell Bowers CIS or the more formal Cornell Ann S. Bowers College of Computing and Information Science or Cornell Bowers Computing and Information Science, never just CIS.↩︎