ggplot2: 根据数据集中的变量设置facet_wrap条纹颜色

79
有没有一种方法可以根据数据框中提供的变量填充使用facet_wrap创建的facet条带呢?
示例数据:
MYdata <- data.frame(fruit = rep(c("apple", "orange", "plum", "banana", "pear", "grape")), farm = rep(c(0,1,3,6,9,12), each=6), weight = rnorm(36, 10000, 2500), size=rep(c("small", "large")))
示例图表: p1 = ggplot(data = MYdata, aes(x = farm, y = weight)) + geom_jitter(position = position_jitter(width = 0.3), aes(color = factor(farm)), size = 2.5, alpha = 1) + facet_wrap(~fruit)
我知道如何更改条带的背景颜色(例如为橙色): p1 + theme(strip.background = element_rect(fill="orange"))
有没有办法将MYdata中size变量的值传递给element_rect中的fill参数?
基本上,我想要小水果(苹果、李子、梨)的条带背景颜色是绿色,大水果(橙子、香蕉、葡萄)的条带背景颜色是红色,而不是所有条带都是同一个颜色。

1
这并不是一件容易的事情,但在另一个网站上已经有人问过了。链接 - nograpes
谢谢,nograpes。我找了一下,但没找到之前有人问过这个问题的地方。 - Dalmuti71
如果你成功运行了那段代码,应该把它作为一个答案发布。 - nograpes
我很想知道如何做到这一点,这是一个好主意。 - user1617979
6
支持这个功能,能够在主题内进行指定会更好。 - Eric Green
5个回答

71

通过一些工作,您可以将您的情节与具有正确 grobs 的虚拟 gtable 结合起来。

enter image description here

d <- data.frame(fruit = rep(c("apple", "orange", "plum", "banana", "pear", "grape")), 
                farm = rep(c(0,1,3,6,9,12), each=6), 
                weight = rnorm(36, 10000, 2500), 
                size=rep(c("small", "large")))

p1 = ggplot(data = d, aes(x = farm, y = weight)) + 
  geom_jitter(position = position_jitter(width = 0.3), 
              aes(color = factor(farm)), size = 2.5, alpha = 1) + 
  facet_wrap(~fruit)

