2016-10-29 34 views
5

我遇到了一种情况,我想创建一个由三个分组变量构成的图。要做到这一点,我只需使用facet_grid(f1 ~ f2 + f3),但问题在于f2的标签是多余的,并且它们会跨越f2中嵌套的f3更好。ggplot2生成组中的嵌套构面

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) 

MWE of nested facet plot

同样,我期待让他们不那么多余的标签用于f结合起来。

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

回答

9

答案在gridgtable包内。情节中的一切都按特定的顺序排列,如果你挖一点,你可以找到所有的东西。

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

# First get the grob 
z <- ggplotGrob(p) 

这个操作的最终目标是覆盖在顶部面的标签,但诀窍是,这两个方面对格栅间距相同的行存在。他们是表内的表(看名称为“带”行,也注意到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] 

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

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] 

对于每个GROB,我们有一个对象,该目录(其中它的绘制顺序(ž),在网格中的位置(细胞),标签名称)和几何图形(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)] 

一旦我们有了职位,我们需要创建一个替换表。我们可以用列表矩阵来做到这一点(是的,这很奇怪,只是滚动它)。这个矩阵需要在我们的例子中有三列和两行,因为这两个方面和它们之间的差距。因为我们只是要在矩阵以后更换的数据,我们要创建一个与zeroGrob S:分两步创建

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")) 

的面具,覆盖第一面组,然后第二个。在第一部分中,我们使用前面记录的位置从原始图中获取适当的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)) 

Nested facet labels

+1

非常感谢您的解决方案。我一直在努力研究如何推广这个解决方案来修改方面标签,如果它们出现在情节的右侧。你能说明一个人如何修改你的解决方案以适应布局基于plot函数'ggplot(cbind(df,df),aes(x = x,y = y))+ geom_point()+ facet_grid( f1 + f2〜f3)'?我还需要推广到一个案例,其中12个方面行减少到6个外部右侧标签,而不是4和2(因为我提供的重写函数会产生)。如果有帮助,很乐意提供一个清晰的例子。非常感谢! – nickb