I'm drawing a plot with ggplot2 in R and I'd like the title for the y axis to appear in the top left corner of the plot. Consider the following code for the default behaviour:
require(ggplot2)
xy = data.frame(x=1:10, y=10:1)
ggplot(data = xy) +
geom_point(aes(x = x, y = y)) +
ylab("very long label")
This produces the following graph:
I would like to move and rotate the text "very long label". I can do this somewhat using the theme()
function:
ggplot(data = xy) +
geom_point(aes(x = x, y = y)) +
ylab("very long label") +
theme(axis.title.y = element_text(angle = 0, vjust = 1.1, hjust = 10))
Which gives me this:
You can see where I'm going with this, but the margins are incorrect -- the left margin is too large because the space is reserved for a rotated label and the top margin is too small for the text.
How can I tell ggplot that I want the y axis title at that position without rotation and have it reserve the appropriate space for it?
Put it in the main plot title:
ggplot(data = xy) +
geom_point(aes(x = x, y = y)) +
ggtitle("very long label") +
theme(plot.title = element_text(hjust = 0))
You can shove it slightly more to the left if you like using negative hjust
values, although if you go too far the label will be clipped. In that case you might try playing with the plot.margin
:
ggplot(data = xy) +
geom_point(aes(x = x, y = y)) +
ggtitle("very long label") +
theme(plot.title = element_text(hjust = -0.3),
plot.margin = rep(grid::unit(0.75,"in"),4))
So obviously this makes it difficult to add an actual title to the graph. You can always annotate manually using something like:
grid.text("Actual Title",y = unit(0.95,"npc"))
Or, vice-versa, use grid.text
for the y label.
I just experienced the same problem and found a solution that can be useful to the community.
The idea is to create a custom theme that substitutes the axis label by a subtitle. The plot used as an example will be the following:
p <- ggplot2::ggplot(iris, ggplot2::aes(x = Sepal.Width, y = Sepal.Length,
color = Species)) +
ggplot2::geom_point()
Create a class mytheme
that modifies the theme used by ggplot
. Here, in the minimalist solution, no constraint is made over style (I will present limitations later).
custom_theme <- function(...){
out <- ggplot2::theme_minimal() + ggplot2::theme(...)
class(out) <- c("mytheme",class(ggplot2::theme_minimal()))
return(out)
}
Associate to this class a ggplot_add
method that overrides the default method. This custom method takes the existing y
label and put it into subtitle
. The substitution is made in the first two lines of the function. The third line of the function uses a update_theme
function defined later on:
ggplot_add.mytheme <- function(object, plot, object_name) {
plot$labels$subtitle <- plot$labels$y
plot$labels$y <- NULL
plot$theme <- update_theme(plot$theme, object)
plot
}
update_theme
function was part of ggplot2
package up to recently. The function disappeared recently (see this commit). The hack I implemented is to define the function like this:
update_theme <- function(oldtheme, newtheme) {
# If the newtheme is a complete one, don't bother searching
# the default theme -- just replace everything with newtheme
if (isTRUE(attr(newtheme, "complete", exact = TRUE)))
return(newtheme)
# These are elements in newtheme that aren't already set in oldtheme.
# They will be pulled from the default theme.
newitems <- !names(newtheme) %in% names(oldtheme)
newitem_names <- names(newtheme)[newitems]
oldtheme[newitem_names] <- theme_get()[newitem_names]
# Update the theme elements with the things from newtheme
# Turn the 'theme' list into a proper theme object first, and preserve
# the 'complete' attribute. It's possible that oldtheme is an empty
# list, and in that case, set complete to FALSE.
old.validate <- isTRUE(attr(oldtheme, "validate"))
new.validate <- isTRUE(attr(newtheme, "validate"))
oldtheme <- do.call(theme, c(oldtheme,
complete = isTRUE(attr(oldtheme, "complete")),
validate = old.validate & new.validate))
oldtheme + newtheme
}
The theme
can be used then to create the expected output:
p + ggplot2::labs(y = "A very very long label") + custom_theme()
If you want to change later on some style elements for the axis title (e.g. text size), that will be problematic for the y label since it is a subtitle element. For instance, the user that runs
p + custom_theme(axis.title = ggplot2::element_text(size = 24))
would expect the y label to also be of size 24. I don't think this a real problem because you can change the custom_theme
with more constraints
labs
after calling the theme
This solution will be problematic if a user sets the theme
before changing the labs
, e.g.
p + ggplot2::labs(y = "A very very long label") + custom_theme() + ggplot2::labs(y = "override")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With