我正在制作一个条形图与长轴标签,我需要包装和正确对齐。唯一复杂的是,我需要添加一个表达式才有上标。
library(ggplot2)
library(scales)
df <- data.frame("levs" = c("a long label i want to wrap",
"another also long label"),
"vals" = c(1,2))
p <- ggplot(df, aes(x = levs, y = vals)) +
geom_bar(stat = "identity") +
coord_flip() +
scale_x_discrete(labels = wrap_format(20))
它产生预期的结果:
与正确包装的文本与所有标签完全对齐。
但是,现在我尝试使用下面的代码添加上标,并且轴文本对齐会发生变化:
p <- ggplot(df, aes(x = levs, y = vals)) +
geom_bar(stat = "identity") +
coord_flip() +
scale_x_discrete(labels = c(expression("exponent"^1),
wrap_format(20)("another also long label")))
(注意,我不能使用unicode,因为它不适用于我需要使用的字体,这是向其他有相同问题的人推荐的)。
即使其中一个轴标签包含表达式,我如何才能使轴文本对齐?
发布于 2019-03-17 07:28:18
这是一件奇怪的事情,但如果向量(例如标签的字符向量)包含由expression()
创建的对象,则整个向量似乎被视为表达式:
# create a simple vector with one expression & one character string
label.vector <- c(expression("exponent"^1),
wrap_format(20)("another also long label"))
> sapply(label.vector, class) # the items have different classes when considered separately
[1] "call" "character"
> class(label.vector) # but together, it's considered an expression
[1] "expression"
..。表达式总是左对齐的。这并不是ggplot特有的现象;我们还可以在基本绘图函数中观察到这一点:
# even with default hjust = 0.5 / vjust = 0.5 (i.e. central alignment), an expression is
# anchored based on the midpoint of its last line, & left-aligned within its text block
ggplot() +
annotate("point", x = 1:2, y = 1) +
annotate("text", x = 1, y = 1,
label = expression("long string\nwith single line break"))+
annotate("text", x = 2, y = 1,
label = expression("long string\nwith multiple line\nbreaks here")) +
xlim(c(0.5, 2.5))
# same phenomenon observed in base plot
par(mfrow = c(1, 3))
plot(0, xlab=expression("short string"))
plot(0, xlab=expression("long string\nwith single line break"))
plot(0, xlab=expression("long string\nwith multiple line\nbreaks here"))
解决办法
如果我们可以强制每个标签单独考虑,而不受标签向量中其他标签的影响,那么非表达式标签就可以像普通字符串一样对齐。实现这一目的的一种方法是将ggplot对象转换为grob,并将y轴标签的单个textGrob替换为多个文本grobs,每个标签一个。
筹备工作:
# generate plot (leave the labels as default)
p <- ggplot(df, aes(x = levs, y = vals)) +
geom_bar(stat = "identity") +
coord_flip()
p
# define a list (don't use `c(...)` here) of desired y-axis labels, starting with the
# bottom-most label in your plot & work up from there
desired.labels <- list(expression("exponent"^1),
wrap_format(20)("another also long label"))
Grob黑客:
library(grid)
library(magrittr)
# convert to grob object
gp <- ggplotGrob(p)
# locate label grob in the left side y-axis
old.label <- gp$grobs[[grep("axis-l", gp$layout$name)]]$children[["axis"]]$grobs[[1]]$children[[1]]
# define each label as its own text grob, replacing the values with those from
# our list of desired y-axis labels
new.label <- lapply(seq_along(old.label$label),
function(i) textGrob(label = desired.labels[[i]],
x = old.label$x[i], y = old.label$y[i],
just = old.label$just, hjust = old.label$hjust,
vjust = old.label$vjust, rot = old.label$rot,
check.overlap = old.label$check.overlap,
gp = old.label$gp))
# remove the old label
gp$grobs[[grep("axis-l", gp$layout$name)]]$children[["axis"]]$grobs[[1]] %<>%
removeGrob(.$children[[1]]$name)
# add new labels
for(i in seq_along(new.label)) {
gp$grobs[[grep("axis-l", gp$layout$name)]]$children[["axis"]]$grobs[[1]] %<>%
addGrob(new.label[[i]])
}
# check result
grid.draw(gp)
https://stackoverflow.com/questions/55200820
复制