04-3: Data Visualization with ggplot2: Fine Tuning

Tips to make the most of the lecture notes

Transcript

The same two navigation tricks, and you will want them here because this is the longest deck in the chapter. The three stacked lines at the bottom left open a table of contents. The letter o gives you every slide at once. This lecture is essentially a reference: you will come back to it looking for one specific option, so learning to jump around now will save you time later.

  • Click on the three horizontally stacked lines at the bottom left corner of the slide, then you will see table of contents, and you can jump to the section you want

  • Hit letter “o” on your keyboard and you will have a panel view of all the slides

Transcript

And the same live code areas, running R in your browser. Run Code executes the box, highlighting a portion and pressing command-enter runs just that part, the two-sheets icon copies the code for your own session, and the reload button restores the original. In this lecture in particular, change the numbers and rerun. Almost every slide here is one option with one value, and the fastest way to understand what it controls is to make it absurdly large and see what moves.

The callout explains an important difference between this browser and your own RStudio session. The browser has already attached every package listed there, but copied code does not carry that setup with it. In RStudio, load ggplot2 for plotting, dplyr for data manipulation, ggthemes for the extra theme functions, RColorBrewer for its palette display, patchwork for combining plots, and nycflights13 for the flights and weather data. Otherwise a copied example can fail with a could-not-find-function or missing-object error even though it ran here. One more, at the right end of the toolbar: the eye icon hides that cell’s output and a second click brings it back. Some of these results run long, and once you have read one it is just pushing the rest of the slide out of view.

  • The box area with a hint of blue as the background color is where you can write code (hereafter referred to as the “code area”).
  • Hit the “Run Code” button to execute all the code inside the code area.
  • You can evaluate (run) code selectively by highlighting the parts you want to run and hitting Command + Enter for Mac (Ctrl + Enter for Windows).
  • If you want to run the codes on your computer, you can first click on the icon with two sheets of paper stacked on top of each other (top right corner of the code chunk), which copies the code in the code area. You can then paste it onto your computer.
  • You can click on the reload button (top right corner of the code chunk, left to the copy button) to revert back to the original code.
  • Click the eye icon to hide a code area’s output, and click it again to bring it back. It sits at the right end of the toolbar, next to the copy button, or on the Output banner when the output is shown beside the code. Useful when a long result pushes the rest of the slide out of view.

If you run this code in RStudio

These slides run R in your browser, and the browser session already has every package attached. Your own R session does not, so code copied from here will fail with could not find function until you load them yourself:

library(ggplot2)
library(dplyr)
library(ggthemes)      # theme_stata(), theme_economist(), ...
library(RColorBrewer)  # display.brewer.all()
library(patchwork)     # combining figures with | and /
library(nycflights13)  # flights, weather

Make your figures presentable to others


Make your figures presentable to others

Transcript

Let us be honest about where we are. The figures we have made so far are fine for looking at ourselves, and not good enough for a paper or a seminar. They are too crude. This lecture is about closing that gap, and the good news is that ggplot2 lets you control virtually every element of a figure. The bad news is that there are a great many elements. Read the callout, because it is the genuine skill here: the hard part is not the syntax, it is knowing what the part of the figure you want to change is called.

  • Figures we have created so far cannot be used for formal presentations or publications. They are simply too crude.

  • We need to fine-tune raw figures before they are publishable.

  • You can control virtually every element of a figure under the ggplot2 framework.

  • Take a look at here for the complete list of options you can use to modify the theme of figures

Key

The most important thing is actually to know which part of a figure a theme option refers to (e.g., axis.text)

Transcript

Here is a distinction that will organize the whole lecture. Changes come in two kinds. Content-altering changes what the figure says: the words of the axis title, the range of the axis, which colours mean which group. Theme-altering changes how it looks: the font size of that title, its position, the colour of the background. The example makes it concrete. The words Corn Yield in brackets bu per acre are content. The fact that they are set in fourteen point is theme. Content and theme are independent, which is why we handle them with different functions.

Two types of operations

Operations to make your figures presentable can be categorized into two types:

  • Content-altering
  • Theme-altering


Examples

For the y-axis title,

  • The axis title text itself (say “Corn Yield (bu/acre)”) falls under the content category.

  • The position of or the font size of the axis-title fall under the theme category

The content itself does not change when theme is altered.

Transcript

This is content-altering, side by side with the original. On the right we have given the axes proper titles and changed the fill colours to a different scheme. Notice what changed: the words on the axes and the meaning of the colours. Notice what did not: the font, the sizes, the grey background, the grid lines. Everything about the styling is untouched, because we only altered content. Keep this comparison in mind as you look at the next tab, which does exactly the opposite.

The setup code first keeps years 2005 through 2010, including both endpoints. The plot maps factor of year to the x axis, corn yield to the y axis, and state name to fill, so year is treated as a set of categories and each state receives its own box colour. The altered version then adds xlab Year, ylab Corn Yield in bushels per acre, and scale fill viridis d. The d matters because state name is discrete. Those three additions change labels and the state-to-colour mapping without changing the observations or the theme.

Original

Altered

Transcript

And now theme-altering, from the same starting figure. One line, setting the axis title size to twenty. The titles get bigger. Their text is identical, the data is identical, the colours are identical. Only the appearance changed. Put this next to the previous tab and the distinction should be clear: content is what the figure communicates, theme is how it is dressed. Once you can classify a change as one or the other, you know which function to reach for, which is most of the battle.

