Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Left justify text from multi-line facet labels

Tags:

r

ggplot2

I'm using facet_grid() to display some data, and I have facet labels that span multiple lines of text (they contain the "\n" character).

require(ggplot2)

#Generate example data
set.seed(3)
df = data.frame(facet_label_text = rep(c("Label A",
                                         "Label B\nvery long label",
                                         "Label C\nshort",
                                         "Label D"),
                                       each = 5),
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

#Plot test data
ggplot(df, aes(x = time, y = value)) +
    geom_line() +
    facet_grid(facet_label_text ~ .) +
    theme(strip.text.y = element_text(angle = 0, hjust = 0))

So by using the hjust = 0 argument, I can left-align facet label text as a unit.

enter image description here

What I would like to do is left-align each individual line of text. So "Label B" and "very long label" are both aligned along the left side, rather than centered relative to each other (ditto for "Label C" and "short"). Is this possible in ggplot2?

like image 518
Brain_Food Avatar asked Jan 12 '16 17:01

Brain_Food


3 Answers

This is fairly straightforward using grid's grid.gedit function to edit the strips.

library(ggplot2)  # v2.1.0
library(grid)

# Your data
set.seed(3)
df = data.frame(facet_label_text = rep(c("Label A",
                                         "Label B\nvery long label",
                                         "Label C\nshort",
                                         "Label D"), 
                                       each = 5),
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

# Your plot
p = ggplot(df, aes(x = time, y = value)) +
    geom_line() +
    facet_grid(facet_label_text ~ .) +
    theme(strip.text.y = element_text(angle = 0, hjust = 0))
p

# Get a list of grobs in the plot
grid.ls(grid.force())  

# It looks like we need the GRID.text grobs.
# But some care is needed:
# There are GRID.text grobs that are children of the strips;
# but also there are GRID.text grobs that are children of the axes.
# Therefore, a gPath should be set up 
# to get to the GRID.text grobs in the strips

# The edit
grid.gedit(gPath("GRID.stripGrob", "GRID.text"),  
         just = "left", x = unit(0, "npc"))

Or, a few more lines of code to work with a grob object (in place of editing on screen as above):

# Get the ggplot grob
gp = ggplotGrob(p)
grid.ls(grid.force(gp))

# Edit the grob
gp = editGrob(grid.force(gp), gPath("GRID.stripGrob", "GRID.text"), grep = TRUE, global = TRUE,
          just = "left", x = unit(0, "npc"))

# Draw it
grid.newpage()
grid.draw(gp)

enter image description here

like image 200
Sandy Muspratt Avatar answered Oct 29 '22 19:10

Sandy Muspratt


Until someone comes along with a real solution, here's a hack: Add space in the labels to get the justification you want.

require(ggplot2)

#Generate example data
set.seed(3)
df = data.frame(facet_label_text = rep(c("Label A",
                                         "Label B           \nvery long label",
                                         "Label C\nshort     ",
                                         "Label D"),
                                       each = 5),
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

#Plot test data
ggplot(df, aes(x = time, y = value)) +
  geom_line() +
  facet_grid(facet_label_text ~ .) +
  theme(strip.text.y = element_text(angle = 0, hjust = 0))

enter image description here

like image 32
eipi10 Avatar answered Oct 29 '22 18:10

eipi10


There may be a cleaner way to do this but I didn't find a way to do this within ggplot2. The padwrap function could be more generalized as it basically does just what you requested. To get the justification right, I had to use a mono-spaced font.

# Wrap text with embedded newlines: space padded and lef justified.
# There may be a cleaner way to do this but this works on the one
# example.  If using for ggplot2 plots, make the font `family`
# a monospaced font (e.g. 'Courier')
padwrap <- function(x) {
    # Operates on one string
    padwrap_str <- function(s) {
        sres    <- strsplit(s, "\n")
        max_len <- max(nchar(sres[[1]]))
        paste( sprintf(paste0('%-', max_len, 's'), sres[[1]]), collapse = "\n" )
    }
    # Applys 'padwrap' to a vector of strings
    unlist(lapply(x, padwrap_str))
}

require(ggplot2)

facet_label_text = rep(c("Label A",
                         "Label B\nvery long label",
                         "Label C\nshort",
                         "Label D"), 5)
new_facet_label_text <- padwrap(facet_label_text)

#Generate example data
set.seed(3)
df = data.frame(facet_label_text = new_facet_label_text,
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

#Plot test data
ggplot(df, aes(x = time, y = value)) +
    geom_line() +
    facet_grid(facet_label_text ~ .) +
    theme(strip.text.y = element_text(angle = 0, hjust = 0, family = 'Courier'))

The strip text is left justified in the image below

enter image description here

like image 9
steveb Avatar answered Oct 29 '22 18:10

steveb