Nicholas Hassan
Nicholas Hassan

Reputation: 969

ggplot - centre plot title with total width of image

The advice typically given to center a plot (or any element) is to use hjust along the lines of:

ggplot() +
  ggtitle("Use theme(plot.title = element_text(hjust = 0.5)) to center") +
  theme(plot.title = element_text(hjust = 0.5))

I've noticed however that in the case of a plot title, this centres it over the graphed variables, but not the entire width of the image. If there's a legend or the y-axis text is too long, they can push the plot area and title around.

The following three charts were exported with the same width and height. The first is the original, the second has a legend, and the third has larger y-axis text. The titles are the same font-size and have been centred using hjust = 0.5. While the charts take up the same amount of space, when stacked on top of each other, the titles do not align. enter image description here enter image description here enter image description here

The code to repoduce:

data(mtcars)
mtcars$cyl = factor(mtcars$cyl)
mtcars$carb = factor(mtcars$carb)
require(dplyr)
averages_carb <- mtcars %>%
  group_by(carb) %>%
  summarise(mpg = mean(mpg))
averages_carb_cyl <- mtcars %>%
  group_by(carb, cyl) %>%
  summarise(mpg = mean(mpg))

png("No legend - regular y-text.png",width = 1000,height = 693, res = 120)
(graph <- ggplot(averages_carb, aes(x = carb, y = mpg)) +
  geom_bar(stat = "identity") +
  theme_minimal() + 
  theme(plot.title = element_text(hjust = 0.5, size = 20),
        axis.text.y = element_text(size = 10)) +
  ggtitle("No legend - regular y-text") +
  scale_y_continuous(breaks = c(1:25), labels = c("Low MPG", rep("", 23), "High MPG")))
dev.off()

png("Legend - regular y-text.png",width = 1000,height = 693, res = 120)
(graph_legend <- ggplot(averages_carb_cyl, aes(x= carb, y = mpg, fill = cyl)) +
  geom_bar(stat = "identity", position = "dodge") +
  theme_minimal() + 
  theme(plot.title = element_text(hjust = 0.5, size = 20),
        axis.text.y = element_text(size = 10)) +
  ggtitle("Legend - regular y-text") +
  scale_y_continuous(breaks = c(1:28), labels = c("Low MPG", rep("", 26), "High MPG")))
dev.off()

png("No legend - large y-text.png",width = 1000,height = 693, res = 120)
(graph_large_text <- ggplot(averages_carb, aes(x = carb, y = mpg)) +
  geom_bar(stat = "identity") +
  theme_minimal() + 
  theme(plot.title = element_text(hjust = 0.5, size = 20),
        axis.text.y = element_text(size = 20)) +
  ggtitle("No legend - large y-text") +
  scale_y_continuous(breaks = c(1:25), labels = c("Low MPG", rep("", 23), "High MPG")))
dev.off()

Upvotes: 1

Views: 1248

Answers (1)

Mikael Poul Johannesson
Mikael Poul Johannesson

Reputation: 1349

To my knowledge, this is not possible without manually manipulating grobs, which will be a lot of tinkering. A simple workaround is to make a title "figure" and print it above the actual figure. The gridExtra package makes this easy (but see also patchwork). For instance,

## Start matter ------------------------------------------------------

library(dplyr)
library(ggplot2)
library(gridExtra)

## Data --------------------------------------------------------------

data(mtcars)

mtcars <-
  mtcars %>%
  mutate_at(vars(cyl, carb), factor)

averages_carb <-
  mtcars %>%
  group_by(carb) %>%
  summarise(mpg = mean(mpg)) %>%
  ungroup()

averages_carb_cyl <-
  mtcars %>%
  group_by(carb, cyl) %>%
  summarise(mpg = mean(mpg)) %>%
  ungroup()

## Figure 1 ----------------------------------------------------------

fig_1_title <-
  ggplot() +
  ggtitle("No legend - regular y-text") +
  geom_point() +
  theme_void() +
  theme(plot.title = element_text(size = 20, hjust = .5))

fig_1_main <-
  averages_carb %>%
  ggplot(aes(x = carb, y = mpg)) +
  geom_bar(stat = "identity") +
  scale_y_continuous(
    breaks = 1:25,
    labels = c("Low MPG", rep("", 23), "High MPG")) +
  theme_minimal()

png(
  "No legend - regular y-text.png",
  width = 1000, height = 693, res = 120
)

grid.arrange(
  fig_1_title, fig_1_main,
  ncol = 1, heights = c(1/20, 19/20)
)

dev.off()

## Figure 2 ---------------------------------------------------------

fig_2_title <-
  ggplot() +
  geom_point() +
  ggtitle("Legend - regular y-text") +
  theme_void() +
  theme(plot.title = element_text(size = 20, hjust = .5))

fig_2_main <-
  averages_carb_cyl %>%
  ggplot(aes(x = carb, y = mpg, fill = cyl)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_y_continuous(
    breaks = 1:28,
    labels = c("Low MPG", rep("", 26), "High MPG")) +
  theme_minimal()

png(
  "Legend - regular y-text.png",
  width = 1000, height = 693, res = 120
)

grid.arrange(
  fig_2_title, fig_2_main,
  ncol = 1, heights = c(1/20, 19/20)
)

dev.off()

## END ---------------------------------------------------------------

enter image description here

enter image description here

(Yes, their centers are aligned.)

Upvotes: 1

Related Questions