Поместите легенду для каждой сетки facet_wrap в ggplot2.

У меня есть этот кадр данных:

        Date Server FileSystem PercentUsed
1  12/1/2011      A          /          60
2   1/2/2012      A       /var          50
3   2/1/2012      A        tmp          90
4  2/10/2012      A        /db          86
5  2/13/2012      A       /app          90
6  12/1/2011      B         C:          67
7   1/2/2012      B         D:          67
8   2/1/2012      B         F:          34
9  2/10/2012      B   /restore          89
10 2/13/2012      B         G:          56
11 12/1/2011      C          /          90
12  1/2/2012      C       /tmp          78
13  2/1/2012      C      /data          67
14 2/10/2012      C   /Storage          34
15 2/13/2012      C  /database          12

dput(x)
structure(list(Date = structure(c(2L, 1L, 3L, 4L, 5L, 2L, 1L, 
3L, 4L, 5L, 2L, 1L, 3L, 4L, 5L), .Label = c("1/2/2012", "12/1/2011", 
"2/1/2012", "2/10/2012", "2/13/2012"), class = "factor"), Server = structure(c(1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), .Label = c("A", 
"B", "C"), class = "factor"), FileSystem = structure(c(1L, 9L, 
14L, 5L, 2L, 10L, 11L, 12L, 6L, 13L, 1L, 8L, 3L, 7L, 4L), .Label = c("/", 
"/app", "/data", "/database", "/db", "/restore", "/Storage", 
"/tmp", "/var", "C:", "D:", "F:", "G:", "tmp"), class = "factor"), 
    PercentUsed = c(60L, 50L, 90L, 86L, 90L, 67L, 67L, 34L, 89L, 
    56L, 90L, 78L, 67L, 34L, 12L)), .Names = c("Date", "Server", 
"FileSystem", "PercentUsed"), class = "data.frame", row.names = c(NA, 
-15L))

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

Когда я это делаю, легенда помещается сбоку от графика для всех FileSystem. Можно ли поставить FileSystem принадлежащих каждому серверу рядом с каждой сеткой?

ggplot(x, aes(Date, PercentUsed, group=1, colour=FileSystem)) + 
     geom_jitter(size=0.5) + geom_smooth(method="loess", se=T) + 
     facet_wrap(~Server, ncol=1)

person user1471980    schedule 12.02.2013    source источник


Ответы (5)


Мех, @joran опередил меня (мой gridExtra устарел, но мне потребовалось 10 минут, чтобы понять это). Вот похожее решение, но в этом случае кошачий скин выполняется по уровням в Server.

library(gridExtra)
out <- by(data = x, INDICES = x$Server, FUN = function(m) {
      m <- droplevels(m)
      m <- ggplot(m, aes(Date, PercentUsed, group=1, colour = FileSystem)) + 
         geom_jitter(size=2) + geom_smooth(method="loess", se=T)
   })
do.call(grid.arrange, out)

# If you want to supply the parameters to grid.arrange
do.call(grid.arrange, c(out, ncol=3))

изображение

person Roman Luštrik    schedule 12.02.2013
comment
Очень хорошо. Я не знал, что у droplevels() есть метод для data.frames. Это удобно! - person Josh O'Brien; 13.02.2013
comment
Есть ли аккуратный способ принудительного выравнивания, т.е. сохранить ширину области графика одинаковой? Прописать ширину легенды? - person mlt; 05.05.2016
comment
@mlt рассмотрите возможность перемещения легенды в верхнюю часть первой фигуры и удаления ее из остальных. - person Roman Luštrik; 06.05.2016
comment
Действительно, тем временем я переместил все легенды наверх. Я не могу использовать одну легенду, поскольку они уникальны для каждого сюжета. Хотя было бы неплохо узнать, есть ли способ задать ширину. - person mlt; 06.05.2016
comment
@mlt рассмотрите возможность создания нового вопроса, хотя, насколько я знаю, это невозможно (хотя можно определить поля). - person Roman Luštrik; 06.05.2016
comment
@mlt, ознакомьтесь с моими предложениями ниже. - person Nova; 23.06.2020

Лучше всего это сделать с помощью пакета gridExtra:

library(gridExtra)

xs <- split(x,f = x$Server)
p1 <- ggplot(xs$A,aes(x = Date,y = PercentUsed,group = 1,colour = FileSystem)) + 
        geom_jitter(size=0.5) + 
        geom_smooth(method="loess", se=T) + 
        facet_wrap(~Server, ncol=1)

p2 <- p1 %+% xs$B
p3 <- p1 %+% xs$C

grid.arrange(p1,p2,p3)

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