Read the nesting on screen from the outside in. You add theme to g_box, identify axis.title as the element for both axis titles, and pass element_text with size equal to twenty because the element is text. That hierarchy is the general syntax you will use throughout the theme sections.

Original

Altered

Transcript

A caveat and a rule of thumb to close the section. The distinction is not always crisp, and there are places where you could reasonably argue a change is either. But the practical guide holds almost always: the scale functions alter content, and the theme function alters theme. And note the last bullet, because it saves typing: for the most commonly changed pieces of content there are shorthand functions, like xlab and ylab for axis titles, which do the same job as the longer scale call.

  • Distinctions between the two types of actions are not always clear

  • But, typically, you use

    • scale_*() function series to alter contents
    • theme() function to alter the theme
  • Note that there are shorthand convenience functions to alter figure contents for commonly altered parts of figures

Axes content

Transcript

We are going to build one figure up through this whole section, so start here. This is a boxplot of corn yield by year, filled by state, over a six-year window. Run it and get it into your session as g_box, because every tab from here refers back to it. Notice that it is deliberately unpolished: no proper axis titles, default colours, everything at default size. That is the raw material we are about to make presentable.

The first pipeline filters county_yield to years greater than or equal to 2005 and less than or equal to 2010. Inside aes, factor of year makes the x variable discrete, corn_yield supplies the numeric y values used to compute each boxplot, and state_name maps the box fill to state. The parentheses around the assignment make R both save the plot as g_box and display it now, so you can confirm the object was created.

We are going to build on this figure in this section:

Transcript

The functions for controlling axis content are the scale functions, and there are four of them because there are two axes and two kinds of variable. Read the callout first, because choosing the wrong one is the most common error here: use the discrete version when the variable is categorical and the continuous version when it is numeric. In our figure the x variable is year wrapped in factor, so it is discrete, while corn yield is numeric, so it is continuous. Then whichever you pick, the four things you can control are the same: the axis title, the range, where the ticks sit, and what the tick labels say.

We can use

  • scale_x_discrete()/scale_x_continuous() for x-axis
  • scale_y_discrete()/scale_y_continuous() for y-axis

to control the following elements of axes:

  • name: an axis title
  • limits: the range of an axis
  • breaks: axis ticks positions
  • labels: axis texts at ticks

Note

  • We use scale_x_discrete() if x is a discrete variable (not numeric) and scale_x_continuous() if x is a continuous variable (numeric).
  • The same applies for y.
Transcript

The simplest thing to change is the axis title, and there are two ways of doing it that produce identical results. The long way, through the name argument of the scale functions, is worth seeing because it shows you that the title is just one of several things those functions control. The short way, xlab and ylab, is what you will actually type, because it is two words instead of a function call. Use the shorthand day to day, and remember the long form exists for when you want to change the title and the range in the same breath.



Or just this,

Transcript

Limits control the range of an axis, and again you get the long form through the scale function and the shorthand ylim. But read the third option on this slide carefully, because it is the important one. Setting a limit does not zoom the plot; it throws away the data outside the range before drawing, which is why you may see a warning about removed rows. For a boxplot that genuinely matters, because a box computed from a truncated sample is not the same box. To zoom without changing the computed statistics, use coord_cartesian with ylim set to 100 through 200.


Or just,


Or,

To zoom without changing computed statistics, use coord_cartesian(ylim = c(100, 200)).

Transcript

Two related controls. breaks decides where the tick marks sit, and labels decides what text appears at them. Look at the y axis here: limits from 100 to 200 with breaks every 10. Now look at the x axis, where labels is given a little function that strips the 20 from the front of each year, so 2005 prints as 05. The callout explains why a function is better than a plain vector. A fixed vector of labels can become mismatched with automatically generated breaks and fail if the number of breaks changes. A function is applied to whatever the breaks turn out to be, so it cannot fall out of step.

  • breaks: determines where the ticks are located
  • labels: defines the texts at the ticks


Warning

If you supply labels as a plain vector, it must have exactly as many elements as there are breaks. Supplying a function (here, ~ gsub("20", "", .x)) applies it to whatever the breaks happen to be, so the labels can never fall out of sync with the data.

Transcript

Now you do it. The instruction tab builds a line plot of mean arrival delay by month for the three New York airports, and then the exercise asks you to fix up both axes: titles, limits, and breaks. Everything you need is in the four tabs you just worked through. Note that the x variable here is month, which is numeric, so this is scale_x_continuous rather than the discrete version we used on the boxplot. That choice is the first thing to get right.

In the preparation pipeline, group_by origin and month defines one airport-month group at a time. Summarize calculates mean_arr_delay from arr_delay, and na.rm equal to true prevents missing delays from turning a group mean into N A. Geom_line maps month to x, the mean to y, and origin to colour, so each airport becomes a separate line. In your answer, set the x name to Month, limits to four through eight, and breaks to the integer sequence four through eight. Set the y name to Average Arrival Delay in minutes and its limits to zero through twenty-five. The output and folded Answer tabs show that complete target, while Work here is the live area for your attempt.

Run the following code to create gg_delay, which you will build on.

Change the axes content to create the figure on the right using scale_x_continuous() and scale_y_continuous().

