Hot questions for Using Ggplot2 in ggtitle


I would like to left align the title in a plot like this

ggplot(data = economics, aes(x = date, y = unemploy)) +
 geom_line() +
 ggtitle("Unemployment in USA between 1967 and 2007") +
 xlab("") +
 ylab("Unemployed [thousands]")

First attempt

ggplot(data = economics, aes(x = date, y = unemploy)) + geom_line() +
 ggtitle("Unemployment in USA for some years") +
 xlab("") +
 ylab("Unemployed [thousands]") +
 theme(plot.title = element_text(hjust = -0.45, vjust=2.12)))

Yay success! But wait... there's more... now I want to change the title to something else.

ggplot(data = economics, aes(x = date, y = unemploy)) +
 geom_line() +
 ggtitle("Unemployment in USA between 1967 and 2007") +
 xlab("") +
 ylab("Unemployed [thousands]") +
 theme(plot.title = element_text(hjust = -0.45, vjust=2.12))

So now I need to adjust hjust... :(

The question

How can I make the title left justified (a couple of pixels left of the y axis label or so) over and over again without messing with the hjust value? Or what is the relationship between hjust and the length of the string?

I have tried to annotate manually according to this question, but then I got only the title, and nothing else for some reason - and an error.

Thank you!


Until someone comes up with a better solution, one way would be something like

p <- ggplot(data = economics, aes(x = date, y = unemploy)) +
    geom_line() +
    labs(x = NULL, y = "Unemployed [thousands]", title = NULL)

title.grob <- textGrob(
    label = "Unemployment in USA for some years",
    x = unit(0, "lines"), 
    y = unit(0, "lines"),
    hjust = 0, vjust = 0,
    gp = gpar(fontsize = 16))

p1 <- arrangeGrob(p, top = title.grob)


I would like to increase the font size of ggtitle and also the font should be bold. My code is as follows.

ggplot(df, aes(x1, y = value, colour = variable)) + 
  geom_point(size=2) + 
  ggtitle("male vs.female") +
        axis.title=element_text(size=14,face="bold")) + 
  theme(legend.text=element_text(size=12)) + 
  labs(x = "x axis", y = "y axis") + 
  ylim(0,100) + xlim(0,100) + 
  scale_colour_manual(values = c("red", "blue"), 
                      labels = c("male", "female"))


Use theme(), here is an example :

ggplot(cars, aes(x=speed,y=dist)) + 
    ggtitle("cars") + geom_point() + 
    theme(plot.title = element_text(size = 40, face = "bold"))

Inspired by this answer.


Consider a simple function, which adds a ggtitle to a grob
f <- function(PLOT, TITLE) {
  PLOT + ggtitle(TITLE)

Calling the function directly works as expected. However, calling the function via, ..) throws an error when TITLE is a language object

## Sample Data
TIT <- bquote(atop("This is some text",  atop(italic("Here is some more text"))))
P   <- qplot(x=1:10, y=1:10, geom="point")

f(P, TIT)

## FAILS, list(P, TIT))
## Error in labs(title = label) : could not find function "atop"

This of course only happens when TIT is a language object

TIT.char <- "This is some text\nHere is some more text", list(P, TIT.char))
## No Error

How can be used correctly when arguments are language objects?


Use, list(P, TIT), quote=TRUE)

instead. The problem is that your expression is being evaluated when you run By setting quote=TRUE it will quote the arguments to leave them un-evaluated when passing them along to f. You can also explicitly quote TIT, list(P, quote(TIT)))


I need to pass three variables to a ggtitle. Something like

g <- ggplot(data=data.frame(x=0,y=0))+geom_point(aes(x=x,y=y))
s<-sprintf("\\alpha=%f, \\lambda=%f, \\memory=%g",alpha,lambda,mem)

but unfortunately no greek letters are shown (I know there is expression but I could not figure out how to use it). With just one variable, there is a thread with a solution (bquote). For passing multiple variables there is another thread but does not treat greek symbols.

Many thanks


In order for the greek letters to render properly, you need to build an expression, not a string. Here's one way to do that

g + ggtitle(bquote(list(alpha==.(alpha), lambda==.(lambda), memory==.(mem))))

bquote() will work with any number of variables, not just one.


I have noticed that in ggplot when you add a title containing lowercase letters q,y,p,g,j the dimensions of the plot gets modified.


*the red lines have been added manually

As you can see, the height of the plot gets smaller when I add one of the above letters to the title

How can I keep the height constant between several plots with different titles?

Code used to produce the two plots:

# plot 1
ggplot(mpg, aes(factor(cyl), hwy)) + 
  geom_point(size=4) + 
  theme_bw() + 