person joran    schedule 12.02.2013
comment
Я должен отметить, что вы сделали точки очень маленькими в geom_jitter, и я не уверен, почему, но я оставил все как есть. Точки есть, но плохо видно. - person joran; 13.02.2013
comment
Я немного заинтригован оператором %+%. Не могли бы вы объяснить, что он делает? - person Legend; 08.05.2013
comment
@Legend Это способ сделать объект ggplot модульным в том смысле, что вы можете использовать его, чтобы просто добавить новый фрейм данных, но использовать все те же спецификации геометрии из предыдущего графика. Конечно, это будет работать только в том случае, если все имена столбцов совпадают, и если вы не использовали никакие другие фреймы данных в других слоях. - person joran; 08.05.2013
comment
+1 Спасибо за оперативный ответ. Это такой фантастический способ повторного использования объектов! - person Legend; 08.05.2013
comment
@joran, как сделать так, чтобы каждый график в сетке занимал одинаковое пространство, чтобы их галочки x совпадали? - person lgd; 23.03.2015
comment
@lgd Это требует некоторой работы. См. здесь (и ссылки в нем) для некоторых идей. - person joran; 23.03.2015
comment
@lgd, несколько предложений в моем ответе ниже, чтобы правильно выровнять пространство сюжета. - person Nova; 23.06.2020

Вместо использования фасетов мы могли бы составить список графиков для каждой группы, а затем использовать для построения графиков cowplot::plot_grid. У каждого будет своя легенда:

# make list of plots
ggList <- lapply(split(x, x$Server), function(i) {
  ggplot(i, aes(Date, PercentUsed, group = 1, colour = FileSystem)) + 
    geom_jitter(size = 2) +
    geom_smooth(method = "loess", se = TRUE)})

# plot as grid in 1 columns
cowplot::plot_grid(plotlist = ggList, ncol = 1,
                   align = 'v', labels = levels(x$Server))

Как предложил @Axeman, мы могли бы добавлять метки, используя facet_grid(~Server) вместо labels = levels(x$Server).

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

person zx8754    schedule 16.02.2018
comment
Отлично. Если вы хотите, чтобы отображались серые полосы фасетов, вы все равно можете добавить + facet_grid(~Server). Использование align = 'v' в plot_grid даст более красивый результат. - person Axeman; 16.02.2018
comment
@Axeman хорошее замечание по поводу v, спасибо, обновлено. Я хотел избежать использования фасета, поэтому вместо этого использовал метку. - person zx8754; 16.02.2018

Мне понравился ответ @joran, и я предложил несколько вариантов, основанных на их коде, в качестве отправной точки. Оба варианта решают проблему неправильного выравнивания фасетов.

Легенды вне граней

Если вы выберете моноширинный шрифт для своих элементов легенды, вы можете использовать str_pad, чтобы добавить отступы с правой стороны всех элементов легенды, заставив длину каждого из них быть согласованной.

Если вы хотите использовать моноширинный шрифт, это быстрое решение.

library(ggplot2)
library(dplyr)
library(gridExtra)
library(stringr)

l <- max(nchar(as.character(x$FileSystem)))
mylevels <- as.character(levels(x$FileSystem))
mylevels <- str_pad(mylevels, width = l, side = "right", pad = " ")
x <- mutate(x, FileSystem = factor(str_pad(FileSystem, width = l, side = "right", pad = " "),
            levels = mylevels))
windowsFonts("Lucida Sans Typewriter" = windowsFont("Lucida Sans Typewriter"))
xs <- split(x,f = x$Server)
p1 <- ggplot(xs$A,aes(x = Date,y = PercentUsed,group = 1,colour = FileSystem)) + 
  geom_jitter(size=0.5) + 
  geom_smooth(method="loess", se=T) + 
  facet_wrap(~Server, ncol=1) +
  theme(legend.text = element_text(family = "Lucida Sans Typewriter"))

p2 <- p1 %+% xs$B
p3 <- p1 %+% xs$C

grid.arrange(p1,p2,p3)

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

Легенды внутри граней

Если вы не возражаете против легенд внутри каждого аспекта, вы можете добавить дополнительное пространство к каждому аспекту с помощью аргумента расширения внутри вызова scale:

library(lubridate)
x <- mutate(x, Date = as.Date(as.character(Date), format = "%m/%d/%Y"))
xs <- split(x,f = x$Server)
p1 <- ggplot(xs$A,aes(x = Date,y = PercentUsed,group = 1,colour = FileSystem)) + 
  geom_jitter(size=0.5) + 
  scale_x_date(expand = expansion(add = c(5, 20)),
               date_labels = "%d-%m-%Y") +
  geom_smooth(method="loess", se=T) + 
  facet_wrap(~Server, ncol=1) +
  theme_bw() +
  theme(legend.position = c(0.9, 0.5))

p2 <- p1 %+% xs$B
p3 <- p1 %+% xs$C

grid.arrange(p1,p2,p3)

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

person Nova    schedule 23.06.2020

Кроме gridExtra и cowplot в игре теперь есть еще patchwork. Следовательно, вы можете сделать следующее:

require(ggplot2)
require(patchwork)
# split
dfs = split(df, f = df$Server)
# apply ggplot function and write to list
gg_l = lapply(dfs, function(x) {
  ggplot(x, aes(x = Date,y = PercentUsed, group = 1, colour = FileSystem)) + 
    geom_jitter(size = 0.5) + 
    geom_smooth(method = "loess", se = TRUE) + 
    facet_wrap(~ Server, ncol = 1)
})
# patchwork
wrap_plots(gg_l, ncol = 1)

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

Вы также можете комбинировать графики вручную, посмотрите здесь. Я использовал данные ОП для df.

person andschar    schedule 01.06.2021