Here are the list of changes you need to make:

  • x-axis
    • change the x-axis title to “Month”
    • change the x-axis range to 4 through 8
    • change the breaks and their labels of the x-axis ticks to 4 through 8
  • y-axis
    • change the y-axis title to “Average Arrival Delay (minutes)”
    • change the y-axis range to 0 through 25
Code
gg_delay +
  scale_x_continuous(
    name = "Month",
    limits = c(4, 8),
    breaks = 4:8
  ) +
  scale_y_continuous(
    name = "Average Arrival Delay (minutes)",
    limits = c(0, 25)
  )

Legends content

Transcript

Next section, legends, and we build on the figure we just finished styling. g_axis is the boxplot with its axes sorted out, so run this first. A word on why legends get their own section: a legend is not decoration, it is the key that makes a colour-coded figure readable at all. If your reader cannot tell which box is Nebraska, the colour is worse than useless. So the things we change here, its title, its colours, its layout, are all about making that key do its job.

The code creates g_axis by adding two scales to g_box. The discrete x scale names the axis Year and applies the label function that removes 20 from the displayed years. The continuous y scale supplies the yield title, limits the displayed scale to 100 through 200, and requests ticks every ten units with seq. The surrounding parentheses save the finished plot and print it, giving every later legend tab the same controlled starting point.

We are going to build on this figure in this section:

Transcript

The first thing to fix on almost any legend is its title, because by default it is the name of your variable, and variable names are written for code rather than for readers. Here it says state_name, with an underscore, which is fine in a script and wrong in a figure. Notice which function does it: scale_fill_brewer, a fill function, because the legend exists to explain the fill aesthetic. That is the general rule, and it catches people out: to change a legend you change the scale for whatever aesthetic that legend belongs to.

Transcript

The same function also chooses the colours, through the palette argument. Set1 is one of the ColorBrewer palettes, and we come back to those properly later in this lecture. For now, notice that one argument changed both the appearance of the boxes and the swatches in the legend, and they stayed in step automatically. That is a real advantage of changing colours through a scale function rather than by hand: the legend is generated from the same scale, so it cannot disagree with the figure.

Transcript

Now the layout of the legend itself, which is controlled through the guide argument. Here we move the title to the left of the keys instead of above them. That looks like a triviality, and it becomes useful when the legend goes at the bottom of the figure, where a title stacked above the keys wastes a whole line of vertical space. The general pattern to notice: the scale function decides what the legend says, and guide_legend inside it decides how the legend is arranged.

Transcript

The other layout control you will reach for is the number of rows or columns. Setting nrow to two arranges the legend keys into two rows instead of the default single column. This matters when a legend has many categories: left to itself it makes one tall column down the right-hand side, which squeezes the plot area. Wrapping the entries into a short wide block, and then putting it underneath the figure, is often the difference between a figure that fits on a slide and one that does not.

Transcript

An exercise on legends, and note the twist in the instruction: you have to work out for yourself what goes in the star of scale-star-brewer. Think about which aesthetic the legend is explaining. In this figure the airports are distinguished by line colour, not by fill, so it is the colour version you need. That single decision is the thing being tested; the rest is filling in the arguments you have just seen. Getting fill and colour confused here is extremely common, and the symptom is that your code runs and nothing changes.

The preparation repeats the airport-by-month calculation: group the flights by origin and month, average arrival delay while removing missing values, and map origin to line colour. The complete scale is scale_color_brewer. Name gives the reader-facing title Airports in NY, palette selects Set2, and guide_legend controls layout. Inside that guide, title.position equal to bottom places the title beneath the keys, while ncol equal to three spreads the three airport entries across three columns. Work here is where you build it, and the folded Answer gives you the exact call to compare after you try.

Run the following code to create gg_delay, which you will build on.

Change the legend contents to create the figure on the right using scale_*_brewer(). You need to identify what goes into * in scale_*_brewer().

Here are the list of changes you need to make:

  • change the legend title to “Airports in NY”
  • change the legend title position to “bottom”
  • change the legend items to be spread in 3 columns
  • change the color palette to Set2
Code
gg_delay +
  scale_color_brewer(
    name = "Airports in NY",
    palette = "Set2",
    guide = guide_legend(
      title.position = "bottom",
      ncol = 3
    )
  )

Theme

Transcript

Now we move from content to theme, and this tab is about naming, which the very first slide called the real skill. The convention is hierarchical. axis.title refers to the titles of both axes. Add a suffix and you narrow it: axis.title.x is only the x axis title. That pattern is consistent throughout, so axis.text is both, axis.text.y is one. Once you see the rule, you can often guess the name of the thing you want to change, and when you cannot, the linked reference page lists them all.

When specifying the theme of figure elements, it is good to know the naming convention of figure elements:

For example:

  • axis.title

This refers to the title of both x- and y-axis. Any aesthetic theme you apply to this element will be reflected on the title of both x- and y-axis.

  • axis.title.x

This refers to the title of only x-axis. Any aesthetic theme you apply to this element will be reflected on the title of only x-axis.

So, basically appending a suffix (e.g., .x, .y) narrows down the scope of the figure elements the element name refers to.

Transcript

