【问题标题】:Adding custom image to geom_polygon fill in ggplot将自定义图像添加到 geom_polygon 填充 ggplot
【发布时间】:2015-03-28 04:48:44
【问题描述】:

一位学生问我是否可以使用 R 重新创建类似于下面的情节:

这是来自this paper....

这类东西不是我的专长,但使用以下代码,我能够创建 95% 的 CI 椭圆并用 geom_polygon() 绘制它们。我使用 rphylopic 包从系统发育库中抓取的图像填充了图像。

#example data/ellipses
set.seed(101)
n <- 1000
x1 <- rnorm(n, mean=2)
y1 <- 1.75 + 0.4*x1 + rnorm(n)
df <- data.frame(x=x1, y=y1, group="A")
x2 <- rnorm(n, mean=8)
y2 <- 0.7*x2 + 2 + rnorm(n)
df <- rbind(df, data.frame(x=x2, y=y2, group="B"))
x3 <- rnorm(n, mean=6)
y3 <- x3 - 5 - rnorm(n)
df <- rbind(df, data.frame(x=x3, y=y3, group="C"))


#calculating ellipses
library(ellipse)
df_ell <- data.frame()
for(g in levels(df$group)){
  df_ell <- rbind(df_ell, cbind(as.data.frame(with(df[df$group==g,], ellipse(cor(x, y), 
                                                                             scale=c(sd(x),sd(y)), 
                                                                             centre=c(mean(x),mean(y))))),group=g))
}
#drawing
library(ggplot2)
p <- ggplot(data=df, aes(x=x, y=y,colour=group)) + 
  #geom_point(size=1.5, alpha=.6) +
  geom_polygon(data=df_ell, aes(x=x, y=y,colour=group, fill=group), alpha=0.1, size=1, linetype=1)


### get center points of ellipses
library(dplyr)
ell_center <- df_ell %>% group_by(group) %>% summarise(x=mean(x), y=mean(y))

### animal images
library(rphylopic)
lion <- get_image("e2015ba3-4f7e-4950-9bde-005e8678d77b", size = "512")[[1]]
mouse <- get_image("6b2b98f6-f879-445f-9ac2-2c2563157025", size="512")[[1]] 
bug <- get_image("136edfe2-2731-4acd-9a05-907262dd1311", size="512")[[1]]

### overlay images on center points
p + add_phylopic(lion, alpha=0.9, x=ell_center[[1,2]], y=ell_center[[1,3]], ysize=2, color="firebrick1")  + 
    add_phylopic(mouse, alpha=1, x=ell_center[[2,2]], y=ell_center[[2,3]], ysize=2, color="darkgreen") +
    add_phylopic(bug, alpha=0.9, x=ell_center[[3,2]], y=ell_center[[3,3]], ysize=2, color="mediumblue") + 
  theme_bw()

这给出了以下内容:

这没关系,但我真正想做的是将图像直接添加到 geom_polygon 的“填充”命令中。这可能吗 ?