# plot 2
ggplot(mpg, aes(factor(cyl), hwy)) + 
  geom_point(size=4) + 
  theme_bw() +


you could add a phantom lowercase letter, but that requires your text to be converted into a plotmath expression which may have some downsides, fontwise

.st <- function(s){

# plot 1
p1 <- ggplot(mpg, aes(factor(cyl), hwy)) + 
  geom_point(size=4) + 
  theme_bw() + 

# plot 2
p2 <- ggplot(mpg, aes(factor(cyl), hwy)) + 
  geom_point(size=4) + 
  theme_bw() +



I want to be able to change the background of the ggtitle of my ggplot to forestgreen while keeping the text white. The color shouldn't apply to the whole of the graph but only the title. This is what I have so far:

p <- ggplot(...)
p <- p + ggtitle("Market Updates") + labs(x = "Date", y = "High")
p <- p + theme(plot.title = element.text(hjust = 0.5, size = 20, 
               color = "#FFFFFF"))

I would like to make it look like this:


Update from comments.

There are a couple of approaches to do this; using facet_, as Axeman suggests, to create a strip above the plot (it is easier to change the the format of the strip than the title strip) , or you can create a title strip manually and then glue it to the plot.



# Create dummy variable to facet on: this name will appear in the strip
mtcars$tempvar <- "Market Updates"

# Basic plot
# Manually added legend to match your expected result
p <- ggplot(mtcars, aes(mpg, wt)) + 
        geom_line(aes(colour="Com")) +
        scale_colour_manual(name="", values=c(Com = "#228b22") ) +
        labs(x = "Date", y = "High")

Using facet_: this only adds the colour bar across the plot panel, although the title is centered.

p + facet_grid(. ~ tempvar) +
        theme(strip.background = element_rect(fill="#228b22"),
              strip.text = element_text(size=15, colour="white"))

Which produces

Using grid functions: this adds the colour bar across the plot panel, but the title is centered on the graphics window. (you could get a bit more control with positioning by adding it to the plot gtable)

my_g <- grobTree(rectGrob(gp=gpar(fill="#228b22")),
                 textGrob("Market Updates", x=0.5, hjust=0.5,
                                            gp=gpar(col="white", cex=1.5)))

grid.arrange(my_g, p, heights=c(1,9))

Which produces


Within the shiny context, how can I remove extra space after and before parentheses in the title of a plot? As shown in the below image, there are redundant spaces between "(" and "6" as well as between "+" and ")".

Also, I was wondering how can I break down the title in such a way that the county name as well as the age group appear in the next line.

Here is my code:

      "Percentage of Population Living with and Responsible for Grandchildren in", 


This should do the job for you. paste0 doesn't have sep = " " like paste and \n adds a new line. Add theme if you want the text to be centered.

      "Percentage of Population Living with and Responsible for Grandchildren in \n",
      " (",
  ) +
  theme(plot.title = element_text(hjust = 0.5))


I would like to subset my dataset based on the value of one variable and draw a ggplot with the value of that variable as the name of the plot AND as the ggtitle and save each plot.

Finally I would like to combine all graphs into one page.

Here is a simplified example.

y=rnorm(100, 0,1)
x=sample(100, 5, replace = T)

z=sample(k, 100, replace=T)

z <- factor(z, levels = c(1,2,3,4,5),
                    labels = c("red", "blue", "green", "purple", "yellow"))

dat=data.frame(y=y, x=x, z=z)

for(i in seq_len(k))
dat2 = data.frame(dat[dat$z=='i',])

i <- ggplot(dat2, aes(x=x, y=y)) +
  geom_point() +

grid.arrange(red, blue, green, purple,yellow, nrow = 2)

I am having trouble with saving the variable as i and line with the ggtitle(). The title should be different colors for each graph: red, blue, green....etc.

Thank you.


Your subsetting is incorrect: you used the string 'i' instead of the variable i.

You could simply use facets instead of your for loop and grid.arrange:

dat=data.frame(y=y, x=x, z=z)
ggplot(dat, aes(x = x, y = y)) + 
  geom_point() + 
  facet_wrap(~ z)

If you want to loop and arrange, you need to use a list:

plots <- list()

