ggplot2跨越组中的嵌套构面

ZNK*_*ZNK 15 r facet ggplot2

我遇到了一种情况,我希望创建一个由三个分组变量构成的图.要做到这一点,我会简单地使用facet_grid(f1 ~ f2 + f3),但问题是f2的标签是多余的,并且让它们跨越f3嵌套的f3的方面会更好.

MWE:

library('tibble')
library('ggplot2')
df <- tribble(
  ~x, ~y, ~f1, ~f2, ~f3,
  0.5, 0.5, "a", "a", "a",
  0.5, 0.5, "b", "a", "a",
  0.5, 0.5, "a", "b", "a",
  0.5, 0.5, "b", "b", "a",
  0.5, 0.5, "a", "a", "b",
  0.5, 0.5, "b", "a", "b",
  0.5, 0.5, "a", "b", "b",
  0.5, 0.5, "b", "b", "b"
)


p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  facet_grid(f1 ~ f2 + f3)
Run Code Online (Sandbox Code Playgroud)

嵌套小平面图的MWE

同样,我希望将f2的标签组合在一起,这样它们就不那么多余了.

编辑:这与其他问题的不同之处在于,它询问如何使用现有分组修改构面而不是添加新构图.

teu*_*and 22

我很抱歉破坏了这个线程并进行了意外的自我推广,但是我可以将其概括为一个facet_nested()函数,可以在这里找到代码。该代码应该是非常独立的,即不应依赖于程序包中的其他功能(这是一个公共的个人程序包,此时具有一些随机绘图功能)。

该功能尚未经过广泛测试,但我认为它可能会对人们带来一些便利。也许会有一些好的反馈。

除分组条的范围外,我还对此功能进行了另外两项修改。一是它不会自动扩展缺少的变量。这是因为我认为在vars()使用两个data.frame进行绘图时,嵌套构面应该能够与非嵌套构面共存,而无需输入第二个或其他参数。第二个是,它从外到内对条带进行排序,从而即使switch设置了内层,也比外层更靠近面板。

假设上面的问题df是这样,那么在此问题中的情节再现如下df

p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  facet_nested(f1 ~ f2 + f3)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

还有一个相关的问题有一个更真实的例子情节,这工作就像下面,假设dfdf从这个问题:

p <- ggplot(df, aes("", density)) + 
  geom_boxplot(width=0.7, position=position_dodge(0.7)) + 
  theme_bw() +
  facet_nested(. ~ species + location +  position) +
  theme(panel.spacing=unit(0,"lines"),
        strip.background=element_rect(color="grey30", fill="grey90"),
        panel.border=element_rect(color="grey90"),
        axis.ticks.x=element_blank()) +
  labs(x="")
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • 我认为 ggplot 坚持这样的理念:ggplot2 包中的功能应该是 1. 普遍适用且维护成本低,或者 2. 是一个基本功能。鉴于它没有经过大量测试(高维护)并且不是必需的功能,因此这最适合扩展包。 (2认同)

ZNK*_*ZNK 21

这个问题的答案就在于在中gridgtable包.情节中的所有内容都按照特定顺序排列,如果你挖一点,你可以找到所有东西.

library('gtable')
library('grid')
library('magrittr') # for the %>% that I love so well

# First get the grob
z <- ggplotGrob(p) 
Run Code Online (Sandbox Code Playgroud)

此操作的最终目标是覆盖顶部构面标签,但技巧是这两个面都存在于网格空间的同一行中.它们是表格中的一个表格(查看名称为"strip"的行,还要注意zeroGrob;这些将在以后有用):

z
## TableGrob (13 x 14) "layout": 34 grobs
##     z         cells       name                                   grob
## 1   0 ( 1-13, 1-14) background        rect[plot.background..rect.522]
## 2   1 ( 7- 7, 4- 4)  panel-1-1               gTree[panel-1.gTree.292]

                                    ...

