ggplot2: Splitting facet/strip text into two lines
Asked Answered
H

4

25

Consider the following ggplot2 graph with long facet/strip text broken in two lines. The text goes outside the area devoted to facet titles.

library(ggplot2)
x <- c(1:3, 1:3)
y <- c(3:1, 1:3)
grp <- c(0, 0, 0, 1, 1, 1)
p <- qplot(x=x, y=y) + geom_line() + facet_wrap(~ grp)
grob <- ggplotGrob(p)
strip.elem.y <- grid.ls(getGrob(grob, "strip.text.x", 
                grep=TRUE, global=TRUE))$name
grob <- geditGrob(grob, strip.elem.y[1], 
        label="First line and\n second line" )
grid.draw(grob)

Is there a way to increase the height of the strip text area ?

Hotfoot answered 29/1, 2012 at 10:59 Comment(1)
You can use str_wrap function from stringr inside a transform. See Ista's answer for details.Doublepark
S
9

I tried this a variety of ways but was frustrated getting the paste(strwrap(text, width=40), collapse=" \n") to give me results for the single row of data and not concatenate the each bit of text from the entire list.

I came up with a solution that worked best for me. I wrote a function like the one below. Given a dataframe data with column text

wrapit <- function(text) {
  wtext <- paste(strwrap(text,width=40),collapse=" \n ")
  return(wtext)
}

data$wrapped_text <- llply(data$text, wrapit)
data$wrapped_text <- unlist(data$wrapped_text)

After I called this function, I just applied my labeller function to the wrapped_text column instead of the text column.

Screening answered 9/12, 2012 at 4:24 Comment(0)
R
63

ggplot2 supports a built in way of doing this using label_wrap_gen.

x <- c(1:3, 1:3)
y <- c(3:1, 1:3)
grp = c(rep("group 1 with a long name",3),rep("group 2 with a long name",3))
d = data.frame(x = x, y =y, grp = grp)
ggplot(d, aes(x=x,y=y)) + geom_line() + facet_wrap(~ grp, labeller = label_wrap_gen(width=10))
Rumrunner answered 30/8, 2017 at 21:45 Comment(0)
P
21

You can use a 2-line label:

grp <- c(rep("foo\nbar",3), 1, 1, 1)
qplot(x=x, y=y) + geom_line() + facet_wrap(~ grp)
Pakistan answered 29/1, 2012 at 11:6 Comment(1)
+1 Very simple, and gives complete control over where you want the breaks to appear. This should be the preferred solution IMO.When
S
9

I tried this a variety of ways but was frustrated getting the paste(strwrap(text, width=40), collapse=" \n") to give me results for the single row of data and not concatenate the each bit of text from the entire list.

I came up with a solution that worked best for me. I wrote a function like the one below. Given a dataframe data with column text

wrapit <- function(text) {
  wtext <- paste(strwrap(text,width=40),collapse=" \n ")
  return(wtext)
}

data$wrapped_text <- llply(data$text, wrapit)
data$wrapped_text <- unlist(data$wrapped_text)

After I called this function, I just applied my labeller function to the wrapped_text column instead of the text column.

Screening answered 9/12, 2012 at 4:24 Comment(0)
J
5

Expanding on the useful example from @groceryheist we can use the argument multi_line = True with label_wrap_gen() to get the desired effect without having to specify a fixed width.

library(ggplot2)

x = c(1:3, 1:3)
y = c(3:1, 1:3)
grp = c(rep("group 1 with a very very very long name",3),
        rep("group 2 with an even longer name",3))
df = data.frame(x = x, y =y, grp = grp)

ggplot(df, aes(x,y)) +
        geom_line() + 
        facet_wrap(~ grp, 
                   labeller = label_wrap_gen(multi_line = TRUE))

Ref: https://ggplot2.tidyverse.org/reference/labellers.html

Joubert answered 23/8, 2022 at 20:16 Comment(2)
Is it also somehow possible to modify the number of lines within the title (labeller) ?Ruse
Not that I'm aware of (see: ggplot2.tidyverse.org/reference/labellers.html) If you want that much control then I'd use the method of inserting \n into the label strings as per the answers above. Don't think there's a half-way house between fully automatic and manual.Joubert

© 2022 - 2024 — McMap. All rights reserved.