for(z in levels(z)) {
  dat2 = data.frame(dat[dat$z==z,])

  plots[[z]] <- ggplot(dat2, aes(x=x, y=y)) +
    geom_point() +

grid.arrange(plots$red, plots$blue, plots$green, plots$purple, plots$yellow, nrow = 2)

Note that I use the values of z for the looping variable rather than 1:5.

This is more elegantly done as:

colors <- c("red", "blue", "green", "purple", "yellow")
N <- 100

dat <- data.frame(x = sample(N, 5, replace = T),
                  y = rnorm(N, 0,1),
                  z = sample(colors, N, replace=T))

plot_color <- function(col, dat) {
  dat2 <- dat[dat$z==col,]

  ggplot(dat2, aes(x=x, y=y)) +
    geom_point() +

plots <- purrr::map(colors, plot_color, dat = dat), c(plots, nrow = 2))


I have a loop of 4 graphs with a character list like 'a, b, c, d', so in the title of each graph I want 'a', 'b', 'c' or 'd'. However, when I run my code, 'a' appears in all titles.

This is the dput of the data I am using.

structure(list(Point = c(5, 6, 7, 8), La = c(535, 565, 532, 587
), Ce = c(45, 46, 58, 43), Pr = c(56, 54, 43, 50), Nd = c(23, 
28, 18, 26)), class = c("spec_tbl_df", "tbl_df", "tbl", "data.frame"
), row.names = c(NA, -4L), spec = structure(list(cols = list(
Point = structure(list(), class = c("collector_double", "collector"
)), La = structure(list(), class = c("collector_double", 
"collector")), Ce = structure(list(), class = c("collector_double", 
"collector")), Pr = structure(list(), class = c("collector_double", 
"collector")), Nd = structure(list(), class = c("collector_double", 
"collector"))), default = structure(list(), class = c("collector_guess", 
"collector")), skip = 1), class = "col_spec"))

and the code I came up so far. ONLY the cols do not cycle through the title. The rest of the code works perfectly. I am still a beginner, so thank you for your time and patience.

acq <- select(X1, La:Nd)


gg <- for (ii in acq){
  cols <- names(X1)[2:5]
  m <-mean(ii)
  sds <- sd(ii)
  m1 <- mean(ii)+1
  m2 <-mean(ii)-1

  g <- ggplot(X1,aes_string(x="Point",y="ii")) +
    ggtitle(paste(cols,"\n",m,"\n",sds,"\n")) +
    theme(plot.title = element_text(hjust = 0.5)) +
    geom_line() + geom_hline(aes(yintercept=mean(ii))) + ylab('') + xlab('')+
    geom_hline(aes(yintercept=m1),linetype=2)  + 
    geom_text(x=8,y=m1,label="10%",vjust=-1) +
    geom_hline(aes(yintercept=m2),linetype=2) + 


My data:

~Point, ~La, ~Ce, ~Pr, ~Nd,
     5, 535,  45,  56,  23,
     6, 565,  46,  54,  28,
     7, 532,  58,  43,  18,
     8, 587,  43,  50,  26


They way you setup the for-loop is not really recommended. It's better to loop through column names then extract that column from the acq data frame accordingly


acq <- select(X1, La:Nd)

## loop ##
for (ii in seq_along(colnames(acq))) {

  current_col <- colnames(acq)[ii]
  print(paste0('Plot col: ', current_col))

  # calculate mean and stdev
  m <- mean(acq[[current_col]])
  sds <- sd(acq[[current_col]])
  m1 <- m + 1
  m2 <- m - 1

  ## plot ##
  g <- ggplot(X1, aes_string(x = "Point", y = current_col)) +
    ggtitle(paste("column = ", current_col, "\n", 
                  "mean = ", formatC(m, digits = 3), "\n",
                  "sd = ", formatC(sds, digits = 3), "\n")) +
    theme_classic(base_size = 12) +
    theme(plot.title = element_text(hjust = 0.5)) +
    geom_line() + 
    geom_hline(aes(yintercept = m)) + 
    ylab("") + xlab("") +
    geom_hline(aes(yintercept = m1), linetype = 2) +
    geom_text(x = 8, y = m1, label = "10%", vjust = -1, check_overlap = TRUE) +
    geom_hline(aes(yintercept = m2), linetype = 2) +
    geom_text(x = 8, y = m2, label = "10%", vjust = 2, check_overlap = TRUE)


Example output:

#> [1] "Plot col: La"

#> [1] "Plot col: Ce"

Another (preferable) way is to use the new tidy evaluation approach (more explanation here)

generate_plot2 <- function(df, .x.variable, .y.variable) {

  x.variable <- rlang::sym(.x.variable)
  y.variable <- rlang::sym(.y.variable)

  sum_df <- df %>% 
    summarise_at(vars(!!y.variable), funs(mean, sd)) %>% 
    mutate(m1 = mean + 1,
           m2 = mean - 1)

  g <- ggplot(df, aes(x = !! x.variable, y = !! y.variable)) +
    ggtitle(paste("column = ", .y.variable, "\n", 
                  "mean = ", formatC(sum_df$mean, digits = 3), "\n",
                  "sd = ", formatC(sum_df$sd, digits = 3), "\n")) +
    geom_line() + 
    geom_hline(aes(yintercept = sum_df$mean)) + 
    ylab("") + xlab("") +
    geom_hline(aes(yintercept = sum_df$m1), linetype = 2) +
    geom_text(x = 8, y = sum_df$m1, label = "10%", vjust = -1, check_overlap = TRUE) +
    geom_hline(aes(yintercept = sum_df$m2), linetype = 2) +
    geom_text(x = 8, y = sum_df$m2, label = "10%", vjust = 2, check_overlap = TRUE) +
    theme_classic(base_size = 12) +
    theme(plot.title = element_text(hjust = 0.5))


plot_list <- names(X1)[-1] %>% 
  map(~ generate_plot2(X1, "Point", .x))

#>     mean       sd     m1     m2
#> 1 554.75 26.15817 555.75 553.75
#>   mean      sd m1 m2
#> 1   48 6.78233 49 47
#>    mean       sd    m1    m2
#> 1 50.75 5.737305 51.75 49.75
#>    mean       sd    m1    m2
#> 1 23.75 4.349329 24.75 22.75



# bonus: combine all plots
plot_grid(plotlist = plot_list, 
          labels = 'AUTO',
          nrow = 2,
          align = 'hv',
          axis = 'tblr')

Created on 2019-03-16 by the reprex package (v0.2.1.9000)


I am trying to get my sample size to appear in my plot title using inline code so that I can avoid the error of forgetting to update (N = 128) each time I use this code chunk. Currently R does not recognize the inline code "(N = r nrow(df))" that I am trying to use in the last line of my code.

library(Rmisc) # for summarySE function

# create descriptive stats for bar plot
    df <- subset(mtcars, select = c(mpg, cyl))
    dfc <- summarySE(df, measurevar = "mpg", groupvars = c("cyl"))

# bar plot
ggplot(dfc, aes(x=cyl, y=mpg)) + # insert variables
      geom_bar(aes(fill=cyl), # essential for bar coloring
               stat="identity", colour="black", size=0) + 
      geom_errorbar(aes(ymin= mpg - se, ymax= mpg + se), 
                    size=.4, width=.1, position=position_dodge(.9)) + 
      ggtitle("(N = `r nrow(df)`)")  ### THIS IS THE LINE I WANT TO WORK ###

Thank you in advance for any help.


You can do this via the paste functions as shown:

n_value <- paste("( N = ", nrow(dat), ")")


I've tried about every iteration I can find on Stack Exchange of for loops and lapply loops to create ggplots and this code has worked well for me. My only problem is that I can't assign unique titles and labels. From what I can tell in the function i takes the values of my response variable so I can't index the title I want as the ith entry in a character string of titles.

The example I've supplied creates plots with the correct values but the 2nd and 3rd plots in the plot lists don't have the correct titles or labels.

Mock dataset:


df=data.frame(nms,measr1,qual1,measr2,qual2,measr3,qual3,stringsAsFactors = FALSE)

identify columns in dataset that contain response variable


Create list of plots that show all samples for each measurement

plotlist=lapply(df[,measrsindex], function(i) ggplot(df,aes_string(x="nms",y=i))+

Create list of plots that show all measurements for each sample

plotlist2=lapply(df[,measrsindex],function(i)ggplot(df,aes_string(x=measrsindex, y=i))+

The problem is that I cant create unique title for each plot. (All plots in the example have the title "measr1" or "SampleA)

Additionally I cant apply unique labels (from qual columns) for each bar. (ex. the letter for qual 2 should appear on top of the column for measr2 for each sample)

Additionally in the second plot list the x-values aren't "measr1","measr2","measr3" they're the index values for those columns which isn't ideal.

I'm relatively new to R and have never posted on Stack Overflow before so any feedback about my problem or posting questions is welcomed.

I've found lots of questions and answers about this sort of topic but none that have a data structure or desired plot quite like mine. I apologize if this is a redundant question but I have tried to find the solution in previous answers and have been unable.

This is where I got the original code to make my loops, however this example doesn't include titles or labels: Looping over ggplot2 with columns


You could loop over the names of the columns instead of the column itself and then use some non-standard evaluation to get column values from the names. Also, I have included label in aes.


plotlist3 <- purrr::map(names(df)[measrsindex], 
             ~ggplot(df, aes(nms, !!sym(.x), label = qual1)) + 
               geom_col() + ggtitle(.x) + geom_text(vjust = -1))



The same can be achieved with lapply as well

plotlist4 <- lapply(names(df)[measrsindex], function(x)
  ggplot(df, aes(nms, !!sym(x), label = qual1)) + 
         geom_col() + ggtitle(x) + geom_text(vjust = -1))