The second half of the naming system is that you cannot just assign a value to an element; you have to say what kind of element it is, using one of these functions. element_text for anything made of words, where you set size, family, angle, colour. element_rect for anything box-shaped, like backgrounds, where you set fill and border. element_line for lines, like grid lines and axis lines, where you set thickness and colour. element_blank for anything at all, which makes it disappear. And unit for measurements like widths and spacings. Match the function to the kind of thing and the rest is straightforward.

There are common functions we use to specify the aesthetic nature of figure elements based on the type of the elements:


  • element_text(): for text elements like axis.text, axis.title, legend.text

Inside the function, you specify things like font size, font family, angle, etc.

  • element_rect(): for box-like elements like legend.background, plot.background, strip.background

Inside the function, you specify things like the fill (background) color, border line color, etc.

  • element_line(): for line elements like panel.grid.major, axis.line.x

Inside the function, you specify things like line thickness, line color, etc.

  • element_blank(): any components

It makes the specified component disappear.

  • unit(): for attributes of figure elements like legend.key.width, legend.box.spacing

Axis theme

Transcript

This section is theme changes applied to axes, and we build on g_axis, the figure whose content we sorted out earlier. Run it so you have the starting point in front of you. Each of the next three tabs adds one more theme setting to the same call, so you can watch them accumulate rather than seeing them all at once.

We are going to build on this figure in this section:

Transcript

Two text elements, and notice they use different levels of the naming hierarchy on purpose. axis.title.x, with the suffix, changes only the x axis title, making it small and red. axis.text, with no suffix, changes the tick labels on both axes at once. Both take element_text, because both are made of words. This is the naming rule from two tabs ago doing real work: the suffix is how you choose between changing one axis and changing both.

The exact values make the contrast visible. The x title becomes size eight and red. Both sets of tick labels become size fourteen in the Times family. Because these assignments sit together inside one theme call and are separated by a comma, both modifications are applied to g_axis in the same plot.

Transcript

Now a line element. axis.line.y draws the axis line itself, and because it is a line it takes element_line, where you set linewidth and colour. Note that in the default ggplot2 theme there is no visible axis line at all, so this is adding something rather than restyling it. If you want the classic look with a solid line along the bottom and left edges, this is how you get it, and it is one of the things the pre-made themes later in this lecture do for you.

Here linewidth equal to two makes the y-axis line intentionally heavy, and color equal to blue makes the new line unmistakable. The earlier axis.title.x and axis.text assignments remain in this code, so the output combines the red size-eight x title, the size-fourteen Times tick labels, and the new blue y-axis line.

Transcript

And a measurement. axis.ticks.length.x controls how long the tick marks are, and because it is a length rather than a colour or a font, it takes unit, where you give a number and the units. The two centimetres here is deliberately ridiculous, so you can see exactly which part of the figure moved. That is a good debugging habit generally: when you are not sure what an option controls, set it to an absurd value, find what jumps, then dial it back to something sensible.

The suffix x means only x-axis tick lengths change. Unit takes the numeric amount two and the unit string cm, rather than treating two as an unlabelled theme size. All of the title, tick-label, and y-axis-line settings above it are retained, which lets you see that a theme call can collect several different element types at once.

Legends theme

Transcript

Legends again, but now their appearance rather than their content. The list shows what you can get at: the title, where the whole legend sits, the keys, the text, the direction it runs, and the background behind it. Note the difference from the earlier legend section: there we used scale functions to decide what the legend said and which colours it showed. Here we use theme to decide how it looks. Same distinction as the very first slide, applied to one component.

We can use theme() to change the aesthetics of legends. Some of the elements include

  • title
  • position
  • key
  • text
  • direction
  • background

See here for the full list of options related to legends.

We will discuss how to change the color scheme of legends in much more detail later.

Transcript

The starting point for this section, and it is worth looking at what it does. It takes our axis-styled figure and adds a fill scale with the Paired palette, arranged in two rows with the title on the left. The following tabs combine one content change, labs fill set to State, with accumulating theme changes.

This is what we will build on:

Transcript

The single most useful legend theme setting is position. By default the legend goes to the right, which eats horizontal space, and horizontal space is exactly what a wide figure needs. Moving it to the bottom often buys you a noticeably larger plot area for free. You can also use top, left, or the word none to remove the legend entirely, which is what you want when the same key is already explained in a neighbouring panel or in the caption.

There are two different operations in the code. Labs with fill equal to State replaces the fill legend’s reader-facing title, which is a content change. Theme with legend.position equal to bottom then moves the whole legend, which is an appearance change. Keeping those in separate functions preserves the content-versus-theme distinction from the start of the lecture.

Transcript

The keys are the little coloured swatches next to each label, and their size is set with legend.key.height and legend.key.width, both taking unit because they are measurements. Widening the keys, as here, is genuinely useful for a bottom-mounted legend: a long flat swatch reads more clearly at a glance than a small square, especially when the colours are close together. Notice also that these two settings are being added to the position setting from the previous tab, so the theme call is accumulating.

The height is set to zero-point-five centimetres and the width to two centimetres, creating the long, low keys you see. Labs still renames the fill legend State, and legend.position remains bottom, so this tab changes key dimensions without losing the title and position work from the preceding tab.

Transcript

Fonts for the two text parts of a legend, and they are named separately because you usually want them different. legend.text is the labels next to each key, and legend.title is the heading. Both take element_text. The values chosen here are deliberately mismatched, a large Times label against a small red Courier title, so you can see clearly which is which. In real use you would set them to the same family as the rest of your figure, and the point of this tab is simply to show you which name controls which part.

