Pytanie ggplot2: Jak usprawnić tekst z wielowierszowych etykiet aspektów?


Używam facet_grid () do wyświetlania niektórych danych i mam etykiety aspektów, które obejmują wiele wierszy tekstu (zawierają znak "\ 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))

Używając argumentu hjust = 0, mogę wyrównać do lewej strony tekstu etykiety jako jednostki.

enter image description here

To, co chciałbym zrobić, to wyrównanie do lewej każdego wiersza tekstu. Tak więc "Etykieta B" i "bardzo długa etykieta" są wyrównane wzdłuż lewej strony, zamiast wyśrodkowane względem siebie (tak samo dla "Etykieta C" i "krótka"). Czy to możliwe w ggplot2? Z góry dziękuje za twoją pomoc.


14
2018-01-12 17:03


pochodzenie




Odpowiedzi:


Jest to dość proste przy użyciu siatki grid.gedit funkcja do edycji pasków.

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

Lub kilka linii kodu do pracy z obiektem grobowym (zamiast edycji na ekranie jak wyżej):

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

enter image description here


9
2018-01-13 04:52



Dzięki za wspaniałą odpowiedź. Wygląda na to, że istnieje ogromna moc wiedzenia, jak modyfikować obiekty grid i grob leżące u podstaw ggplota. Czy możesz polecić dobry materiał dla początkujących lub przewodnik po edycji siatki / groba? Dzięki jeszcze raz. - Brain_Food
Po zabawie z tym trochę, myślę, że możesz dostać grob bez konieczności wyświetlania go za pomocą tego drobne poprawki: grab = grid.grabExpr(grid.draw(gp)) - Brain_Food
@Brain_Food AFAIK, nie ma nic, co łączyłoby edycję siatki wykresów ggplot2 w kompleksowy sposób. Dla grid, sprawdź referencje na stronie informacyjnej r-grid oznacz na SO. Warsztaty UseR 2015 Paula Murrell'a podaj szczegóły dotyczące edycji ggplots za pomocą funkcji siatki. W przeciwnym razie odpowiedzi SO. Również, gtable funkcje są przydatne. Baptiste zebrał się razem kilka uwag na gtable - Sandy Muspratt
Takie podejście rzeczywiście działa, ale czy istnieje sposób, aby ponownie uchwycić wynik gTable obiekt jako ggplot obiekt? Po prostu szukam odwrotności ggplot2::ggplotGrob ... - balin
@balin Przepraszam, ggplotGrob jest ulicą jednokierunkową. - Sandy Muspratt


Dopóki ktoś nie przyjdzie z prawdziwym rozwiązaniem, oto hack: dodaj miejsce na etykietach, aby uzyskać uzasadnienie, którego potrzebujesz.

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

enter image description here


7
2018-01-12 17:23





Może być na to czystszy sposób, ale nie znalazłem sposobu, aby to zrobić w ggplot2. The padwrap funkcja może być bardziej uogólniona, ponieważ w zasadzie robi to, o co prosiłeś. Aby uzyskać uzasadnienie, musiałem użyć czcionki jednoprzestrzennej.

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

Tekst paska jest wyrównany do lewej na poniższym obrazku

enter image description here


7
2018-01-12 18:11



Sprytny pomysł. Może mógłbyś uogólnić go na czcionki nie-mono-rozmieszczone. Posługiwać się strwidth aby uzyskać względne rozmiary każdego znaku, a następnie użyć go, aby ustawić ilość dopełnienia potrzebnego do każdego ciągu. Na przykład: strwidth(c(LETTERS, letters, " "), units="inches", family="Times") da ci szerokość wszystkich małych i wielkich liter oraz spację w Czasy czcionka. - eipi10
@ eipi10 Nie wiedziałem strwidth. Kiedy mam trochę czasu, zaimplementuję go i zobaczę, jak to działa (chyba, że ​​ktoś mnie do tego pobije). Zastanawiałem się, w jaki sposób uzyskać szerokość nie-mono-rozmieszczonych czcionek; Twoja sugestia rozwiązuje to (zakładając strwidth działa tak, jak mamy nadzieję). - steveb
Spróbuj tego plot(strwidth(c(LETTERS, letters, " "), units="inches", family="Times"), 1:53, pch=c(LETTERS, letters, " ")) i to plot(strwidth(c(LETTERS, letters, " "), units="inches", family="Courier"), 1:53, pch=c(LETTERS, letters, " ")) oraz kilka innych czcionek i możesz zobaczyć różnicę w szerokości znaków (lub nie dla Couriera). - eipi10
Dziękuję i @ eipi10 za świetne odpowiedzi. Idę z odpowiedzią od Sandy'ego Muspratta, ponieważ wyrównuje tekst bezpośrednio, bez potrzeby dodawania spacji lub używania określonych czcionek. Biorąc to pod uwagę, dowiedziałem się dużo o formatowaniu tekstu i manipulacji w R z twoich postów. Dzięki jeszcze raz. - Brain_Food