29

I would like to create one separate plot per group in a data frame and include the group in the title.

With the iris dataset I can in base R and ggplot do this

plots1 <- lapply(split(iris, iris$Species), 
  function(x) 
    ggplot(x, aes(x=Petal.Width, y=Petal.Length)) +
      geom_point() +
      ggtitle(x$Species[1]))

Is there an equivalent using dplyr?

Here's an attempt using facets instead of title.

p <- ggplot(data=iris, aes(x=Petal.Width, y=Petal.Length)) + geom_point()
plots2 = iris %>% group_by(Species) %>% do(plots = p %+% . + facet_wrap(~Species))

where I use %+% to replace the dataset in p with the subset for each call.

Workaround with facets

or (working but complex) with ggtitle

plots3 = iris %>%
  group_by(Species) %>%
  do(
    plots = ggplot(data=.) +
      geom_point(aes(x=Petal.Width, y=Petal.Length)) +
      ggtitle(. %>% select(Species) %>% mutate(Species=as.character(Species)) %>% head(1) %>% as.character()))

Working example

The problem is that I can't seem to set the title per group with ggtitle in a very simple way.

Thanks!

zx8754
  • 52,746
  • 12
  • 114
  • 209
bytesinflight
  • 1,624
  • 2
  • 17
  • 28

3 Answers3

49

Use .$Species to pull the species data into ggtitle:

iris %>% group_by(Species) %>% do(plots=ggplot(data=.) +
         aes(x=Petal.Width, y=Petal.Length) + geom_point() + ggtitle(unique(.$Species)))
James
  • 65,548
  • 14
  • 155
  • 193
  • I tend to solve this problem via a custom-function where I use `species[1]` to specify the title. But if your actual plot is this simple, this certainly works. IE--my workflow is often `plot.cust <- function(...); iris %>% group_by(Species) %>% plot.cust(...)` – alexwhitworth Mar 13 '15 at 14:47
  • Very simple solution, did not occur to me! Thanks! – bytesinflight Mar 13 '15 at 14:48
  • 1
    @MatthewPlourde I suppose so, but it looks a bit clearer than `.$Species[1]`. – James Mar 13 '15 at 15:44
  • 6
    You could also use dplyr-like `first(.$Species)` – talat Mar 13 '15 at 16:33
  • Actually just using ggtitle(.$Species) seems to work, but I don't know why, and I haven't checked the speed of any of the suggestions. Thanks again! – bytesinflight Mar 16 '15 at 21:06
17
library(dplyr, warn.conflicts = FALSE)
library(ggplot2)

plots3 <- iris %>%
  group_by(Species) %>%
  group_map(~ ggplot(.) + aes(x=Petal.Width, y=Petal.Length) + geom_point() + ggtitle(.y[[1]]))

length(plots3)
#> [1] 3
# for example, the second plot :
plots3[[2]]

Created on 2021-11-19 by the reprex package (v2.0.1)

moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
  • Interesting to learn about group_map, though I found this didn't work with dplyr 0.8.3, plots3 returns 3 1x1 tibbles. For the last line I think it should be plots3[[2]]$plots. If one wanted to save those plots within the pipe, you think that's possible? Or better to loop through plots3[[n]]$plots ? Cheers! – dez93_2000 Nov 13 '19 at 22:07
  • What's `.y` in `ggtitle`? I guess it contains information about the different groups? Where does the name `y` come in and how can I access it? – Jon May 18 '21 at 20:02
  • 2
    `.y` is a tibble with exactly one row and columns for each grouping variable, see the doc. So here I take the content of the first and only column, which is the name. – moodymudskipper May 19 '21 at 07:24
  • 2
    This solution needs updating. – Brad Oct 21 '21 at 06:02
  • Sorry all, I was too quick with my edit, now fixed. – moodymudskipper Nov 19 '21 at 14:34
2

This is another option using rowwise:

plots2 = iris %>% 
    group_by(Species) %>% 
    do(plots = p %+% .) %>% 
    rowwise() %>%
    do(x=.$plots + ggtitle(.$Species))
Matthew Plourde
  • 43,932
  • 7
  • 96
  • 113