将分面绘图拆分为绘图列表

19

我非常喜欢使用ggplot2中的分面图。但是,有时候子图太多了,希望将它们拆分成一个图表列表。例如:

df <- data.frame(x=seq(1,24,1), y=seq(1,24,1), z=rep(seq(1,12),each=2))
df
    x  y  z
1   1  1  1
2   2  2  1
3   3  3  2
4   4  4  2
5   5  5  3
.   .  .  .
.   .  .  .

myplot <- ggplot(df,aes(x=x, y=y))+geom_point()+facet_wrap(~z)
myplot

我该如何编写函数来将结果图分割成一系列图形列表? 大致如下:

enter image description here

splitFacet <- function(subsPerPlot){
  # Method to break a single facet plot into a list of facet plots, each with at most `subsPerPlot` subplots

  # code...

  return(listOfPlots)
}

4
创建单独的图表不是更容易吗?例如像myplots <- lapply(unique(df$z), function(id) ggplot(subset(df, z == id), aes(x=x, y=y))+geom_point() + ylim(range(df$y)) + xlim(range(df$x)) ); do.call(gridExtra::grid.arrange, myplots[3:6])这样。 - lukeA
1
如果您想在图表中包含一个带有z值的条形标签,那么您应该添加 facet_wrap(~z) - eipi10
1
@lukeA 我不确定它是否与图例兼容。 - Ben
@Ben,你最终解决了这个问题吗?我正在寻找类似的解决方案。 - Pete900
@Pete900 很遗憾,不行。 - Ben
4个回答

19

将绘图区域拆分成单独的小区域

我们按以下步骤构建一个函数:

  1. 通过遍历对象的结构,获取用于分面的变量名称(在这里是'z')。
  2. 我们用空的ggplot对象中的facet元素覆盖绘图对象的facet元素(因此如果我们在此阶段打印它,则分面将被删除)。
  3. 我们提取数据并沿着我们在第1步中确定的变量进行拆分。
  4. 我们用每个子集(这里是12次)覆盖原始数据并将所有输出存储在列表中。

代码:

splitFacet <- function(x){
  facet_vars <- names(x$facet$params$facets)         # 1
  x$facet    <- ggplot2::ggplot()$facet              # 2
  datasets   <- split(x$data, x$data[facet_vars])    # 3
  new_plots  <- lapply(datasets,function(new_data) { # 4
    x$data <- new_data
    x})
}    

new_plots <- splitFacet(myplot)
length(new_plots) # [1] 12
new_plots[[3]]    # 3rd plot


将图案分割成多个子图,每个子图最多有n

如果我们想保留这些子图,但想要更少的子图,可以跳过第2步,重新调整我们的分割方式,使其包括用于分面的多个变量值。

我们不需要创建一个单独的函数,而是将第1个函数通用化,其中n表示每个图中的子图数量。

n = NULL表示获得先前输出的结果,这与n = 1(每个图中仅包含一个子图)稍有不同。

splitFacet <- function(x, n = NULL){
  facet_vars <- names(x$facet$params$facets)               # 1
  if(is.null(n)){
    x$facet  <- ggplot2::ggplot()$facet                    # 2a
    datasets <- split(x$data, x$data[facet_vars])          # 3a
  } else {
    inter0 <- interaction(x$data[facet_vars], drop = TRUE) # 2b
    inter  <- ceiling(as.numeric(inter0)/n)
    datasets <- split(x$data, inter)                       # 3b
  }
  new_plots  <- lapply(datasets,function(new_data) {       # 4
    x$data <- new_data
    x})
} 

new_plots2 <- splitFacet(myplot,4)
length(new_plots2) # [1] 3
new_plots2[[2]]    


这也可能会很有用:

unfacet <- function(x){
  x$facet <- ggplot2::ggplot()$facet
  x
}

整洁的方式

如果代码可用,就不需要费这么大劲了,我们可以在将数据提供给 ggplot 之前对其进行拆分:


library(tidyverse)
myplots3 <-
  df %>% 
  split(ceiling(group_indices(.,z)/n_facets)) %>% 
  map(~ggplot(.,aes(x =x, y=y))+geom_point()+facet_wrap(~z))

myplots3[[3]]


1
感谢您提供这个精彩的答案! - markus

5
在寻找这个问题的解决方案时,我发现了ggplus。具体来说,是函数facet_multiplehttps://github.com/guiastrennec/ggplus 它允许您通过指定每页所需的图表数量来将一个数据面板拆分成多个页面。在您的示例中,它应该是:
library(ggplus)

df <- data.frame(x=seq(1,24,1), y=seq(1,24,1), z=rep(seq(1,12),each=2))

myplot <- ggplot(df,aes(x=x, y=y))+geom_point()

facet_multiple(plot = myplot, facets = 'z', ncol = 2, nrow = 2)

这是你需要的类型吗?对我来说,它非常有效。


install.packages("ggplus") 正在安装包到... 警告:install.packages: 包‘ggplus’在R版本3.2.2中不可用。有什么解决办法吗? - Matias Andina
1
啊,抱歉我不确定。也许你可以在 GitHub 页面上发布并寻求帮助:https://github.com/guiastrennec/ggplus。我仍然使用 v3.2.1,现在你提到了这个问题,我不会升级。如果你解决了这个问题,请告诉我。谢谢。Pete - Pete900

3

这与Moody_Muddskipper的回答类似,但适用于任何类型的分面(facet_gridfacet_wrap),处理分面中任意表达式,并且不绘制分面条。

library(rlang)
library(ggplot2)

split_facets <- function(x) {
  facet_expr <- unlist(x[["facet"]][["params"]][c("cols", "rows", "facets")])
  facet_levels <- lapply(facet_expr, rlang::eval_tidy, data = x[["data"]])
  facet_id <- do.call(interaction, facet_levels)
  panel_data <- split(x[["data"]], facet_id)
  plots <- vector("list", length(panel_data))
  for (ii in seq_along(plots)) {
    plots[[ii]] <- x
    plots[[ii]][["data"]] <- panel_data[[ii]]
    plots[[ii]][["facet"]] <- facet_null()
  }
  plots
}

split_facets(ggplot(df,aes(x=x, y=y))+geom_point()+facet_wrap(~z))
split_facets(ggplot(df,aes(x=x, y=y))+geom_point()+facet_grid(z %% 2 ~ z %% 5))

它使用rlang::eval_tidy来评估分面表达式,将它们组合成一个单一的分类因子,然后使用该因子来拆分数据。它还通过使用facet_null()替换每个子图的分面部分来“抑制”每个子图的分面部分。

0

为了帮助想要使用ggplus的人,我在这里发布一下。ggplus可以与较新版本的R一起使用,但您需要按照开发者的说明进行安装,即:

 devtools::install_github("guiastrennec/ggplus")

我在尝试使用RStudio安装它时遇到了同样的问题,然后意识到它并不是“标准包”之一。我正在使用3.4.4版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接