5

I have scoured SO and other sites but cannot find a solution to my problem with my ggtitle(). I know there are other examples out there, but I haven't been able to apply them directly to my issue successfully.

What I am trying to do is make a multi-line plot title where the first line is bold, size = 10 and then underneath that first line is a second (and potentially third), more descriptive line(s) in non-bold, size=8. The kicker is that I'm trying to make this left justified over the y-axis. This left justification is what makes this question unique because previous answers, including the one linked by a moderator, use atop() which does not allow left justification or does not include it in the answer linked.

Here is what my plot currently looks like:

enter image description here

title <- paste("An analysis of the mtcars dataset  ")
subheader <- paste("How does mpg change by   \n number of cyl?")

ggplot(mtcars, aes(mpg,cyl))+ 
  geom_smooth(aes(fill="Mean",level=0.095)) +
  ggtitle(paste0(title,"\n",subheader)) +
  scale_fill_grey(name='95% Confidence\n Interval',start=.65,end=.65) +
  theme(plot.title = element_text(size = rel(2.0),hjust=-.1,face="bold"))

I have tried using bquote(), mtext(), atop(), and even a grungy paste() with extra spaces included to push the title....but I haven't been able to find a solution.

Please, let me know if you have any questions or need any clarification. I appreciate any and all help!

medavis6
  • 843
  • 10
  • 32
  • I think when you remove that theme(plot.title = element_text(size = rel(2.0),hjust=-.1,face="bold")) you get what you want? is that right? – MLavoie Feb 12 '16 at 16:15
  • @MLavoie When I remove the last line, I get [this](https://i.gyazo.com/96347f744772fead6fbca0778bc98e17.png), which, I apologize if I'm not being clear enough, but is not what I want. I want the top line bold, in a larger font, and all lines left justified above the y-axis. – medavis6 Feb 12 '16 at 16:18
  • @ScottJacobs I came across that and many other questions in my previous attempts but it seems that if you use that method that you cannot have both `atop()` and left justification using `hjust()` or `vjust()`. – medavis6 Feb 12 '16 at 16:53

3 Answers3

6

Here are three approaches using tableGrob to split a text into multiple lines,

enter image description here

title <- paste("An analysis of the mtcars dataset  ")
subheader <- paste("How does mpg change by \nnumber of cyl?")

library(gridExtra)
library(grid)

table_label <- function(label, params=list())  {

  params1 <- modifyList(list(hjust=0, x=0, fontsize=12, fontface=2), params)
  params2 <- modifyList(list(hjust=0, x=0, fontsize=8, fontface=1), params)

  mytheme <- ttheme_minimal(core = list(fg_params = params2),
                            colhead = list(fg_params = params1))
  disect <- strsplit(label, "\\n")[[1]]
  m <- as.matrix(disect[-1])
  g <- tableGrob(m, cols=disect[1], theme=mytheme)
  g$widths <- unit(1,"npc")
  g
}


p <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
      geom_line() 

## add a title, note: centering is defined wrt the whole plot
grid.arrange(p, top=table_label(paste0(title,"\n",subheader), 
                                params=list(x=0.1)))

## to align precisely with the panel, use gtable instead
library(gtable)
titleify <- function(p, label, ...){
  g <- ggplotGrob(p)
  title <- table_label(label, ...)
  g <- gtable_add_grob(g, title, t=1, l=4)
  g$heights <- grid:::unit.list(g$heights)
  g$heights[1] <-list(sum(title$heights))
  grid.newpage()
  grid.draw(g)
}

titleify(p, paste0(title,"\n",subheader))

## we can also hack ggplot2 to define a custom element for the title
## though be warned the hardware supporting your computer may be damaged by head banging

element_custom <- function() {
  structure(list(), class = c("element_custom", "element_text"))
}

element_grob.element_custom <- function(element, label="", ...)  {
  table_label(label)
}

# default method is unreliable
heightDetails.gtable <- function(x) sum(x$heights)

ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
  geom_line() + 
  ggtitle(paste0(title,"\n",subheader))+
  (theme_grey() %+replace% theme(plot.title = element_custom()))
baptiste
  • 75,767
  • 19
  • 198
  • 294
4

You can use this approach, but justification is unbelievably finicky. Because atop was designed to format equations, it automatically centers, so you need to hack around that by inserting spaces before the longer title and playing with hjust. There seems to be an upper limit on title size with this approach, too; there has to be space for the spaces to move off to the left.

What I managed:

library(ggplot2)
title <- paste("           An analysis of the mtcars dataset")
subheader <- paste("How does mpg change by number of cyl?")

ggplot(mtcars, aes(mpg,cyl)) + 
  geom_smooth(aes(fill="Mean",level=0.095)) +
  ggtitle(bquote(atop(bold(.(title)), atop(.(subheader), ""))))  +
  scale_fill_grey(name='95% Confidence\n Interval',start=.65,end=.65) +
  theme(plot.title = element_text(size = rel(1.3),hjust=-.6,face="bold"))

plot with title and subtitle left justified

To go any further, you're probably going to need to break into grid.

Community
  • 1
  • 1
alistaire
  • 42,459
  • 4
  • 77
  • 117
4

Here's a way to use custom annotation. The justification is straightforward, but you have to determine the coordinates for the text placement by hand. Perhaps you could create some logic to automate this step if you're going to do this a lot.

library(grid) 

title <- paste("An analysis of the mtcars dataset  ")
subheader <- paste("How does mpg change by\nnumber of cyl?")

p1 = ggplot(mtcars, aes(mpg,cyl))+ 
  geom_smooth(aes(fill="Mean",level=0.095)) +
  scale_fill_grey(name='95% Confidence\n Interval',start=.65,end=.65) 

p1 = p1 + 
  annotation_custom(grob=textGrob(title, just="left", 
                                  gp=gpar(fontsize=10, fontface="bold")),
                    xmin=9.8, xmax=9.8, ymin=11.7) +
  annotation_custom(grob=textGrob(subheader, just="left", 
                                  gp=gpar(fontsize=8, lineheight=1)),
                    xmin=9.8, xmax=9.8, ymin=10.4) +
  theme(plot.margin=unit(c(4,rep(0.5,3)), "lines"))

# Turn off clipping
p1 <- ggplot_gtable(ggplot_build(p1))
p1$layout$clip[p1$layout$name == "panel"] <- "off"
grid.draw(p1) 

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285