ggplot выровнять текст по правой оси при использовании выражения

#r #ggplot2

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

что приводит к желаемому результату:

введите описание изображения здесь

с правильно обернутым текстом, все метки которого полностью выровнены по правому краю.

Однако теперь я пытаюсь добавить верхний индекс, используя приведенный ниже код, и выравнивание текста по оси изменяется:

 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, как рекомендуется другим с тем же вопросом, потому что он не работает со шрифтом, который я должен использовать).

Как я могу выровнять текст по оси по правому краю, даже если одна из меток оси содержит выражение?

Ответ №1:

Это странная вещь, но если вектор (например, символьный вектор меток) включает объект, созданный expression() , весь вектор, по-видимому, обрабатывается как выражение:

 # create a simple vector with one expression amp; 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, amp; left-aligned within its text block
ggplot()  
  annotate("point", x = 1:2, y = 1)  
  annotate("text", x = 1, y = 1, 
           label = expression("long stringnwith single line break")) 
  annotate("text", x = 2, y = 1, 
           label = expression("long stringnwith multiple linenbreaks 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 stringnwith single line break"))
plot(0, xlab=expression("long stringnwith multiple linenbreaks here"))
  

иллюстрация 1

иллюстрация 2

Обходной путь

Если бы мы могли заставить рассматривать каждую метку отдельно, без влияния других меток в векторе меток, метки, не содержащие выражения, можно было бы выровнять как обычные символьные строки. Один из способов сделать это — преобразовать объект ggplot в grob и заменить одну текстовую строку для меток по оси y несколькими текстовыми строками, по одной для каждой метки.

Подготовительная работа:

 # 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 amp; 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)
  

Результат

Комментарии:

1. Большое спасибо за невероятно подробный и полезный пост! это отлично решило мою проблему!

2. Можно ли использовать переменные внутри выражения?