Transcript

And the panel behind the legend, which is a box, so it takes element_rect and you set its fill and its border. A light background behind the legend can help when the legend sits on top of a busy plot, and it is unnecessary when the legend has its own space outside the panel. Notice that the theme call has now accumulated six settings across four tabs, all inside one theme function separated by commas. That is how real theme code looks: one call, a list of element assignments.

The new assignment is legend.background equal to element_rect with a lightblue fill and a solid line type. Everything above it is still active: the bottom position, half-centimetre by two-centimetre keys, size-sixteen Times labels, and size-six red Courier title. Labs continues to label the fill legend State. Reading the full call this way helps you distinguish the one new setting from the accumulated context.

Pre-made and customized themes

Transcript

Everything so far has been setting elements one at a time, which is powerful and slow. A pre-made theme changes dozens of them at once. ggplot2 ships with several, and the ggthemes package adds many more, including imitations of the house styles of various publications. Install and load ggthemes to follow along. The linked gallery is genuinely worth a look, because picking a theme that is already close to what you want is far less work than building one from the default.

There are a bunch of pre-made themes from the ggplot2 and ggthemes packages that can quickly change how figures look.

Install and library ggthemes package first:

#--- install ---#
install.packages("ggthemes")

#--- library ---#
library("ggthemes")


See the full list of pre-made themes here.

Transcript

Six themes on the same figure, so click through them and see the range. theme_bw is the sober one you will probably use most, white background with grid lines. theme_void removes essentially everything, which is what you want for a map. Then stata, gdocs, economist and excel, imitating other software and publications. Two things to notice as you click. First, how much changes with one function call. Second, that a theme only touches appearance; the underlying labels, mappings, and data remain unchanged, although themes such as theme_void hide the axis titles.

Transcript

This is the technique I would actually recommend, and it combines the last two sections. Start from the pre-made theme closest to what you want, then override the specific parts you do not like by adding a theme call after it. Order matters: the later one wins, so the pre-made theme must come first. The tabs work through an example, taking theme_bw and then removing the minor grid lines, then the vertical major ones, then restyling the horizontal ones. That is a realistic workflow, and it is far less effort than specifying forty elements yourself.

In Preparation, g_axis plus theme_bw is saved as g_axis_bw. The minor-grid tab adds element_blank to panel.grid.minor, removing both horizontal and vertical minor lines. The major-grid tab writes the same construction without the saved shortcut, blanks panel.grid.major.x to remove the vertical major lines, and replaces panel.grid.major.y with blue dotted lines of width one. The link beneath the code is a reference for other accepted line types. The important reading habit is to move through the plus signs in order: establish the base plot, apply the broad theme, and then apply narrow overrides.

You can simply override parts of the pre-made theme by adding theme options like this (see the Custom theme tab below for more on this):

g_axis +
  theme_bw() +
  theme(
    panel.grid.minor = element_blank()
  )


So, you can pick the pre-made theme that looks the closest to what you would like, and then add on theme elements to the part you do not like.

We will build from this figure:

See here for the line types available.

Transcript

And the final step: if you have a combination you like, save it to a name and reuse it. A theme is an ordinary R object, so my_theme can be built once and then added to any figure exactly like a built-in theme. The tabs show it built on theme_economist, compared against the unmodified version, and then the theme_set function, which applies your theme to every figure you draw from then on. For a paper where every figure has to look the same, define the theme once at the top of your script and use theme_set. That consistency is worth more to a reader than any individual styling choice.

The Introduction tab creates my_theme by starting with theme_economist and then blanking axis.title and panel.grid.major. It next maps factor of month to x and temperature to y in the weather data, draws a boxplot, and adds my_theme just as it would add theme_bw. Compare places that result beside the same plot with plain theme_economist, isolating exactly what your two overrides removed. Finally, theme_set with my_theme changes the session-wide default, so later plots inherit it even when you do not add my_theme explicitly. Use that global setting when the whole project should share a design, and add the object plot by plot when only selected figures should use it.

You can create your own theme, save it, and then use it later.

Here, I am creating my own theme off of theme_economist(), where axis titles and major panel grids are absent.

You can add my_theme like below just like a regular pre-made theme:

ggplot(data = weather) +
  geom_boxplot(
    aes(y = temp, x = factor(month))
  ) +
  my_theme

If you would like to apply your theme to all the figures you generate, then use theme_set() like below:

theme_set(my_theme)

After this, all of your figures will follow my_theme.

Faceted figure theme

Transcript

Faceted figures have parts that ordinary figures do not, and they need their own names. The strip is the little labelled bar along the edge of each panel telling you which group it holds. So there is strip.background for the bar itself, strip.text for the words in it, and strip.placement for where it sits. The gaps between panels are controlled separately with panel.spacing. If you have ever wanted to make those labels readable and not known what to call them, this is the vocabulary.

Faceted figures have strip elements that do not exist for non-faceted figures:

  • strip.background
  • strip.placement
  • strip.text

They also have panel-spacing elements such as panel.spacing. We learn how to modify these elements.

Transcript

The setup for this section, and note that it deliberately cuts the data down to three states and two years. That is not just to keep the example small; it is the lesson from the faceting lecture applied again. A grid of six panels can be read, and a grid of sixty cannot. Then we build the faceted histogram we will restyle. Each year column gets its own x scale, shared by the three state panels in that column.

