Wenn Sie ein benutzerdefiniertes Bild zu geom_polygon hinzufügen, füllen Sie ggplot aus

72

Ich wurde von einem Schüler gefragt, ob es möglich sei, ein Diagramm ähnlich dem folgenden mit R neu zu erstellen:

Geben Sie hier die Bildbeschreibung ein Dies ist aus diesem Papier ....

Diese Art von Sachen ist nicht meine Spezialität, aber mit dem folgenden Code konnte ich 95% CI-Ellipsen erstellen und mit ihnen zeichnen geom_polygon(). Ich habe die Bilder mit Bildern gefüllt, die ich mit dem rphylopicPaket aus der Phylopic-Bibliothek geholt habe .

#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()

Welches gibt das folgende:

Geben Sie hier die Bildbeschreibung ein

Das ist in Ordnung, aber ich würde wirklich gerne ein Bild direkt zum Befehl 'fill' von geom_polygon hinzufügen. Ist das möglich ?

Jalapic
quelle
3
Ich nehme an, die offizielle Antwort lautet "es ist nicht möglich" ( Hadleys Antwort ). Allerdings gibt es eine neuere Antwort von @baptiste hier , die hilfreich sein könnten.
Tonytonov
Dies ist nicht das, was Sie fragen, sondern im Sinne von "das richtige Werkzeug für den richtigen Job": Ich würde das zugrunde liegende Diagramm in R mit Daten dahinter erstellen. Ich würde dann entweder Photoshop verwenden oder es ist kostenloses Open-Source- GIMP , das nahezu gleichwertig ist . Erstellen Sie dann verschiedene Ebenen und passen Sie deren Transparenz an, um die ovale Form durchzulassen.
Mike Williamson

Antworten:

19

Wir können keine Musterfüllung für ggplot festlegen, aber wir können mit Hilfe von eine recht einfache Problemumgehung vornehmen geom_tile. Reproduzieren Ihrer ursprünglichen Daten:

#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))
}

Das Hauptmerkmal ich zeigen möchte , ist die Umwandlung eines Rasterbildes in data.framemit Spalten X, Y, colorso können wir später plotten es mitgeom_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))
}

Bilder konvertieren:

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

Soweit ich weiß, möchte der Autor Bilder nur innerhalb von Ellipsen zeichnen, nicht in ihrer ursprünglichen rechteckigen Form. Wir können es mit Hilfe der point.in.polygonFunktion aus dem Paket erreichen sp.

# 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,]

Und schlussendlich...

#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()

Geben Sie hier die Bildbeschreibung ein

Wir können die Größe des Plots leicht ändern, ohne Änderungen am Code vorzunehmen.

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Und natürlich sollten Sie die Leistungsfähigkeiten Ihres Computers berücksichtigen und wahrscheinlich keine 20-Megapixel-Bilder zum Plotten in Ihrem ggplot=) auswählen.

inscaven
quelle
Beeindruckend! Ich verfolge diesen Thread immer wieder, seit ich die Frage zum ersten Mal gestellt habe - ich war mir nicht sicher, ob wir eine Antwort bekommen würden. Vielen Dank! Ich liebe die point.in.polygonFunktion. !
Jalapic
4

Eine schnelle und hässliche Lösung ohne Verwendung ggplotkönnte sein, zu verwenden rasterImagerund die package(jpg)(oder png, abhängig vom Format Ihrer Bilder):

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

(Die Bilder sind "zufällige" Bilder auf Wikimedia, die für diesen Anlass umbenannt wurden.)

Hier habe ich das Bild einfach auf den Mittelwert jeder Gruppe zentriert (wie im Artikel) und ihre Größe proportional zur Standardabweichung gemacht. Es wird nicht schwierig sein, es an das im Artikel verwendete 95% -Konfidenzintervall anzupassen.

Es ist nicht genau das benötigte Ergebnis, aber es ist ziemlich einfach (obwohl ich eher zu einer Gimp-Lösung gehen würde, wenn Sie Ihr Bild wirklich an die Ellipse anpassen möchten, wie von @Mike vorgeschlagen).

imageRaster

Simon C.
quelle
-4
#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)
Ankush Dapke
quelle
Ich bin mir über den Inhalt nicht sicher, aber dies muss formatiert werden.
Halvor Holsten Strand