многопанельный ggplot из списка с grid_arrange_shared_legend

Я пытаюсь сделать свою мультипанель ggplot с общей легендой более гибкой в ​​ShinyApp, позволяя пользователю выбирать, сколько панелей отображать.

В настоящее время мой код записывает объекты панели по 1 за раз.

grid_arrange_shared_legend(p1,p2,p3,p4, ncol = 4, nrow = 1)

Я не совсем понимаю, почему я не могу найти способ сказать grid_arrange_shared_legend принять список графиков (объект списка), а не записывать их 1 за другим. Выдает эту ошибку:

Ошибка в UseMethod("ggplot_build"): нет применимого метода для 'ggplot_build', примененного к объекту класса "NULL"

library(ggplot2)
library(lemon)
plotlist <- list()
dsamp <- diamonds[sample(nrow(diamonds), 300), ]
plotlist$p1 <- qplot(carat, price, data = dsamp, colour = clarity)
plotlist$p2 <- qplot(cut, price, data = dsamp, colour = clarity)
plotlist$p3 <- qplot(color, price, data = dsamp, colour = clarity)
plotlist$p4 <- qplot(depth, price, data = dsamp, colour = clarity)
grid_arrange_shared_legend(plotlist, ncol = 4, nrow = 1)

при использовании списка не имело бы значения, сколько графиков в списке, и я бы вычислил ncol или nrow на основе длины списка...


person Mark    schedule 01.04.2019    source источник


Ответы (2)


Моя доморощенная версия функции получает это, добавляя параметр plotlist и добавляя строку plots <- c(list(...), plotlist) в качестве первой строки кода. Таким образом, он может принимать как список графиков, так и отдельные объекты графика.

grid_arrange_shared_legend_plotlist <- function(..., 
                                                plotlist=NULL,
                                                ncol = length(list(...)),
                                                nrow = NULL,
                                                position = c("bottom", "right")) {

  plots <- c(list(...), plotlist)

  if (is.null(nrow)) nrow = ceiling(length(plots)/ncol)

  position <- match.arg(position)
  g <- ggplotGrob(plots[[1]] + theme(legend.position = position))$grobs
  legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
  lheight <- sum(legend$height)
  lwidth <- sum(legend$width)
  gl <- lapply(plots, function(x) x + theme(legend.position="none"))
  gl <- c(gl, ncol = ncol, nrow = nrow)

  combined <- switch(position,
                     "bottom" = arrangeGrob(do.call(arrangeGrob, gl),
                                            legend,
                                            ncol = 1,
                                            heights = unit.c(unit(1, "npc") - lheight, lheight)),
                     "right" = arrangeGrob(do.call(arrangeGrob, gl),
                                           legend,
                                           ncol = 2,
                                           widths = unit.c(unit(1, "npc") - lwidth, lwidth)))

  grid.newpage()
  grid.draw(combined)

  # return gtable invisibly
  invisible(combined)
}

Используя ваш пример:

library(gridExtra)
library(grid)
library(ggplot2)
plots <- list()
dsamp <- diamonds[sample(nrow(diamonds), 300), ]
plots$p1 <- qplot(carat, price, data = dsamp, colour = clarity)
plots$p2 <- qplot(cut, price, data = dsamp, colour = clarity)
plots$p3 <- qplot(color, price, data = dsamp, colour = clarity)
plots$p4 <- qplot(depth, price, data = dsamp, colour = clarity)

grid_arrange_shared_legend_plotlist(plotlist = plots, ncol = 4)

результат

person mgiormenti    schedule 01.04.2019
comment
все еще получаю ту же ошибку Ошибка в UseMethod (ggplot_build): нет применимого метода для 'ggplot_build', примененного к объекту класса NULL grid_arrange_shared_legend_custom (список графиков, ncol = 4, nrow = 1) (дал функции новое имя) - person Mark; 01.04.2019
comment
Вы явно передаете объект списка графиков в параметр списка графиков? (извините, что они называются одинаково). Как grid_arrange_shared_legend(plotlist = plotlist, ncol = 4). Если вы этого не сделаете, список графиков будет взят ... при условии, что это один объект графика, что приведет к ошибке. - person mgiormenti; 02.04.2019
comment
ах да, это объясняет это. Я также уже понял, что plotlist не был умным именем для фиктивного кода, в моем реальном приложении он имеет другое имя. Я обнаружил, что мое глупое решение eval (анализ ниже тоже работает как шарм, хотя - person Mark; 02.04.2019
comment
Ваше решение читается гораздо более приятным кодом, и теперь оно действительно работает нормально. ласки. Я приму ваш ответ как решение - person Mark; 02.04.2019
comment
Спасибо! Рад, что был чем-то полезен. Оглядываясь назад, я должен был включить рабочий пример с самого начала. Я буду иметь это в виду с этого момента. - person mgiormenti; 02.04.2019
comment
конечно действительно избегает путаницы. Я пропустил аргумент в пользу функции, так как у меня уже был долгий день кодирования и работы. Спасибо - person Mark; 02.04.2019

Некрасивое решение для вставки текстовой строки:

Поскольку предоставленные ответы, похоже, не работают или не подходят (восстановление совершенно другого набора графиков, чем список объектов графика, который у меня уже есть из обширного кода, я немного поиграл с eval(parse(text = ....) и paste0 для динамического создания текстовой строки что в конечном итоге является полностью написанным кодом (который работает) без его фактического написания

nplots = 4
nrow = 2
ncol = ceiling(nplots/nrow)
eval(parse( text = paste0("grid_arrange_shared_legend(", paste0("plotlist", "[[", c(1:nplots), "]]", sep = '', collapse = ','), ",ncol =", ncol, ",nrow =", nrow, ", position = 'right',  top=grid::textGrob('My title', gp=grid::gpar(fontsize=18)))", sep = '')))

который производит:

[1] "grid_arrange_shared_legend (список графиков [[1]], список графиков [[2]], список графиков [[3]], список графиков [[4]], ncol = 2, nrow = 2, position = 'right', top = grid::textGrob('Мой заголовок', gp=grid::gpar(fontsize=18)))"

person Mark    schedule 01.04.2019