The two percent-in-percent filters keep Nebraska, Colorado, and Kansas, then 2005 and 2006. Geom_histogram maps corn yield to x, so each panel shows the distribution of county yields. In facet_grid, state_name on the left of the tilde creates the rows and year on the right creates the columns. Scales equal to free_x allows the x scale to vary from one year column to the other while panels within a column still share it. The surrounding parentheses save this plot as g_f and display it for inspection.

Create a dataset for this section:


Create a faceted figure we will build on:

Transcript

Strip text, and notice it uses the suffix rule from earlier: strip.text.x is the labels across the top, strip.text.y is the labels down the side. They are styled separately here for a reason worth knowing. The labels down the right-hand side are rotated ninety degrees by default, which is hard to read, and setting angle to zero turns them upright. That single change makes a faceted figure noticeably easier to read, and it is probably the most useful thing on this slide.

The top strip labels use size twelve, the Times family, and red. The side strip labels use angle zero, size six, and blue. Both assignments take element_text because they style words, and placing them together inside theme lets the row and column labels have independent treatments.

Transcript

The strip backgrounds, which are boxes, so element_rect. Two different treatments here on purpose. The x strips get a blue border, and the y strips get element_blank, which removes their grey background entirely. That second one is a common piece of polish: the default grey bars can dominate a figure with many panels, and stripping them back so only the label text remains usually looks cleaner. Remember element_blank works on any element, so it is your general tool for making something go away.

Transcript

And the gaps between panels, set with panel.spacing, again with x and y variants and again taking unit because they are measurements. The values here are deliberately extreme in opposite directions, a wide two centimetre gap horizontally and almost nothing vertically, so you can see each one act independently. In practice you would widen the spacing when the axis labels of neighbouring panels are colliding, and tighten it when you want the panels read as one continuous picture.

Color


More flexible color options with HEX

Transcript

Now colour, properly. So far we have used colour names like red and blue, which is fine and very limited. A HEX code specifies a colour exactly, as a hash followed by six characters, and it gives you the entire range your screen can produce. The direction in the callout is worth actually doing: go to the site, pick a colour you like, and copy the code beneath it. Note the remark at the end too, that RGB codes also work but there is no real reason to use them.

Instead of naming the color you want to use, you can use HEX color codes instead.

Direction

  • Visit here
  • Click on any color you like
  • Then you will see two sets of color gradients (thicker and lighter from the color you picked)
  • Pick the color you like from the color bar and copy the HEX color code beneath the color you picked

You could alternatively use the RGB codes, but I do not see any reason to do so because the use of HEX codes is sufficient.

Transcript

And here it is in use. A HEX code goes anywhere a colour name goes, in exactly the same position, so nothing about the syntax changes. This matters more than it sounds. If your department or journal has house colours, you can use precisely those colours rather than an approximation, and a figure that uses two or three deliberately chosen colours consistently looks considerably more professional than one built from the default palette. Try one of your own.

The plot uses county_yield, maps d-three-five-nine to x and corn yield to y, and draws one point per row. Color is outside aes, so hash eight-two-four-two-eight-three is a single constant colour applied to every point. If color were mapped inside aes, ggplot would interpret it as a data mapping and construct a scale and legend instead. The Try callout asks you to replace that code with a HEX colour you chose and rerun the cell.

You can use HEX color codes for any color-related elements in a figure.

Try

Pick a Hex color and try it yourself.

Color scale

Transcript

Now colour scales, meaning the rule that maps your data to colours, which matters far more than any individual colour choice. The naming follows a pattern worth internalizing, because there are a lot of these functions and the pattern is the only way to keep them straight. Every one is scale, then the aesthetic, then the method. So the first blank is either colour or fill, and the second is whichever colour system you are using. Learn the pattern and the long list on the next tab stops being intimidating.

The choice of color schemes for your figures is very important.

We use scale_A_B() functions for color specification:

  • A is the name of aesthetic (color or fill)
  • B is the type of color specification method
Transcript

Here is the pattern applied. In this code the aesthetic being mapped is colour, so the first blank is colour, and that already halves the list. Then the second blank is the method, and there are many. Do not try to memorize them. Instead note the single most important thing, which is at the bottom of the slide: some of these are for discrete variables and some for continuous, and you have to know which kind your variable is. Corn yield is numeric, so it needs a continuous scale, and handing it a discrete one is a very common error.

The list labels the choices explicitly. Brewer and viridis d are discrete options, while distiller and viridis c are continuous options. The generic scale_color_continuous and scale_color_discrete functions follow the same split, and scale_color_hue is another discrete scale. In the example, geom_point maps corn yield to y and also to colour, with d-three-five-nine on x. Since that colour variable is numeric, scale_color_viridis_c or another continuous choice is valid, while a function labelled discrete is not.

For example, consider the following code:

ggplot(data = county_yield) +
  geom_point(aes(y = corn_yield, x = d3_5_9, color = corn_yield))

Since it is the color aesthetic that we want to work on, A = color.

There are many options for B. Indeed, there are so many that, it gets confusing!

  • scale_color_brewer() (discrete)
  • scale_color_distiller() (continuous)
  • scale_color_viridis_d() (discrete)
  • scale_color_viridis_c() (continuous)
  • scale_color_continuous() (continuous)
  • scale_color_discrete() (discrete)
  • scale_color_hue() (discrete)

