5

This is part-2 to my previous question (getting constant text size while using atop function in r).

Now the issue relates to how I can prevent plotmath from centering the text to avoid the extra spacing (highlighted here in yellow). I want everything aligned to the right side of the plot.

(Unfortunately, I can't replace substitute with expression if that's what your suggestion is going to be.)

Any suggestions?

library(ggplot2)

ggplot(iris, aes(Species, Sepal.Length)) +
  geom_boxplot()  +
  labs(caption = substitute(atop(
    atop(
      displaystyle("layer1 is small"),
      displaystyle("layer2 is a bit longer")
    ),
    "layer3 is super-duper longgggggggg"
  )))

enter image description here

Indrajeet Patil
  • 4,673
  • 2
  • 20
  • 51
  • 1
    There are already few related question (see plotmath alignment). E.g., would manually adding spaces as in https://stackoverflow.com/questions/35781950/r-alignment-of-axis-labels-with-expressions would be acceptable? Rather than actually manually trying different numbers of spaces it could be somewhat automatized. – Julius Vainora Nov 10 '18 at 14:58
  • Yeah, but the problem is that all of them use `expression` to create the caption, while I am using `substitute`. The solutions provided of course don't work well with `substitute`. (In case you are wondering why the obsession with `substitute`, this is the kind of context where I am using it- https://github.com/IndrajeetPatil/ggstatsplot/blob/c07736cc4abfc81430b3e725e57a60b883a444d9/R/helpers_ggbetween_subtitles.R#L167-L231) – Indrajeet Patil Nov 10 '18 at 15:02
  • 1
    So I guess the problem is that the text is not of fixed length. Why don't you update your example a little where manually adding, say, 30 spaces doesn't fix the problem? Then I'll give it a try. – Julius Vainora Nov 10 '18 at 15:11
  • The text in `layer 2` and `layer 3` are of fixed length, so that will be easy to fix. It's the `layer 1` that will vary because the user can choose to enter `caption` of any length they want. – Indrajeet Patil Nov 10 '18 at 18:54

2 Answers2

3

Let's start with good news. Here's a function that adds enough leading spaces to from as to be as long as the longest element from the list to:

push <- function(from, to)
  sprintf(paste("%", max(nchar(from), max(nchar(to))), "s"), from)

Next we have three layers, which also may use substitute (as I understand, in your case only the first one uses it).

l1 <- substitute("layer1 is small")
l2 <- "layer2 is a bit longer"
l3 <- "layer3 is super-duper longgggggggg"

Now the bad news is that push achieves the desired effect only with mono fonts, which is not the default family in ggplot2. There are multiple question on SO regarding fonts, so perhaps you may import some other mono font, if you prefer.

ggplot(iris, aes(Species, Sepal.Length)) +
  geom_boxplot()  +
  labs(caption = substitute(atop(atop(textstyle(l1), textstyle(l2)), textstyle(l3)), 
                            list(l1 = push(l1, list(l2 ,l3)),
                                 l2 = push(l2, list(l1, l3)), 
                                 l3 = push(l3, list(l2, l3))))) +
  theme(plot.caption = element_text(family = "mono"))

enter image description here

Julius Vainora
  • 47,421
  • 9
  • 90
  • 102
1

easiest might be to add text grobs row by row in a gtable,

gtable_add_caption <- function(p, cap, g = ggplotGrob(p), 
                               hjust=1, x=unit(1,"npc"), pad = unit(c(2,2),"mm"),
                               ...){

  for(ii in seq_along(cap)){
    line <- tryCatch(parse(text = cap[ii]), error = function(e) cap[ii])
    tg <- textGrob(line, x = x - pad[1], hjust = hjust, gp=gpar(...))
    hg <- grobHeight(tg) 
    g <- gtable_add_rows(g, hg + pad[2])
    g <- gtable_add_grob(g, tg, t = nrow(g), l=1, r=ncol(g))
  }

  g
}

p <- ggplot()
ggplot(iris, aes(Species, Sepal.Length)) +
  geom_boxplot() ->p
g <- gtable_add_caption(p, c("first line", "integral(frac(1,x-1)*dx,alpha,beta)", "thirdddddddddddddddddd line"))
grid.newpage()
grid.draw(g)