Paul
Paul

Reputation: 2977

How to put y axis title in top left corner of graph using grid's functions?

I want to write the y-axis title horizontally on top of the y-axis line while keeping settings defined for the y-axis in the theme() function. The objective is to avoid wasting space while keeping a nice looking design (e.g. with the title left-aligned with y-axis-ticks-labels).

I know it is possible to use other "hacks" (see this using subtitle), but here I look for a solution using the grid approach. I am also aware that it is possible to move around the title by setting margins but it requires to uptade it all the time and I was not able to get a satisfying result with it.

Then, using grid functions as demonstrated in this SO post seems the way to go.

Here is what I would like to get:

expected output

It is possible to start with theme(axis.title.y = element_text(angle = 0, vjust = 1)) but the y-axis title is not on top of the line.

what I have so far

This code removes the y-axis title while keeping the theme() parameters defined for the grob:

library(ggplot2)
library(grid)

p <- ggplot(data = iris, aes(x = Sepal.Length, y = Petal.Length)) +
  theme(axis.title.y = element_text(angle = 0, vjust = 1),
        axis.title.x = element_blank())
# convert from ggplot to grob object
gp <- ggplotGrob(p)
# locate the grob that corresponds to y-axis title and save its parameters
y.label.grob <- gp$grobs[[which(gp$layout$name == "ylab-l")]]$children
# remove y-axis labels from the plot, & shrink the space occupied by them
gp$grobs[[which(gp$layout$name == "ylab-l")]] <- zeroGrob()
gp$widths[gp$layout$l[which(gp$layout$name == "ylab-l")]] <- unit(0, "cm")

But now I am stuck as I do not know how to use functions from the grid package to move the title where I would like to (i.e. on the top left corner).

Upvotes: 2

Views: 1339

Answers (2)

teunbrand
teunbrand

Reputation: 38053

You can exceed vjust beyond 1 and adapt the margins a bit. It's hard to get the alignment perfectly though.

library(ggplot2)
library(grid)

ggplot(data = iris, aes(x = Sepal.Length, y = Petal.Length)) +
  theme(
    plot.margin = margin(t = 30),
    axis.title.y = element_text(
      angle = 0, vjust = 1.1, 
      margin = margin(r = -50, t = 5.5, b = 5.5, l = 5.5)),
    axis.title.x = element_blank()
  )

If you know the title in advance you can use it's string width.

ggplot(data = iris, aes(x = Sepal.Length, y = Petal.Length)) +
  theme(
    plot.margin = margin(t = 30),
    axis.title.y = element_text(
      angle = 0, vjust = 1.07,
      margin = unit.c(
        unit(c(2.75), "pt"),
        unit(-1, "strwidth", data = "Petal.Length"),
        unit(c(2.75, 2.75), "pt")
      )
    ),
    axis.title.x = element_blank()
  )

Created on 2021-09-09 by the reprex package (v2.0.1)

Upvotes: 2

user63230
user63230

Reputation: 4708

You could use cowplot as another approach:

library(ggplot2)
library(cowplot)
title <- ggdraw() + 
  draw_label(
    "Petal.Length",
    #fontface = 'bold',
    x = 0,
    hjust = 0
  ) +
  theme(
    # add margin on the left of the drawing canvas,
    # so title is aligned with left edge of first plot
    plot.margin = margin(0, 0, 0, 7)
  )

p <- ggplot(data = iris, aes(x = Sepal.Length, y = Petal.Length)) +
  labs(y = "")
plot_row <- plot_grid(p)
plot_grid(
  title, plot_row,
  ncol = 1,
  rel_heights = c(0.1, 1)
)

enter image description here

Upvotes: 0

Related Questions