在ggplot条形图中添加图像背景,使图像仅在条形内部可见。

5

我希望在R中使用ggplot2创建一个柱状图,使得柱子是透明的,以便背景图片可以显示出来,而图表的其余部分则是不透明的并覆盖在背景图片上。

我可以像下面演示的那样将一个图片添加到背景中,但我找不到一种方法只在柱内显示背景图片。实质上,我希望创建与我现在所拥有的相反的效果。

library(ggplot2)
library(jpeg)
library(grid)
library(scales)

montage <- readJPEG("AcanthMontage.jpg")
mont <- rasterGrob(montage, 
                   width = unit(1,"npc"), 
                   height = unit(1,"npc"))

montplot <- ggplot(frequencyDF, aes(x=depth, y= perLiter)) + 
  annotation_custom(mont, -Inf, Inf, -Inf, Inf) +
  scale_fill_continuous(guide = FALSE) +
  geom_bar(stat = "identity", color="black", fill="white", alpha=0.5) + 
  coord_flip() + 
  scale_y_continuous(limits= c(0,1.25), expand = c(0, 0)) + 
  scale_x_continuous(limits= c(-1000,0), expand = c(0,0)) + 
  theme_bw() + 
  theme(text=element_text(size=16)) + 
  xlab("Depth (m)") + 
  ylab("Cells per Liter")

montplot

ggplot with image as background


4
有趣。从一开始就说,我不知道怎么做,但作为情节的制作者和消费者,我必须评论这是非常繁忙和分散注意力的,甚至可能违背Tufte的理念。你特别想要这种布局有什么原因吗?(我确实理解你的意思,但还是有点奇怪...我认为我的眼睛会试图赋予某些条形图案以意义。) - r2evans
@r2evans 感谢您的评论!我同意图表变得太繁忙了。蒙太奇中的图像是实际计数的细胞,用于创建“每升细胞”计数。我认为将细胞排列在条形图内部看起来很好,就像一个象形图一样,但是,由于每个条形图内部的细胞数量与每升细胞数量不直接相关,所以我理解这会产生误导。本质上,我试图为海报调整图表并在较小的空间内包含更多信息 - 细胞长什么样子以及有多少个。 - MaggiB
我理解了。 "Poster" 表示您的用法,尽管可能仍然很忙,但更有意义。 - r2evans
2个回答

4

这让我想起了一个类似的问题此处,在那个问题中,被接受的解决方案使用 geom_ribbon()提供了遮罩层。

沿着类似的思路,由于在这种情况下需要遮罩单个条形图,所以我们正在寻求创建一个能够优雅地处理孔洞的多边形图层。据我所知, geom_polygon 表现不是很好,但ggpolypath包中的 geom_polypath可以做到。

可重复示例,使用R标志作为示例图像和内置数据框:

library(ggplot2)
library(grid)
library(jpeg)

montage <- readJPEG(system.file("img", "Rlogo.jpg", package="jpeg"))
mont <- rasterGrob(montage, width = unit(1,"npc"), 
                   height = unit(1,"npc"))

p <- ggplot(mpg, aes(x = class)) +
  annotation_custom(mont, -Inf, Inf, -Inf, Inf) +
  geom_bar(color = "black", fill = NA) +
  coord_flip() +
  theme_bw()

p

plot 1

创建一个坐标数据框以用于遮罩层:
library(dplyr)
library(tidyr)

# convert the xmin/xmax/ymin/ymax values for each bar into
# x/y coordinates for a hole in a large polygon,
# then add coordinates for the large polygon

new.data <- layer_data(p, 2L) %>%  

  select(ymin, ymax, xmin, xmax) %>%
  mutate(group = seq(1, n())) %>%
  group_by(group) %>%
  summarise(coords = list(data.frame(x = c(xmin, xmax, xmax, xmin),
                                     y = c(ymin, ymin, ymax, ymax),
                                     order = seq(1, 4)))) %>%
  ungroup() %>%
  unnest() %>%

  rbind(data.frame(group = 0,
                   x = c(-Inf, Inf, Inf, -Inf),
                   y = c(-Inf, -Inf, Inf, Inf),
                   order = seq(1, 4)))

> new.data
# A tibble: 32 x 4
   group     x     y order
   <dbl> <dbl> <dbl> <int>
 1     1  0.55     0     1
 2     1  1.45     0     2
 3     1  1.45     5     3
 4     1  0.55     5     4
 5     2  1.55     0     1
 6     2  2.45     0     2
 7     2  2.45    47     3
 8     2  1.55    47     4
 9     3  2.55     0     1
10     3  3.45     0     2
# ... with 22 more rows

添加遮罩层:
library(ggpolypath)

p +
  geom_polypath(data = new.data,
                aes(x = x, y = y, group = group),
                inherit.aes = FALSE, 
                rule = "evenodd",
                fill = "white", color = "black")

plot 2

顺便说一句,“仅仅因为你能做到,并不意味着你应该这样做”的古老格言可能适用于这里...


谢谢@Z.Lin!这完美地解决了问题。我同意这可能不是展示信息的最佳方式,美学上也不是。最初的想法是通过在条形图中显示单元格图像以指示其密度来节省空间并可能吸引人眼球--有点像象形文字。我将继续思考最好的图像,但同时感谢您的帮助! - MaggiB

0
相当有趣的问题。我加入@r2evans的担忧,这可能会产生一个混乱的图形,可能会传达错误的解释和条形图中的模式。
如果这是一个图像编辑问题,您正在寻找一个掩码 - 条形是过滤背景图像的掩码。包raster有一个函数可以实现这个功能,但我不确定如何将其与ggplot2结合使用。
ggplot2使用grid包将元素呈现到图形设备上(即屏幕、窗口、pdf等)。在这里,您可以将每个条形图与视口组合起来创建遮罩效果。首先停止的地方将是从ggplot2对象渲染ggplotGrob对象(有效地ggplotGrob(p),其中p <- ggplot(...) + ...)。

关于视口的更多信息,请参见Zhou 2010Murell 2018


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