使用表达式

时间:2019-03-16 19:46:09

标签: r ggplot2

我正在制作带有长轴标签的条形图,需要包装它们并使其右对齐。唯一的麻烦是我需要添加一个表达式以具有上标。

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))

产生预期的结果:

enter image description here

带有正确包装的文本,所有标签均完全对齐。

但是,现在我尝试使用以下代码添加上标,并且轴文本对齐方式发生更改:

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")))

enter image description here

(请注意,我不能使用unicode,因为它与我需要使用的字体不兼容,因此建议其他人使用相同的问题。)

即使轴标签之一包含表达式,如何使轴文本正确对齐?

1 个答案:

答案 0 :(得分:1)

这很奇怪,但是如果向量(例如标签的字符向量)包含由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"))

illustration 1

illustration 2

解决方法

如果我们可以强迫每个标签自己考虑,而在标签向量中没有其他标签的影响,则非表达标签可以像普通字符串一样对齐。一种方法是将ggplot对象转换为grob,然后用多个文本grob替换用于y轴标签的单个textGrob,每个文本grob替换一个。

预备工作:

# 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"))

黑客入侵:

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)

result