Ggplot2: Как выровнять по левому краю текст из многострочных меток?

Я использую facet_grid() для отображения некоторых данных, и у меня есть метки фасетов, которые охватывают несколько строк текста (они содержат символ "\n" ).

require(ggplot2)

#Generate example data
set.seed(3)
df = data.frame(facet_label_text = rep(c("Label A",
                                         "Label B\nvery long label",
                                         "Label C\nshort",
                                         "Label D"),
                                       each = 5),
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

#Plot test data
ggplot(df, aes(x = time, y = value)) +
    geom_line() +
    facet_grid(facet_label_text ~ .) +
    theme(strip.text.y = element_text(angle = 0, hjust = 0))

Итак, используя аргумент hjust = 0, я могу выровнять по левому краю текст метки фасетки как единицы.

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

То, что я хотел бы сделать, - выровнять по левому краю каждую отдельную строку текста. Таким образом, "Label B" и "очень длинная метка" выравниваются по левой стороне, а не центрируются относительно друг друга (например, для "Label C" и "short" ). Возможно ли это в ggplot2? Заранее благодарим за помощь.

Ответ 1

Это довольно просто, используя функцию grid grid.gedit для редактирования полос.

library(ggplot2)  # v2.1.0
library(grid)

# Your data
set.seed(3)
df = data.frame(facet_label_text = rep(c("Label A",
                                         "Label B\nvery long label",
                                         "Label C\nshort",
                                         "Label D"), 
                                       each = 5),
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

# Your plot
p = ggplot(df, aes(x = time, y = value)) +
    geom_line() +
    facet_grid(facet_label_text ~ .) +
    theme(strip.text.y = element_text(angle = 0, hjust = 0))
p

# Get a list of grobs in the plot
grid.ls(grid.force())  

# It looks like we need the GRID.text grobs.
# But some care is needed:
# There are GRID.text grobs that are children of the strips;
# but also there are GRID.text grobs that are children of the axes.
# Therefore, a gPath should be set up 
# to get to the GRID.text grobs in the strips

# The edit
grid.gedit(gPath("GRID.stripGrob", "GRID.text"),  
         just = "left", x = unit(0, "npc"))

Или еще несколько строк кода для работы с объектом grob (вместо редактирования на экране, как указано выше):

# Get the ggplot grob
gp = ggplotGrob(p)
grid.ls(grid.force(gp))

# Edit the grob
gp = editGrob(grid.force(gp), gPath("GRID.stripGrob", "GRID.text"), grep = TRUE, global = TRUE,
          just = "left", x = unit(0, "npc"))

# Draw it
grid.newpage()
grid.draw(gp)

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

Ответ 2

Пока кто-то не встретится с реальным решением, вот хак: добавьте пробел в ярлыки, чтобы получить оправдание, которое вы хотите.

require(ggplot2)

#Generate example data
set.seed(3)
df = data.frame(facet_label_text = rep(c("Label A",
                                         "Label B           \nvery long label",
                                         "Label C\nshort     ",
                                         "Label D"),
                                       each = 5),
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

#Plot test data
ggplot(df, aes(x = time, y = value)) +
  geom_line() +
  facet_grid(facet_label_text ~ .) +
  theme(strip.text.y = element_text(angle = 0, hjust = 0))

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

Ответ 3

Там может быть более чистый способ сделать это, но я не нашел способ сделать это в ggplot2. Функция padwrap может быть более обобщенной, поскольку она в основном выполняет только то, что вы просили. Чтобы получить право обоснования, мне пришлось использовать шрифт с монослоем.

# Wrap text with embedded newlines: space padded and lef justified.
# There may be a cleaner way to do this but this works on the one
# example.  If using for ggplot2 plots, make the font `family`
# a monospaced font (e.g. 'Courier')
padwrap <- function(x) {
    # Operates on one string
    padwrap_str <- function(s) {
        sres    <- strsplit(s, "\n")
        max_len <- max(nchar(sres[[1]]))
        paste( sprintf(paste0('%-', max_len, 's'), sres[[1]]), collapse = "\n" )
    }
    # Applys 'padwrap' to a vector of strings
    unlist(lapply(x, padwrap_str))
}

require(ggplot2)

facet_label_text = rep(c("Label A",
                         "Label B\nvery long label",
                         "Label C\nshort",
                         "Label D"), 5)
new_facet_label_text <- padwrap(facet_label_text)

#Generate example data
set.seed(3)
df = data.frame(facet_label_text = new_facet_label_text,
                time = rep(c(0, 4, 8, 12, 16), times = 4),
                value = runif(20, min=0, max=100))

#Plot test data
ggplot(df, aes(x = time, y = value)) +
    geom_line() +
    facet_grid(facet_label_text ~ .) +
    theme(strip.text.y = element_text(angle = 0, hjust = 0, family = 'Courier'))

Текст полосы выравнивается на изображении ниже

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