【问题讨论】:

  • 我想官方的回答是“不可能”(Hadley's answer)。但是,@baptiste here 的最新回答可能会有所帮助。
  • 这不是您要问的,而是本着“正确工作的正确工具”的精神:我将在 R 中制作基础图,并在其背后提供数据。然后我会使用 Photoshop,或者它是免费的、开源的,几乎等同于 GIMP。然后创建不同的图层并调整它们的透明度,让椭圆形通过。

标签: r ggplot2


【解决方案1】:

我们不能为 ggplot 设置图案填充,但我们可以在 geom_tile 的帮助下做一个非常简单的解决方法。复制您的初始数据:

#example data/ellipses
set.seed(101)
n <- 1000
x1 <- rnorm(n, mean=2)
y1 <- 1.75 + 0.4*x1 + rnorm(n)
df <- data.frame(x=x1, y=y1, group="A")
x2 <- rnorm(n, mean=8)
y2 <- 0.7*x2 + 2 + rnorm(n)
df <- rbind(df, data.frame(x=x2, y=y2, group="B"))
x3 <- rnorm(n, mean=6)
y3 <- x3 - 5 - rnorm(n)
df <- rbind(df, data.frame(x=x3, y=y3, group="C"))

#calculating ellipses
library(ellipse)
df_ell <- data.frame()
for(g in levels(df$group)){
  df_ell <- 
    rbind(df_ell, cbind(as.data.frame(
      with(df[df$group==g,], ellipse(cor(x, y), scale=c(sd(x),sd(y)), 
                                     centre=c(mean(x),mean(y))))),group=g))
}

我要展示的关键功能是将光栅图像转换为 data.frameXYcolor 以便我们以后可以使用 geom_tile 绘制它

require("dplyr")
require("tidyr")
require("ggplot2")
require("png")

# getting sample pictures
download.file("http://content.mycutegraphics.com/graphics/alligator/alligator-reading-a-book.png", "alligator.png", mode = "wb")
download.file("http://content.mycutegraphics.com/graphics/animal/elephant-and-bird.png", "elephant.png", mode = "wb")
download.file("http://content.mycutegraphics.com/graphics/turtle/girl-turtle.png", "turtle.png", mode = "wb")
pic_allig <- readPNG("alligator.png")
pic_eleph <- readPNG("elephant.png")
pic_turtl <- readPNG("turtle.png")

# converting raster image to plottable data.frame
ggplot_rasterdf <- function(color_matrix, bottom = 0, top = 1, left = 0, right = 1) {
  require("dplyr")
  require("tidyr")

  if (dim(color_matrix)[3] > 3) hasalpha <- T else hasalpha <- F

  outMatrix <- matrix("#00000000", nrow = dim(color_matrix)[1], ncol = dim(color_matrix)[2])

  for (i in 1:dim(color_matrix)[1])
    for (j in 1:dim(color_matrix)[2]) 
      outMatrix[i, j] <- rgb(color_matrix[i,j,1], color_matrix[i,j,2], color_matrix[i,j,3], ifelse(hasalpha, color_matrix[i,j,4], 1))

  colnames(outMatrix) <- seq(1, ncol(outMatrix))
  rownames(outMatrix) <- seq(1, nrow(outMatrix))
  as.data.frame(outMatrix) %>% mutate(Y = nrow(outMatrix):1) %>% gather(X, color, -Y) %>% 
    mutate(X = left + as.integer(as.character(X))*(right-left)/ncol(outMatrix), Y = bottom + Y*(top-bottom)/nrow(outMatrix))
}

转换图像:

# preparing image data
pic_allig_dat <- 
  ggplot_rasterdf(pic_allig, 
                  left = min(df_ell[df_ell$group == "A",]$x), 
                  right = max(df_ell[df_ell$group == "A",]$x),
                  bottom = min(df_ell[df_ell$group == "A",]$y),
                  top = max(df_ell[df_ell$group == "A",]$y) )

pic_eleph_dat <- 
  ggplot_rasterdf(pic_eleph, left = min(df_ell[df_ell$group == "B",]$x), 
                  right = max(df_ell[df_ell$group == "B",]$x),
                  bottom = min(df_ell[df_ell$group == "B",]$y),
                  top = max(df_ell[df_ell$group == "B",]$y) )

pic_turtl_dat <- 
  ggplot_rasterdf(pic_turtl, left = min(df_ell[df_ell$group == "C",]$x), 
                  right = max(df_ell[df_ell$group == "C",]$x),
                  bottom = min(df_ell[df_ell$group == "C",]$y),
                  top = max(df_ell[df_ell$group == "C",]$y) )

据我所知,作者只想在椭圆内绘制图像,而不是原始矩形。我们可以借助 sp 包中的 point.in.polygon 函数来实现它。

# filter image-data.frames keeping only rows inside ellipses
require("sp")

gr_A_df <- 
  pic_allig_dat[point.in.polygon(pic_allig_dat$X, pic_allig_dat$Y, 
                                 df_ell[df_ell$group == "A",]$x, 
                                 df_ell[df_ell$group == "A",]$y ) %>% as.logical,]
gr_B_df <- 
  pic_eleph_dat[point.in.polygon(pic_eleph_dat$X, pic_eleph_dat$Y, 
                                 df_ell[df_ell$group == "B",]$x, 
                                 df_ell[df_ell$group == "B",]$y ) %>% as.logical,]
gr_C_df <- 
  pic_turtl_dat[point.in.polygon(pic_turtl_dat$X, pic_turtl_dat$Y, 
                                 df_ell[df_ell$group == "C",]$x, 
                                 df_ell[df_ell$group == "C",]$y ) %>% as.logical,]

最后……

#drawing
p <- ggplot(data=df) + 
  geom_polygon(data=df_ell, aes(x=x, y=y,colour=group, fill=group), alpha=0.1, size=1, linetype=1)

p + geom_tile(data = gr_A_df, aes(x = X, y = Y), fill = gr_A_df$color) + 
  geom_tile(data = gr_B_df, aes(x = X, y = Y), fill = gr_B_df$color) + 
  geom_tile(data = gr_C_df, aes(x = X, y = Y), fill = gr_C_df$color) + theme_bw()

我们可以在不更改代码的情况下轻松调整绘图大小。

当然,您应该牢记您机器的性能,并且可能不要选择 20MP 图片在您的ggplot 中进行绘图 =)

