Question adapted from this question and solution: Highlighting individual axis labels in bold using ggplot2
I would like to selectively justify the horizontal axes labels depending on meeting a criteria. So borrowing from the above question and answer I've set up an example:
require(ggplot2)
require(dplyr)
set.seed(36)
xx<-data.frame(YEAR=rep(c("X", "Y"), each=20),
CLONE=rep(c("A", "B", "C", "D", "E"), each=4, 2),
TREAT=rep(c("T1", "T2", "T3", "C"), 10),
VALUE=sample(c(1:10), 40, replace=T))
# Simple plot with factors on y axis
ggplot(xx, aes(x = VALUE, y=CLONE, fill=YEAR)) +
geom_bar(stat="identity", position="dodge") +
facet_wrap(~TREAT)
Ok so I adopted function from the above question + answer to generate a vector of justifications:
# Modify to control justification
colorado2 <- function(src, boulder) {
if (!is.factor(src)) src <- factor(src)
src_levels <- levels(src)
brave <- boulder %in% src_levels
if (all(brave)) {
b_pos <- purrr::map_int(boulder, ~which(.==src_levels))
b_vec <- rep(0.2, length(src_levels))
b_vec[b_pos] <- 0.9
b_vec
} else {
stop("All elements of 'boulder' must be in src")
}
}
# Redraw the plot with modifcation
ggplot(xx, aes(x = VALUE, y=CLONE, fill=YEAR)) +
geom_bar(stat="identity", position="dodge") +
facet_wrap(~TREAT) +
theme(axis.text.y=element_text(hjust=colorado2(xx$CLONE, c("A", "B", "E"))))
I'm getting this unfortunate mess:
The labels are justified in the direction I want - but taking up far too much of the plot for reasons I cannot figure out. How do I fix this ?
I did some digging. The problem is with how ggplot sets the grob width of the y axis grob. It assumes that hjust
is the same across all labels. We can fix this with some hacking of the grob tree. The following code was tested with the development version of ggplot2 and may not work as written with the currently released version.
First, a simple reproducible example:
p <- ggplot(mpg, aes(manufacturer, hwy)) + geom_boxplot() + coord_flip() +
theme(axis.text.y = element_text(hjust = c(rep(1, 10), rep(0, 5))))
p # doesn't work
The problem is that the grob width of the axis grob gets set to the entire plot area. But we can manually go in and fix the width. Unfortunately we have to fix it in multiple locations:
# get a vector of the y labels as strings
ylabels <- as.character(unique(mpg$manufacturer))
library(grid)
g <- ggplotGrob(p)
# we need to fix the grob widths at various locations in the grob tree
g$grobs[[3]]$children[[2]]$widths[1] <- max(stringWidth(ylabels))
g$grobs[[3]]$width <- sum(grobWidth(g$grobs[[3]]$children[[1]]), grobWidth(g$grobs[[3]]$children[[2]]))
g$widths[3] <- g$grobs[[3]]$width
# draw the plot
grid.newpage()
grid.draw(g)
The axis-drawing code of ggplot2 could probably be modified to calculate width like I did here from the outset, and then the problem would disappear.
The method you employ is hacking the underlying grid graphics engine and results may not always be obvious. See here for an answer that goes into the grob tree and fixes things.
However, for the problem as stated in the question, there is a simple solution that doesn't require any hacking. Just make labels that have some trailing spaces. I did this manually here, but you could also write a function that does this.
ggplot(xx, aes(x = VALUE, y=CLONE, fill=YEAR)) +
geom_bar(stat="identity", position="dodge") +
scale_y_discrete(breaks = c("A", "B", "C", "D", "E"),
labels = c("A ", "B ", "C", "D", "E ")) +
facet_wrap(~TREAT)
来源:https://stackoverflow.com/questions/48286116/justify-individual-axis-labels-in-bold-using-ggplot2