在ggplot2中将空图添加到facet_wrap

wwtsj6pe  于 2023-07-31  发布在  其他
关注(0)|答案(2)|浏览(96)

我尝试创建3个图,其中所有面板的大小都应该相同,所以我认为解决方案可能是facet_wrap。我的问题是,我不希望每个图中有相同数量的图

df <- data.frame(group=c(1,1,2,2,2,3,3),
                 name=c('a','b','c','d','e','f','g'),
                 x=c(1,2,3,4,5,6,7),
                 y=c(2,3,4,5,6,7,8))
ggplot(df, aes(x,y)) + geom_point() + facet_wrap(~ name, ncol=3)

字符串
结果将图形组合为连续顺序,但我希望按组列对它们进行分组。
所以我期望的结果是

a b _
c d e 
f g _


但我得到的是

a b c
d e f
g _ _


有没有人建议创建一个这样的图,也许是通过添加空图?我找到了另一个线程Force facet_wrap to fill bottom row...,但我无法让它在我的情况下运行。

oogrdqng

oogrdqng1#

facet_wrap简单地将一个图放在另一个图之后,并在适当数量的图之后插入“换行符”。但是还有facet_grid,它允许您指定行和列索引。您的行索引是group,我现在添加一个列索引,如下所示:

cols<-c(a=1,c=1,f=1,b=2,d=2,g=2,e=3)
df$col <- as.factor(cols[as.character(df$name)])

字符串
现在,您可以使用以下命令绘制面栅格

ggplot(df, aes(x,y)) + geom_point() + facet_grid(group~col)


当然,根据您的问题是什么,您必须考虑一种适当的方法来设置列索引。
这就是剧情:


的数据

编辑:

作为对您的评论和借鉴this answer的React,我创建了第二个解决方案。它使用gridExtra并直接操作ggplot grob。我认为它给出了理想的结果,但在我呈现的形式中,它是“手工工作”。此解决方案使用facet_wrap而不是facet_grid
首先,我在变量name中添加一个“保持器级别”,这将确保创建空的小平面,然后创建图:

df$name2 <- factor(df$name,levels=c('a','b','','c','d','e','f','g',' '))
p <- ggplot(df, aes(x,y)) + geom_point() + facet_wrap(~name2,ncol=3,drop=FALSE)


drop=FALSE是导致ggplot绘制空面的原因。该图与我的第一个解决方案的不同之处仅在于如何标记面。现在到了棘手的部分:

library(gridExtra)
g <- ggplotGrob(p)
## remove empty panels
g$grobs[names(g$grobs) %in% c("panel3", "panel9", "strip_t3", "strip_t9")] <- NULL
## remove them from the layout
g$layout <- g$layout[!(g$layout$name %in% c("panel-3", "panel-9", 
                                           "strip_t-3", "strip_t-9")),]
## move axis closer to panel
g$layout[g$layout$name == "axis_b-9", c("t", "b")] = c(9,9)


这基本上就是评论所说的。如果您使用另一组图,请查看names(g$grobs)g$layout$name的输出,以确定必须删除哪些元素。
现在,您可以使用以下命令创建地块

grid.newpage()
grid.draw(g)


编辑二:

对于较新版本的ggplot2,上述解决方案不起作用。不幸的是,我不知道这是从哪个版本开始的,但它肯定不适用于2.2.1版本。
必须更改的部分是grob的修改:

g <- ggplotGrob(p)
# get the grobs that must be removed
rm_grobs <- g$layout$name %in% c("panel-1-3", "panel-3-3", "strip-t-3-1", "strip-t-3-3")
# remove grobs
g$grobs[rm_grobs] <- NULL
g$layout <- g$layout[!rm_grobs, ]
## move axis closer to panel
g$layout[g$layout$name == "axis-b-3-3", c("t", "b")] = c(14.5, 14.5)
grid.newpage()
grid.draw(g)


主要的变化是g$grobs不再是一个命名列表,并且grob的名称已经改变。注意,面板标记为"panel-row-col",而灰色条标记为"strip-t-col-row"

guicsvcw

guicsvcw2#

@Stibu的精彩回答的小补充:
不幸的是,当使用facet_wrap时,g$grobs中的面板名称并不总是与"panel-row-col"一致(而条带名称是一致的,并且在facet_grid中一切都很好)。
下面是一个基于@Stibu的答案的函数,我使用它来避免记住任何细节:

remove_facets <- function(plot, layout) {
  layout <- strsplit(layout, split = '\n')[[1]]
  layout <- lapply(layout, trimws)
  layout <- matrix(unlist(sapply(layout, strsplit, "")),
                   nrow = length(layout), byrow = T)
  layout <- which(layout == "#", arr.ind = TRUE)
  prm <- apply(layout,1,\(x) {
    c(glue::glue("panel-{x[1]}-{x[2]}"),
      glue::glue("strip-t-{x[2]}-{x[1]}"))
  })
  # https://stackoverflow.com/a/30372692/1296582
  g <- ggplot2::ggplotGrob(plot)
  rm_grobs <- g$layout$name %in% prm
  g$grobs[rm_grobs] <- NULL
  g$layout <- g$layout[!rm_grobs, ]
  ggpubr::as_ggplot(g)
}

字符串
其中plotggplotlayout是表示分面图的字符串,其中要移除的分面由#指示。
下面是一个简单的例子:

(p1 <- ggplot(mtcars, aes(mpg, disp)) +
  geom_point() +
  facet_grid(rows=vars(cyl), cols=vars(gear)))

# any character can indicate a facet, '#' indicates a facet that will be removed
a <- c("a#a
        ##a
        a##")

remove_facets(p1, a)


x1c 0d1x的数据
如前所述,该函数并不健壮:

  • facet_grid配合使用效果最佳,与facet_wrap配合使用时可能需要手动调整
  • facet_wrap的条带应位于顶部
  • 返回类型是ggpubr::as_ggplot,但它并不确切地说是ggplot;根据您对绘图的处理,返回或直接绘制为grob可能更可取
  • 当与其他图组合时,cowplot比patchwork更好地处理这个伪grob

相关问题