Объединение grid_arrange_shared_legend() и facet_wrap_labeller() в R

Я пытаюсь объединить grid_arrange_shared_legend() и facet_wrap_labeller() в R. В частности, я хочу нарисовать фигуру, включающую две фигуры ggplot с несколькими панелями каждая и иметь общую легенду. Я также хочу выделить курсивом часть этикеток фасетной полосы. Первое возможно с помощью функции grid_arrange_shared_legend(), представленной здесь, а последнее может быть достигнуто с помощью функции facet_wrap_labeller() здесь. Тем не менее, мне не удалось совместить их.

Вот пример.

library("ggplot2")
set.seed(1)
d <- data.frame(
  f1 = rep(LETTERS[1:3], each = 100),
  f2 = rep(letters[1:3], 100),
  v1 = runif(3 * 100),
  v2 = rnorm(3 * 100)
)
p1 <- ggplot(d, aes(v1, v2, color = f2)) + geom_point() + facet_wrap(~f1)
p2 <- ggplot(d, aes(v1, v2, color = f2)) + geom_smooth() + facet_wrap(~f1)

Я могу поместить p1 и p2 на один и тот же рисунок и иметь общую легенду, используя grid_arrange_shared_legend() (слегка измененную по сравнению с оригиналом).

grid_arrange_shared_legend <- function(...) {
    plots <- list(...)
    g <- ggplotGrob(plots[[1]] + theme(legend.position = "right"))$grobs
    legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
    lheight <- sum(legend$width)
    grid.arrange(
        do.call(arrangeGrob, lapply(plots, function(x)
            x + theme(legend.position = "none"))),
        legend,
        ncol = 2,
        widths = unit.c(unit(1, "npc") - lheight, lheight))
}
grid_arrange_shared_legend(p1, p2)

Вот что я получаю. введите здесь описание изображения

Часть этикетки полосы можно выделить курсивом facet_wrap_labeller().

facet_wrap_labeller <- function(gg.plot,labels=NULL) {
  require(gridExtra)

  g <- ggplotGrob(gg.plot)
  gg <- g$grobs      
  strips <- grep("strip_t", names(gg))

  for(ii in seq_along(labels))  {
    modgrob <- getGrob(gg[[strips[ii]]], "strip.text", 
                       grep=TRUE, global=TRUE)
    gg[[strips[ii]]]$children[[modgrob$name]] <- editGrob(modgrob,label=labels[ii])
  }
  g$grobs <- gg
  class(g) = c("arrange", "ggplot",class(g)) 
  g
}
facet_wrap_labeller(p1, 
  labels = c(
    expression(paste("A ", italic(italic))),
    expression(paste("B ", italic(italic))), 
    expression(paste("C ", italic(italic)))
  )
)

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

Тем не менее, я не могу совместить эти две вещи простым способом.

p3 <- facet_wrap_labeller(p1, 
  labels = c(
    expression(paste("A ", italic(italic))),
    expression(paste("B ", italic(italic))), 
    expression(paste("C ", italic(italic)))
  )
)
p4 <- facet_wrap_labeller(p2, 
  labels = c(
    expression(paste("A ", italic(italic))),
    expression(paste("B ", italic(italic))), 
    expression(paste("C ", italic(italic)))
  )
)
grid_arrange_shared_legend(p3, p4)
# Error in plot_clone(p) : attempt to apply non-function

Кто-нибудь знает, как изменить одну или обе функции, чтобы их можно было комбинировать? Или есть другой способ достичь цели?


person Akira Murakami    schedule 12.07.2015    source источник
comment
facet_wrap_labeller возвращает gtable, поэтому другая функция запутается, потому что ожидает ggplot.   -  person baptiste    schedule 12.07.2015
comment
Спасибо за выявление причины. Я думал, что тоже не передаю объект с правильным классом. Есть ли способ преобразовать объект gtable в объект ggplot? Я не совсем знаком с gtable (пока) и не знаю, как решить эту проблему.   -  person Akira Murakami    schedule 12.07.2015


Ответы (1)


Вам нужно передать gtable вместо ggplot,

library(gtable)
library("ggplot2")
library(grid)
set.seed(1)
d <- data.frame(
  f1 = rep(LETTERS[1:3], each = 100),
  f2 = rep(letters[1:3], 100),
  v1 = runif(3 * 100),
  v2 = rnorm(3 * 100)
)
p1 <- ggplot(d, aes(v1, v2, color = f2)) + geom_point() + facet_wrap(~f1)
p2 <- ggplot(d, aes(v1, v2, color = f2)) + geom_smooth() + facet_wrap(~f1)


facet_wrap_labeller <- function(g, labels=NULL) {

  gg <- g$grobs      
  strips <- grep("strip_t", names(gg))

  for(ii in seq_along(labels))  {
    oldgrob <- getGrob(gg[[strips[ii]]], "strip.text", 
                       grep=TRUE, global=TRUE)
    newgrob <- editGrob(oldgrob,label=labels[ii])
    gg[[strips[ii]]]$children[[oldgrob$name]] <- newgrob
  }
  g$grobs <- gg
  g
}


combined_fun <- function(p1, p2, labs1) {

  g1 <- ggplotGrob(p1 + theme(legend.position = "right"))
  g2 <- ggplotGrob(p2 + theme(legend.position = "none")) 

  g1 <- facet_wrap_labeller(g1, labs1)

  legend <- gtable_filter(g1, "guide-box", trim = TRUE)
  g1p <- g1[,-(ncol(g1)-1)]
  lw <- sum(legend$width)

  g12 <- rbind(g1p, g2, size="first")
  g12$widths <- unit.pmax(g1p$widths, g2$widths)
  g12 <- gtable_add_cols(g12, widths = lw)
  g12 <- gtable_add_grob(g12, legend, 
                         t = 1, l = ncol(g12), b = nrow(g12))
  g12
}


test <- combined_fun(p1, p2, labs1 = c(
                      expression(paste("A ", italic(italic))),
                      expression(paste("B ", italic(italic))), 
                      expression(paste("C ", italic(italic)))
                    )
)

grid.draw(test)
person baptiste    schedule 12.07.2015
comment
Это потрясающе! Спасибо. - person Akira Murakami; 13.07.2015