## 20  3 ( 7- 7,12-12)   axis-r-1                         zeroGrob[NULL]
## 21  3 ( 9- 9,12-12)   axis-r-2                         zeroGrob[NULL]
## 22  2 ( 6- 6, 4- 4)  strip-t-1                          gtable[strip]
## 23  2 ( 6- 6, 6- 6)  strip-t-2                          gtable[strip]
## 24  2 ( 6- 6, 8- 8)  strip-t-3                          gtable[strip]
## 25  2 ( 6- 6,10-10)  strip-t-4                          gtable[strip]
## 26  2 ( 7- 7,11-11)  strip-r-1                          gtable[strip]
## 27  2 ( 9- 9,11-11)  strip-r-2                          gtable[strip]

                                    ...

## 32  8 ( 3- 3, 4-10)   subtitle  zeroGrob[plot.subtitle..zeroGrob.519]
## 33  9 ( 2- 2, 4-10)      title     zeroGrob[plot.title..zeroGrob.518]
## 34 10 (12-12, 4-10)    caption   zeroGrob[plot.caption..zeroGrob.520]
Run Code Online (Sandbox Code Playgroud)

如果放大到第一个条带,则可以看到嵌套结构:

z$grob[[22]]
## TableGrob (2 x 1) "strip": 2 grobs
##   z     cells  name                                 grob
## 1 1 (1-1,1-1) strip absoluteGrob[strip.absoluteGrob.451]
## 2 2 (2-2,1-1) strip absoluteGrob[strip.absoluteGrob.475]
Run Code Online (Sandbox Code Playgroud)

对于每个grob,我们有一个对象,列出了它的绘制顺序(z),网格中的位置(单元格),标签(名称)和几何(grob).

由于我们可以在gtables中创建gtables,我们将使用它来绘制我们原始的情节.首先,我们需要找到需要替换的图中的位置.

# Find the location of the strips in the main plot
locations <- grep("strip-t", z$layout$name)

# Filter out the strips (trim = FALSE is important here for positions relative to the main plot)
strip <- gtable_filter(z, "strip-t", trim = FALSE)

# Gathering our positions for the main plot
top <- strip$layout$t[1]
l   <- strip$layout$l[c(1, 3)]
r   <- strip$layout$r[c(2, 4)]
Run Code Online (Sandbox Code Playgroud)

一旦我们有了职位,我们就需要创建一个替换表.我们可以使用列表矩阵来做到这一点(是的,它很奇怪.只需滚动它).在我们的例子中,这个矩阵需要有三列和两行,因为它们有两个面和它们之间的间隙.由于我们稍后将替换矩阵中的数据,我们将使用zeroGrobs 创建一个:

mat   <- matrix(vector("list", length = 6), nrow = 2)
mat[] <- list(zeroGrob())

# The separator for the facets has zero width
res <- gtable_matrix("toprow", mat, unit(c(1, 0, 1), "null"), unit(c(1, 1), "null"))
Run Code Online (Sandbox Code Playgroud)

掩模分两步创建,覆盖第一个构面组,然后覆盖第二个构面组.在第一部分中,我们使用我们之前记录的位置从原始图中抓取适当的grob并将其添加到替换矩阵的顶部res,跨越整个长度.然后我们在我们的情节之上添加该矩阵.

# Adding the first layer
zz <- res %>%
  gtable_add_grob(z$grobs[[locations[1]]]$grobs[[1]], 1, 1, 1, 3) %>%
  gtable_add_grob(z, ., t = top,  l = l[1],  b = top,  r = r[1], name = c("add-strip"))

# Adding the second layer (note the indices)
pp <- gtable_add_grob(res, z$grobs[[locations[3]]]$grobs[[1]], 1, 1, 1, 3) %>%
  gtable_add_grob(zz, ., t = top,  l = l[2],  b = top,  r = r[2], name = c("add-strip"))

# Plotting
grid.newpage()
print(grid.draw(pp))
Run Code Online (Sandbox Code Playgroud)

嵌套的facet标签

  • 非常感谢您的解决方案.我一直在努力弄清楚如何将这个解决方案概括为修改facet标签,如果它们出现在图的右侧.您能否展示如何修改您的解决方案以适应布局基于绘图函数`ggplot(cbind(df,df),aes(x = x,y = y))+ geom_point()+ facet_grid( f1 + f2~f3)`?我还需要推广一个案例,其中12个方面行减少到6个外部右边标签而不是4和2(因为我提供的重写函数会生成).如果有帮助,很高兴提供一个明确的例子.非常感谢! (3认同)