dummy <- ggplot(data = d, aes(x = farm, y = weight))+ facet_wrap(~fruit) + 
  geom_rect(aes(fill=size), xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  theme_minimal()

library(gtable)

g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(dummy)

gtable_select <- function (x, ...) 
{
  matches <- c(...)
  x$layout <- x$layout[matches, , drop = FALSE]
  x$grobs <- x$grobs[matches]
  x
}

panels <- grepl(pattern="panel", g2$layout$name)
strips <- grepl(pattern="strip_t", g2$layout$name)
g2$layout$t[panels] <- g2$layout$t[panels] - 1
g2$layout$b[panels] <- g2$layout$b[panels] - 1

new_strips <- gtable_select(g2, panels | strips)
grid.newpage()
grid.draw(new_strips)

gtable_stack <- function(g1, g2){
  g1$grobs <- c(g1$grobs, g2$grobs)
  g1$layout <- transform(g1$layout, z= z-max(z), name="g2")
  g1$layout <- rbind(g1$layout, g2$layout)
  g1
}
## ideally you'd remove the old strips, for now they're just covered
new_plot <- gtable_stack(g1, new_strips)
grid.newpage()
grid.draw(new_plot)

2
这太[厉害了]!我已经试了好久才做到这个! - orville jackson
1
@duckertito 你本可以编辑这篇帖子,但我已经替你完成了。在当前版本的 ggplot2_2.2.1 中,你需要使用 grepl 来查找 "strip-t" 而不是 strip_t。我已经在我的编辑中进行了更改。顺便说一句,感谢你提供的好方法! - andschar
1
是的,对我也有效(使用@andrasz的更改)。不过我希望能为条纹颜色添加图例。你有什么想法吗? - mic
3
我明白了,谢谢您的选择。但是,当我运行这段代码时,面板标签没有显示出来。 - user1757654
2
我也遇到了面板标签不显示的问题。已经找到解决方案了吗? - philiporlando
显示剩余4条评论

25
如果您想要为条纹背景使用不同的填充,您可以使用ggh4x中的facets来设置更复杂的条纹,并使用strip_themed()。无需使用gtables,您的图表仍然是ggplot,所以您可以在之后添加常规层/比例尺/主题选项等。
library(ggh4x)
#> Loading required package: ggplot2

# Only colour strips in x-direction
strip <- strip_themed(background_x = elem_list_rect(fill = rainbow(7)))

# Wrap variant
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap2(~ class, strip = strip)

它也适用于网格布局,但如果您想为垂直条纹着色,则还需要在strip_themed()中设置background_y参数。
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_grid2(year ~ cyl, strip = strip)

该内容由reprex package (v2.0.1)于2023年1月4日创建

免责声明:我是ggh4x的作者


3
定义,最佳解决方案!非常好的ggplot2扩展。使用起来非常容易!现在这应该是答案了! - Ni-Ar
1
我同意。这个ggplot2扩展是一个很好的解决方案。它可以改变strip文本(例如字体、面孔、大小、颜色)和矩形(填充等)的所有元素。它也很直观。我只是希望帮助页面能在R Studio中更好地集成。(R Studio,在加载库时即使帮助选项卡中没有找到函数名称)。除此之外…完美! - ESELIA
太棒了!@teunbrand,这个套餐真是太好了,感谢你的贡献。 - Ricardo Saporta
我不明白这个回答如何与问题有任何关联,问题是根据水果的大小来给条纹颜色分层。 - undefined
很抱歉,这个答案不能满足您的需求。如果您找到了更好的方法,请随时将其发布为答案。 - undefined

5

你可以在这里找到这个问题的更新答案。

g <- ggplot_gtable(ggplot_build(p))
stripr <- which(grepl('strip-r', g$layout$name))
fills <- c("red","green","blue","yellow")
k <- 1
for (i in stripr) {
  j <- which(grepl('rect', g$grobs[[i]]$grobs[[1]]$childrenOrder))
  g$grobs[[i]]$grobs[[1]]$children[[j]]$gp$fill <- fills[k]
  k <- k+1
}
grid::grid.draw(g)

enter image description here


有人知道如何为条带颜色添加图例吗? - Max J.
1
如果您没有使用“color”或“fill”美学,您可以创建一个虚拟图例,与条带颜色相匹配,而不是数据。但这又是在修改gtable的麻烦之上的另一个麻烦。也许ggh4x提供了更简单的解决方案? - filups21
我能够通过创建一个虚拟图并通过cowplot添加它来创建一个图例,但我不知道如何直接在ggplot中实现。 - Max J.

0

我很想知道如何做到这一点,这是一个很好的主意。一个想法是独立生成每个图表,并使用不同的颜色,然后使用类似于multiplot或viewports的东西将它们并排显示 - 这需要更多的工作。

如果你想提取图例,对于这种方法你会需要它 - 这里是我之前在Hadley那里找到的一些代码。

g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  return(legend)}

看看它从图表p中提取出来的方式,然后我把它从图例中取出来。 legend <- g_legend(p) lwidth <- sum(legend$width) #如果您想基于此定义视口 p <- p + theme(legend.position="none")
然后最终画出它。
grid.newpage()
vp <- viewport(width = 1, height = 1)
#print(p, vp = vp)

submain <- viewport(width = 0.9, height = 0.9, x = 0.5, y = 1,just=c("center","top"))
print(p, vp = submain)
sublegend <- viewport(width = 0.5, height = 0.2, x = 0.5, y = 0.0,just=c("center","bottom"))
print(arrangeGrob(legend), vp = sublegend)

祝你好运


0

这并不是直接为您的facet着色,但是在这里您有另一个(非常快速和简单)的解决方案,基于两个变量的facet(size ~ fruit)而不是一个(~ fruit):

ggplot(data = MYdata, aes(x = farm, y = weight)) + 
  geom_jitter(position = position_jitter(width = 0.3), 
      aes(color = factor(farm)), size = 2.5, alpha = 1) + 
  facet_wrap(size ~ fruit)

enter image description here


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