I’m very pleased to announce the release of ggplot2 2.0.0. I know I promised that there wouldn’t be any more updates, but while working on the 2nd edition of the ggplot2 book, I just couldn’t stop myself from fixing some long standing problems.

On the scale of ggplot2 releases, this one is huge with over one hundred fixes and improvements. This might break some of your existing code (although I’ve tried to minimise breakage as much as possible), but I hope the new features make up for any short term hassle. This blog post documents the most important changes:

These are described in more detail below. See the release notes for a complete list of all changes.


Perhaps the bigggest news in this release is that ggplot2 now has an official extension mechanism. This means that others can now easily create their on stats, geoms and positions, and provide them in other packages. This should allow the ggplot2 community to flourish, even as less development work happens in ggplot2 itself. See vignette("extending-ggplot2") for details.

Coupled with this change, ggplot2 no longer uses proto or reference classes. Instead, we now use ggproto, a new OO system designed specifically for ggplot2. Unlike proto and RC, ggproto supports clean cross-package inheritance, which is necessary for extensibility. Creating a new OO system isn’t usually the right solution, but I’m pretty sure it was necessary here. Read more about it in the vignette.

New and updated geoms

ggplot(mpg, aes(cyl, hwy))

ggplot(mpg, aes(cty, hwy)) +
ggplot(mpg, aes(cty, hwy)) +

df <- expand.grid(x = 1:2, y = 1:2)
ggplot(df, aes(x, y, xend = x + 0.5, yend = y + 0.5)) +
  geom_curve(aes(colour = "curve")) +
  geom_segment(aes(colour = "segment"))

ggplot(mpg, aes(cyl)) +

ggplot(mpg, aes(cyl)) +
  geom_histogram(binwidth = 1)

If you got into the (bad) habit of using geom_histogram() to create bar charts, or geom_bar() to create histograms, you’ll need to switch.

ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars))) +
  geom_point() +
  geom_text(nudge_y = 0.5, check_overlap = TRUE)

(Labelling points well is still a huge pain, but at least these new features make life a lit better.)

grid <- expand.grid(
  x = seq(-pi, pi, length = 50),
  y = seq(-pi, pi, length = 50)
) %>% mutate(r = x ^ 2 + y ^ 2, z = cos(r ^ 2) * exp(-r / 6))

ggplot(grid, aes(x, y)) +
  geom_raster(aes(fill = z)) +
  geom_label(data = data.frame(x = 0, y = 0), label = "Center") +
  theme(legend.position = "none") +

ggplot(mpg, aes_(~displ, ~cty)) +
# Same as
ggplot(mpg, aes(displ, cty)) +


I’ve made a number of small tweaks to the default appearance:

ggplot(mpg, aes(factor(cyl), fill = drv)) +
  geom_bar(colour = "black", size = 1) +

p <- ggplot(mpg, aes(displ,hwy, colour = manufacturer)) +
  geom_point() +
  theme(legend.position = "bottom")
# Revert back to previous behaviour
p + guides(colour = guide_legend(nrow = 1))

Facet labels

Thanks to the work of Lionel Henry, facet labels have received three major improvements:

  1. You can switch the position of facet labels so they’re next to the axes.

  2. facet_wrap() now supports custom labellers.

  3. You can create combined labels when facetting by multiple variables.

Switching the labels

The new switch argument allows you to switch the labels to display near the axes:

data <- transform(mtcars,
  am = factor(am, levels = 0:1, c("Automatic", "Manual")),
  gear = factor(gear, levels = 3:5, labels = c("Three", "Four", "Five"))

ggplot(data, aes(mpg, disp)) +
  geom_point() +
  facet_grid(am ~ gear, switch = "both")

This is especially useful when the labels directly characterise the axes. In that situation, switching the labels can make the plot clearer and more readable. You may also want to use a neutral label background by setting strip.background to element_blank():

data <- mtcars %>%
    Logarithmic = log(mpg),
    Inverse = 1 / mpg,
    Cubic = mpg ^ 3,
    Original = mpg
) %>% tidyr::gather(transformation, mpg2, Logarithmic:Original)

ggplot(data, aes(mpg2, disp)) +
  geom_point() +
  facet_wrap(~transformation, scales = "free", switch = "x") +
  theme(strip.background = element_blank())

Wrap labeller

A longstanding issue in ggplot was that facet_wrap() did not support custom labellers. Labellers are small functions that make it easy to customise the labels. You can now supply labellers to both wrap and grid facets:

ggplot(data, aes(mpg2, disp)) +
  geom_point() +
  facet_wrap(~transformation, scales = "free", labeller = "label_both")

Composite margins

Labellers have now better support for composite margins when you facet over multiple variable with +. All labellers gain a multi_line argument to control whether labels should be displayed as a single line or over multiple lines, one for each factor.

The labellers still work the same way except for label_bquote(). That labeller makes it easy to write mathematical expression involving the values of facetted factors. Historically, label_bquote() could only specify a single expression for all margins and factor. The factor value was referred to via the backquoted placeholder .(x). Now that it supports expressions combining multiple factors, you must backquote the variable names themselves. In addition, you can provide different expressions for each margin:

my_labeller <- label_bquote(
  rows = .(am) / alpha,
  cols = .(vs) ^ .(cyl)

ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  facet_grid(am ~ vs + cyl, labeller = my_labeller)


I’ve given the documentation a thorough overhaul:

Deprecated features

A number of geoms have been renamed to be more consistent. The previous names will continue to work for the forseeable future, but you should switch to the new names for new work.

All defunct functions have been removed.