Search code examples
rggplot2facet

parsed labels along one facet axis, unparsed labels along the other


I'm trying to find a convenient solution to a scenario where I have multi-line, plain-text labels for one faceting dimension and plotmath-style math expressions for the other faceting dimensions. The various solutions to multi-line strip labels (such as here) are a bit clunky for my case; I have factor labels in snake_case and want to be able to convert them all to multiline versions programmatically without descending into substitute(bquote(parse(...)) hell (although solutions along those lines would also be accepted).

The direction I went was to (try to) write a custom labeller that would apply label_parsed() or leave the labels alone depending on whether they contained newlines or not. I can't seem to get quite the right syntax for the parsed stuff. The current structure of the multi-line, unparsed stuff is

List of 1
 $ : chr [1:4] "peak\ntime" "peak\nvirulence" "equilibrium\nvirulence" "relative\npeak"

which works, but the parsed stuff is

List of 3
 $ :List of 1
  ..$ :  expression(beta)
 $ :List of 1
  ..$ :  expression(gamma)
 $ :List of 1
  ..$ :  expression(x[1])

which doesn't. Applying unlist(r) and unlist(r,recursive=FALSE) to this structure both give error messages ...

The result (shown below) is that all three of the parsed labels get stacked up in the first column ...

?label_parsed says

The return value must be a rectangular list where each 'row' characterises a single facet. The list elements can be either character vectors or lists of plotmath expressions. When multiple elements are returned, they get displayed on their own new lines (i.e., each facet gets a multi-line strip of labels).

Setup: load package, define old and new labels

library(ggplot2); theme_set(theme_bw())
orig_sum_labs <- c("peak_time","peak_vir","eq_vir","rel_peak")
new_sum_labs <- c("peak time","peak virulence","equilibrium virulence",
                  "relative peak")
fake <- data.frame(f1=rep(orig_sum_labs,each=12),
                   f2=factor(rep(1:3,16),levels=1:3,
                             labels=c("beta","gamma","x[1]")),
                   x=rep(1:4,12),y=rep(1:4,12))

Put newlines into labels:

nn <- gsub(" ","\n",new_sum_labs)
fake$f1 <- factor(fake$f1,levels=orig_sum_labs,labels=nn)

My labeller function:

L <- function(labels,multi_line=TRUE) {
    r <- if (all(grepl("\n",labels[[1]]))) {
        list(as.character(labels[[1]]))
    } else {
        label_parsed(labels[[1]],multi_line=multi_line)
    }
    ## browser()
    return(r)
}
class(L) <- "labeller"

Try it out:

ggplot(fake,aes(x,y))+geom_point()+
    facet_grid(f1~f2,labeller=L)

enter image description here


Solution

  • Oops. It's a bit magical (and I would happily accept answers that explain clearly what's going on), but I managed to solve my problem. In my labeller function above,

    label_parsed(labels[[1]],multi_line=multi_line)
    

    doesn't work, but

    label_parsed(labels,multi_line=multi_line)
    

    works ...

    enter image description here