13 Guides: legends and axes
In Chapter 12 I discussed the way a scale maps a variable to an aesthetic. This chapter is a natural extension of the last and discusses the role scales play in controlling the guide (the axis or legend associated with the scale). Guides allow you to read observations from the plot and map them back to their original values. In ggplot2, guides are produced automatically based on the layers in your plot. This is very different to base R graphics, where you are responsible for drawing the legends by hand. In ggplot2, you don’t directly control the legend; instead you set up the data so that there’s a clear mapping between data and aesthetics, and a legend is generated for you automatically. This can be frustrating when you first start using ggplot2, but once you get the hang of it, you’ll find that it saves you time, and there is little you cannot do. If you’re struggling to get the legend you want, it’s likely that your data is in the wrong form.
You might find it surprising that axes and legends are the same type of thing, but while they look very different there are many natural correspondences between the two, as shown in table below and in Figure 13.1.
|Ticks & grid line||Key||
|Tick label||Key label||
The early sections of this chapter highlight functionality that is shared by axes and legends. Section 13.1 discusses the
name argument, while Section 13.2 covers the
labels arguments in more detail. However, legends are more complicated than axes because:
A legend can display multiple aesthetics (e.g. colour and shape), from multiple layers, and the symbol displayed in a legend varies based on the geom used in the layer.
Axes always appear in the same place. Legends can appear in different places, so you need some global way of controlling them.
Legends have considerably more details that can be tweaked: should they be displayed vertically or horizontally? How many columns? How big should the keys be?
As a consequence there are some extra options that only apply to legends, and the later sections in the chapter focus on this legend-specific behaviour.
13.1 Scale name
The first argument to the scale function,
name, is the axes/legend title. You can supply text strings (using
\n for line breaks) or mathematical expressions in
quote() (as described in
Because tweaking these labels is such a common task, there are three
helpers that save you some typing:
There are two ways to remove the axis label. Setting it to
"" omits the label, but still allocates space;
NULL removes the label and its space. Look closely at the left and bottom borders of the following two plots. I’ve drawn a grey rectangle around the plot to make it easier to see the difference.
13.2 Scale breaks and labels
breaks argument controls which values appear as tick marks on axes and keys on legends. Each break has an associated label, controlled by the
labels argument. If you set
labels, you must also set
breaks; otherwise, if data changes, the breaks will no longer align with the labels.
The following code shows some basic examples for both axes and legends.
If you want to relabel the breaks in a categorical scale, you can use a named labels vector:
To suppress breaks (and for axes, grid lines) or labels, set them to
13.2.1 Break and label functions
Additionally, you can supply a function to
breaks function should have one argument, the limits (a numeric vector of length two), and should return a numeric vector of breaks. The
labels function should accept a numeric vector of breaks and return a character vector of labels (the same length as the input). The scales package provides a number of useful labelling functions:
scales::comma_format()adds commas to make it easier to read large numbers.
scales::unit_format(unit, scale)adds a unit suffix, optionally scaling.
scales::dollar_format(prefix, suffix)displays currency values, rounding to two decimal places and adding a prefix or suffix.
scales::wrap_format()wraps long labels into multiple lines.
See the documentation of the scales package for more details.
You can adjust the minor breaks (the faint grid lines that appear between the major grid lines) by supplying a numeric vector of positions to the
minor_breaks argument. This is particularly useful for log scales:
Note the use of
%o% to quickly generate the multiplication table, and that the minor breaks must be supplied on the transformed scale.
Recreate the following graphic:
Adjust the y axis label so that the parentheses are the right size.
List the three different types of object you can supply to the
breaksargument. How do
Recreate the following plot:
What label function allows you to create mathematical expressions? What label function converts 1 to 1st, 2 to 2nd, and so on?
What are the three most important arguments that apply to both axes and legends? What do they do? Compare and contrast their operation for axes vs. legends.
13.2.3 Dates: A special case
An important special case arises when an aesthetic is mapped to date or date/time data, which are treated as continuous variables with special labels. The ggplot2 package supports the
Date (for dates) and
POSIXct (for date/times) classes, as well as the
hms class provided by the hms package (Müller 2020). If your dates are in a different format you will need to convert them using
hms::as_hms(). For appropriately formatted data mapped to the x aesthetic, ggplot2 will default to
scale_x_datetime(), and there are similar scales for other aesthetics. In most respects date scales behave the same way as other continuous scales, but they have special behaviour for dates that is worth noting. Specifically, in addition to the usual
labels arguments, date scales have special
date_labels arguments that allow you to work in date-friendly units:
date_minor_breaks()allows you to position breaks by date units (years, months, weeks, days, hours, minutes, and seconds). For example,
date_breaks = "2 weeks"will place a major tick mark every two weeks.
date_labelscontrols the display of the labels using the same formatting strings as in
hour, in 12-hour clock (1-12)
hour, in 12-hour clock (01-12)
hour, in 24-hour clock (00-23)
day of week, abbreviated (Mon-Sun)
day of week, full (Monday-Sunday)
day of month (1-31)
day of month (01-31)
month, numeric (01-12)
month, abbreviated (Jan-Dec)
month, full (January-December)
year, without century (00-99)
year, with century (0000-9999)
For example, if you wanted to display dates like 14/10/1979, you would use the string
The code below illustrates some of these parameters.
13.3 Legends for multiple layers
The previous sections describe features of ggplot2 that are the same for both axes and legend, but because legends are more complex than axes, there are additional topics to discuss that pertain only to legends. First among these is the fact that a legend may need to draw symbols from multiple layers. For example, if you’ve mapped colour to both points and lines, the keys will show both points and lines. If you’ve mapped fill colour, you get a rectangle. Note the way the legend varies in the plots below:
By default, a layer will only appear if the corresponding aesthetic is mapped to a variable with
aes(). You can override whether or not a layer appears in the legend with
FALSE to prevent a layer from ever appearing in the legend;
TRUE forces it to appear when it otherwise wouldn’t. Using
TRUE can be useful in conjunction with the following trick to make points stand out:
Sometimes you want the geoms in the legend to display differently to the geoms in the plot. This is particularly useful when you’ve used transparency or size to deal with moderate overplotting and also used colour in the plot. You can do this using the
override.aes parameter of
guide_legend(), which you’ll learn more about shortly.
norm <- data.frame(x = rnorm(1000), y = rnorm(1000)) norm$z <- cut(norm$x, 3, labels = c("a", "b", "c")) ggplot(norm, aes(x, y)) + geom_point(aes(colour = z), alpha = 0.1) ggplot(norm, aes(x, y)) + geom_point(aes(colour = z), alpha = 0.1) + guides(colour = guide_legend(override.aes = list(alpha = 1)))
ggplot2 tries to use the fewest number of legends to accurately convey the aesthetics used in the plot. It does this by combining legends where the same variable is mapped to different aesthetics. The figure below shows how this works for points: if both colour and shape are mapped to the same variable, then only a single legend is necessary.
In order for legends to be merged, they must have the same
name. So if you change the name of one of the scales, you’ll need to change it for all of them.
13.4 Legend layout
A number of settings that affect the overall display of the legends are controlled through the theme system. You’ll learn more about that in Section 16.2, but for now, all you need to know is that you modify theme settings with the
The position and justification of legends are controlled by the theme setting
legend.position, which takes values “right”, “left”, “top”, “bottom”, or “none” (no legend).
Switching between left/right and top/bottom modifies how the keys in each legend are laid out (horizontal or vertically), and how multiple legends are stacked (horizontal or vertically). If needed, you can adjust those options independently:
legend.direction: layout of items in legends (“horizontal” or “vertical”).
legend.box: arrangement of multiple legends (“horizontal” or “vertical”).
legend.box.just: justification of each legend within the overall bounding box, when there are multiple legends (“top”, “bottom”, “left”, or “right”).
Alternatively, if there’s a lot of blank space in your plot you might want to place the legend inside the plot. You can do this by setting
legend.position to a numeric vector of length two. The numbers represent a relative location in the panel area:
c(0, 1) is the top-left corner and
c(1, 0) is the bottom-right corner. You control which corner of the legend the
legend.position refers to with
legend.justification, which is specified in a similar way. Unfortunately positioning the legend exactly where you want it requires a lot of trial and error.
There’s also a margin around the legends, which you can suppress with
legend.margin = unit(0, "mm").
13.5 Guide functions
The guide functions,
guide_legend(), offer additional control over the fine details of the legend. Legend guides can be used for any aesthetic (discrete or continuous) while the colour bar guide can only be used with continuous colour scales.
You can override the default guide using the
guide argument of the corresponding scale function, or more conveniently, the
guides() helper function.
guides() works like
labs(): you can override the default guide associated with each aesthetic.
Both functions have numerous examples in their documentation help pages that illustrate all of their arguments. Most of the arguments to the guide function control the fine level details of the text colour, size, font etc. You’ll learn about those in the themes chapter. Here I’ll focus on the most important arguments.
The legend guide displays individual keys in a table. The most useful options are:
ncolwhich specify the dimensions of the table.
byrowcontrols how the table is filled:
FALSEfills it by column (the default),
TRUEfills it by row.
reversereverses the order of the keys. This is particularly useful when you have stacked bars because the default stacking and legend orders are different:
override.aes: override some of the aesthetic settings derived from each layer. This is useful if you want to make the elements in the legend more visually prominent. See discussion in Section 13.3.
default.unit) allow you to specify the size of the keys. These are grid units, e.g.
The colour bar guide is designed for continuous ranges of colors—as its name implies, it outputs a rectangle over which the color gradient varies. The most important arguments are:
default.unit) allow you to specify the size of the bar. These are grid units, e.g.
nbincontrols the number of slices. You may want to increase this from the default value of 20 if you draw a very long bar.
reverseflips the colour bar to put the lowest values at the top.
These options are illustrated below:
How do you make legends appear to the left of the plot?
What’s gone wrong with this plot? How could you fix it?
Can you recreate the code for this plot?
#> `geom_smooth()` using formula 'y ~ x'
Müller, Kirill. 2020. Hms: Pretty Time of Day. https://CRAN.R-project.org/package=hms.