One thing to remember is that you need to be aware of whether the aesthetic variable (here, corn_yield) is numeric or not as that determines acceptable type of B.

Viridis

Transcript

Viridis is the colour system I would recommend as a default, and this tab is the whole of it. Four functions, from the two aesthetics times the two variable types, following the naming pattern you just learned, with c for continuous and d for discrete. Then eight palettes within it, selected with the option argument by name or by letter. And the line at the bottom is the real reason to prefer these: they are colour-blind safe, which around one man in twelve in your audience will benefit from, and they also survive being printed in greyscale.

We have four scale functions for Viridis color map:

  • scale_color_viridis_c(): for color aesthetic with a continuous variable
  • scale_color_viridis_d(): for color aesthetic with a discrete variable
  • scale_fill_viridis_c(): for fill aesthetic with a continuous variable
  • scale_fill_viridis_d(): for fill aesthetic with a discrete variable

There are several color scheme types under the Viridis color map:

  • magma (option A)
  • inferno (option B)
  • plasma (option C)
  • viridis (option D, the default)
  • cividis (option E)
  • rocket, mako, turbo (options F, G, H)

You can use option to specify which one of them you want to use inside the scale functions, using either the name or the letter.

These color schemes are color-blind safe.

Transcript

Four of the palettes shown together on the same data so you can compare them fairly. Magma and inferno run through black and red into yellow, plasma is purple to yellow, and viridis, the default, runs blue through green to yellow. They all share the property that matters: brightness increases steadily from one end to the other, so the ordering survives even if the colour information is lost. Pick whichever suits your subject; the technical properties are the same.

The code loads the geyser data from MASS, puts eruption duration on x and waiting time on y, and restricts the visible ranges to zero-point-five through six and forty through one hundred ten. Stat_density2d computes density levels, maps the computed level to fill with after_stat, and draws filled polygons. Theme_bw supplies the base appearance, then panel.grid equal to element_blank removes the grid. The rightward assignment saves that common plot as gg so every comparison starts identically. Patchwork’s vertical bar places two plots beside each other, and its slash stacks the two rows. Each copy adds viridis option A, B, C, or D and uses labs to put the palette name on x while removing the y title. That construction is why you can compare only the palette rather than accidentally comparing different data or themes.

Transcript

A continuous example, using the default viridis palette. Notice which function is used, scale colour viridis c, and take the two parts of the name apart: colour because we mapped the colour aesthetic, and c because corn yield is a continuous variable. If you take one thing from this section, make it the habit of reading those function names as a specification rather than as a word to memorize.

The filter keeps observations with corn yield above fifty. Geom_point maps d-three-five-nine to x, corn yield to y, and that same numeric corn-yield variable to point colour. Adding scale_color_viridis_c replaces ggplot’s default continuous colour scale while leaving the data, point geometry, and mappings unchanged.

Transcript

The same figure with a different palette, selected through the option argument. Use the letter, in quotes, or the palette name; here B gives you inferno. One warning worth having: option also appears to accept a bare number, but the numbering does not line up with the A to H list on the instruction tab the way you would expect, so a number will quietly give you a different palette from the one you meant. Write the letter or the name and there is no ambiguity.

Transcript

One more argument you will want. Setting direction to minus one reverses the palette, so the dark end and the light end swap. This is not cosmetic fussiness. Whether high values should be dark or light depends on what you are showing and on what your reader expects, and getting it backwards makes a figure actively misleading. For a variable where high means bad, a reader will usually expect the intense end of the scale to sit on the high values.

RColorBrewer

Transcript

The other colour system worth knowing is ColorBrewer, and its real contribution is the three-way classification at the top of this tab, which is a genuinely useful way to think. Sequential palettes are for variables that run from low to high, like temperature. Diverging palettes have a meaningful midpoint with two directions away from it, which is exactly right for something like change, where negative and positive mean opposite things. And qualitative palettes are for categories with no order at all, like state names. Then two functions, brewer for discrete variables and distiller for continuous.

In scale_A_brewer and scale_A_distiller, A is the aesthetic you are controlling, either color for lines and point outlines or fill for interiors. Choose that part from the geometry’s mapping, then choose brewer when the mapped variable is discrete or distiller when it is continuous. The palette family is a separate decision based on whether the values are ordered, centred around a meaningful reference, or simply categorical.

RColorBrewer package provides a number of color palettes of three types:

  • sequential: suitable for a variable that has ordinal meaning (e.g., temperature, precipitation)
  • diverging: suitable for variables that take both negative and positive values (e.g., changes in groundwater level)
  • qualitative: suitable for qualitative or categorical variable

We use two types of scale functions for the palettes:

  • scale_A_brewer(): for discrete aesthetic variable
  • scale_A_distiller(): for continuous aesthetic variable
Transcript

These three tabs display every palette in each family, and they are worth spending a moment with. Look at the sequential ones and see the brightness climb steadily. Look at the diverging ones and see the pale midpoint with colour intensifying in both directions. Then look at the qualitative ones and notice that they primarily distinguish categories by hue rather than a steady lightness progression. Their colours are not equally bright, and palettes such as Paired and Accent vary substantially in lightness. Matching the family to the kind of variable you have is most of what good colour choice consists of.

