#r #ggplot2 #lemon
#r #ggplot2
Вопрос:
Я пытаюсь сделать мою мультипанель ggplot
с общей легендой более гибкой в ShinyApp
, позволяя пользователю выбирать, сколько панелей отображать.
В настоящее время мой код записывает объекты панели по 1 за раз, подобный этому.
grid_arrange_shared_legend(p1,p2,p3,p4, ncol = 4, nrow = 1)
Я не совсем понимаю, почему я не могу найти способ сообщить grid_arrange_shared_legend
, чтобы он принимал список графиков (list object), а не выписывал их один за другим.
Выдает эту ошибку:
Ошибка в 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 на основе длины списка…
Ответ №1:
Моя домашняя версия функции получает это, добавляя 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)
Комментарии:
1. по-прежнему получаю ту же ошибку Ошибка в UseMethod(«ggplot_build»): нет применимого метода для ‘ggplot_build’, примененного к объекту класса «NULL» grid_arrange_shared_legend_custom(plotlist, ncol = 4, nrow = 1) (присвоено функции новое имя)
2. Вы явно передаете объект plotlist в параметр plotlist? (извините, что они называются одинаково). Нравится
grid_arrange_shared_legend(plotlist = plotlist, ncol = 4)
. Если вы этого не сделаете, список графиков будет взят … предполагается, что это один объект plot, что приводит к ошибке.3. ах да, это все объясняет. Я также уже понял, что «plotlist» не было разумным названием для фиктивного кода, в моем реальном приложении у него другое имя. Я обнаружил, что мое глупое решение eval (parse, приведенное ниже, тоже работает как шарм
4. Однако ваше решение намного приятнее читается с точки зрения кода, и действительно, сейчас оно работает нормально. обнимашки. Я приму ваш ответ как решение
5. Спасибо! Рад, что был чем-то полезен. Оглядываясь назад, я должен был включить рабочий пример с самого начала. Впредь я буду иметь это в виду.
Ответ №2:
Решение для вставки уродливой текстовой строки:
Поскольку предоставленные ответы, похоже, не работают или не подходят (перестраивают совершенно другой набор графиков, чем список объектов графика, который у меня уже есть из обширного кода, я немного поиграл с 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(размер шрифта = 18)))»