【讨论】:

  • 哇!自从我第一次提出这个问题以来,我一直在关注这个话题——不确定我们会得到答案。谢谢!喜欢point.in.polygon 功能。 !
【解决方案2】:

不使用ggplot 的快速而丑陋的解决方案可能是使用rasterImagerpackage(jpg)(或png,取决于图像的格式):

set.seed(101)
n <- 1000
x1 <- rnorm(n, mean=2)
y1 <- 1.75 + 0.4*x1 + rnorm(n)
df <- data.frame(x=x1, y=y1, group="1")
x2 <- rnorm(n, mean=8)
y2 <- 0.7*x2 + 2 + rnorm(n)
df <- rbind(df, data.frame(x=x2, y=y2, group="2"))
x3 <- rnorm(n, mean=6)
y3 <- x3 - 5 - rnorm(n)
df <- rbind(df, data.frame(x=x3, y=y3, group="3"))

plot(df$x,df$y,type="n")
for(g in unique(df$group)){
    ifile=readJPEG(paste(g,".jpg",sep=""),FALSE)
    x=df$x[df$group == g]
    y=df$y[df$group == g]
    xmin=mean(x)-sd(x)*2
    ymin=mean(y)-sd(y)*2
    xmax=mean(x)+sd(x)*2
    ymax=mean(y)+sd(y)*2
    rasterImage(ifile,xmin,ymin,xmax,ymax)
}

(这些图片是在维基媒体上找到的“随机”图片,为当时的情况重命名)

在这里,我只是将图像集中在每组的平均值上(如文章中所示),并使它们的大小与标准偏差成正比。使其符合文章中使用的 95% 置信区间并不难。

这不是完全需要的结果,但它很容易做到(尽管如果您真的想按照@Mike 的建议将图像适合椭圆,我会更多地使用 gimp 解决方案)

【讨论】:

    【解决方案3】:
    #example data/ellipses set.seed(101) n <- 1000 x1 <- rnorm(n, mean=2) y1 <- 1.75 + 0.4*x1 + rnorm(n) df <- data.frame(x=x1, y=y1,
        group="A") x2 <- rnorm(n, mean=8) y2 <- 0.7*x2 + 2 + rnorm(n) df <-
        rbind(df, data.frame(x=x2, y=y2, group="B")) x3 <- rnorm(n, mean=6)
        y3 <- x3 - 5 - rnorm(n) df <- rbind(df, data.frame(x=x3, y=y3,
        group="C"))
    
    
    #calculating ellipses library(ellipse) df_ell <- data.frame() for(g in levels(df$group)){
      df_ell <- rbind(df_ell,
        cbind(as.data.frame(with(df[df$group==g,], ellipse(cor(x, y),                                                                            
       scale=c(sd(x),sd(y)),                                                                                  
       centre=c(mean(x),mean(y))))),group=g)) }
    
    #drawing library(ggplot2) p <- ggplot(data=df, aes(x=x, y=y,colour=group)) +    
        #geom_point(size=1.5, alpha=.6) +  
        geom_polygon(data=df_ell, aes(x=x, y=y,colour=group, fill=group),
        alpha=0.1, size=1, linetype=1)
    

    【讨论】:

    • 我不确定内容,但这需要格式化。
    猜你喜欢
    • 1970-01-01
    • 2021-01-05
    • 1970-01-01
    • 2016-03-02
    • 2021-11-21
    • 2017-07-17
    • 2014-05-31
    • 2012-10-01
    • 2014-07-29
    相关资源
    最近更新 更多