Each tab calls display.brewer.all with a different type abbreviation: seq for sequential, div for diverging, and qual for qualitative. The function displays the palette names together with their swatches, so you can choose a valid palette name for a later brewer or distiller scale rather than guessing from memory.

Transcript

A discrete example: state is a category with no natural ordering, so this is a qualitative palette applied with scale fill brewer. brewer because the variable is discrete, fill because we are colouring the interiors of boxes. Set2 is a good default for this kind of thing, being distinguishable without being garish. Follow the naming apart again: aesthetic, then method, and both halves determined by facts about your data rather than by preference.

The preparation first filters county_yield to the inclusive period from 2005 through 2010 and saves it as county_yield_s_b2010. The plot then puts factor of year on x, corn yield on y, and state name on fill. Factor makes the years discrete groups for the boxplots, while the fill mapping creates the state legend. Palette equal to Set2 selects that qualitative ColorBrewer set without changing which state is mapped to which legend entry.

Generate a dataset for visualization:


Create a figure:

Transcript

And a continuous example, which is where the distiller function comes in. Same palette family, but corn yield is numeric, so the palette has to be interpolated into a smooth gradient rather than used as a handful of discrete swatches, and distiller is what does that. The palette here, red-yellow-green, is a diverging one, so pause on whether it is really appropriate: diverging palettes imply a meaningful middle, and if yield has no natural centre point, a sequential palette would represent the data more honestly.

The filter removes yields at or below fifty. Geom_point maps d-three-five-nine to x, corn yield to y, and corn yield again to colour, so the legend is a continuous colour bar for the same variable plotted vertically. Scale_color_distiller selects the colour aesthetic, handles its numeric values continuously, and uses the RdYlGn palette named in the palette argument.

Set color scale manually

Transcript

Sometimes none of the ready-made palettes is what you want, and you simply need specific colours for specific groups. That is what the manual scales are for. The mechanism is a named vector: each group name paired with the colour it should get. Two things worth noticing. You can mix colour names and HEX codes freely in the same vector. And because the mapping is by name rather than by position, it does not matter what order the groups happen to appear in your data, and the colours stay attached to the right groups even if the data changes.

In the Instruction tab, cols assigns red to Colorado, blue to Nebraska, and hash-f-f-zero-zero-eight-zero to Kansas. Scale_fill_manual receives that vector through values because the box interiors, not point or line colours, are being controlled. The Example tab creates the same vector, then maps factor of year to x, corn yield to y, and state name to fill in the 2005 through 2010 data. Adding the manual fill scale makes ggplot look up each state by its name and use the corresponding value from cols.

Sometimes, you just want to pick colors yourself. In that case, you can use

  • scale_color_manual()
  • scale_fill_manual()

Inside the scale_*_manual() function, you provide a named vector where a sequence of group names and their corresponding colors are specified to the scale function via the values option.

For example, consider the box plot of corn yield for the three states in county_yield: Colorado, Kansas, and Nebraska. Then, a sample named vector looks like this:

(
  cols <- c("Colorado" = "red", "Nebraska" = "blue", "Kansas" = "#ff0080")
)

Note that you can mix color names and HEX codes freely.


Now that a named vector is created, you can do the following to impose the color scheme you just defined.

scale_fill_manual(values = cols)

Define a named color vector:


Create a figure:

Transcript

And the continuous counterpart, gradientn, where you build a smooth scale from your own colours. You give it a vector of colours and a matching vector of positions between zero and one, and the two must be the same length, because each colour is placed at its position and ggplot blends between neighbours. That is the key idea: the positions do not carve the bar into blocks, they are pins that the gradient is stretched between. Colours pinned close together give a fast transition, colours far apart a long gradual one, which is exactly what the example demonstrates.

The star in scale-star-gradientn is again the aesthetic, so the example uses scale_color_gradientn because corn yield is mapped to point colour. County yield is the data, d-one-five-nine is x, corn yield is y and colour, and size equal to zero-point-three sits outside aes to give every point the same small size. The four colour-position pairs are red at zero, orange at zero-point-two, green at zero-point-nine, and blue at one. Limits equal to 100 and 250 define the data values at the ends of the scale. Within that span, zero-point-two corresponds to 130 bushels per acre and zero-point-nine to 235. That leaves a long orange-to-green transition from 130 to 235 and compresses the green-to-blue transition into the top tenth, exactly as the callout’s position rule predicts.

How

You can use scale_*_gradientn() to create your own continuous color scale.


Syntax

scale_*_gradientn(colors, values)

  • colors: a vector of colors
  • values: a vector of numbers ranging from 0 to 1
  • limits: define the lower and upper bounds of the scale bar

Each colour is positioned at the corresponding entry of values, and ggplot2 blends smoothly between neighbouring colours. So colors and values must have the same length.

Reading a positions vector

Colours bunched close together in values give a fast transition; colours far apart give a long, gradual one.

Create a figure:


Four colours, four positions. Read it as: red sits at the bottom of the scale, orange a fifth of the way up, green nine tenths of the way up, and blue right at the top.

With limits = c(100, 250), those positions correspond to actual yields of 130 and 235 bu/acre (100 + (250-100)\times 0.2 and 100 + (250-100)\times 0.9).

So the long stretch from 130 to 235 bu/acre is one slow orange-to-green blend, which is why warm and green tones fill most of the bar, while blue is squeezed into the top tenth.