Domanda

Sometimes it is helpful to use a technique of adding a gray semi-transparent layer to an image and then cutting a keyhole into that layer to highlight a certain portion of the image below. Here is an example from a youtube vide:

enter image description here

I do this with plots sometimes but use Inkscape to add a semitransparent layer and then use the eraser to cut a hole in that layer. This (a) looks less than professional (b) requires extra time and a separate program and (c) possible loss in quality.

I want to do it in R. I'm asking about ggplot2 because this is my tool of choice but I think any grid answer would be good (I know base probably has a very different approach).

So here is a MWE where I've added a geom_rect to show where we'd want to cut the keyhole/window:

ggplot(mtcars, aes(mpg, wt)) + 
    geom_point(size=3) +    
    geom_rect(mapping=aes(xmin=20, xmax=25, 
        ymin=3, ymax=3.3), color="black", alpha=.01)

How can I use R to make a plot that looks something like this:

enter image description here

È stato utile?

Soluzione

Turns out you can do this with grid.path(...) in grid package. Read the documentation to see how to create a path with a hole in it.

library(gridExtra)
library(ggplot2)

ggp <- ggplot(mtcars, aes(mpg, wt)) + geom_point(size=3)
grid.newpage()
grid.draw(arrangeGrob(ggp))
grid.path(c(0,0,1,1,.48,.48,.62,.62),
                    c(0,1,1,0,.43,.50,.50,.43),
                    id=rep(1:2, each=4),
                    rule="evenodd",gp=gpar(fill="black", alpha=0.6))

NB: grid.draw(...) and grid.path(...) are in the grid package; arrangeGrob(...) is in the gridExtra package. Loading gridExtra causes grid to be loaded. Thanks to @MartinBel for suggesting the edit.

In response to @BrandonBertelsen comment: grid.path(...) is agnostic about shape; you just have to supply the coordinates.

center <- c(x=0.55,y=0.48)
r      <- 0.1
circle <- do.call(rbind,lapply(seq(0,2*pi,length=36),
                               function(th)c(x=r*cos(th),y=r*sin(th))))
circle <- data.frame(circle)
circle$x <- circle$x + center["x"]
circle$y <- circle$y + center["y"]

ggp <- ggplot(mtcars, aes(mpg, wt)) + geom_point(size=3)
grid.newpage()
grid.draw(arrangeGrob(ggp))
grid.path(c(0,0,1,1,circle[,1]),
          c(0,1,1,0,circle[,2]),
          id=c(1,1,1,1,rep(2,nrow(circle))),
          rule="evenodd",gp=gpar(fill="black", alpha=0.6))

The "circle" is an ellipse because of the plot window's aspect ratio.


Further reading: It’s Not What You Draw, It’s What You Don’t Draw by Paul Murrell in the R Journal

Altri suggerimenti

How about the following?

P <- ggplot(mtcars, aes(mpg, wt)) + geom_point(size=3) 

## Set the limits for x & y
xlims <- c(20, 25)
ylims <- c(3, 3.3)


# Where P is the original plot
P + geom_rect(mapping=aes(xmin=-Inf, xmax=min(xlims), ymin=-Inf, ymax=+Inf), fill="black", alpha=.01) +
    geom_rect(mapping=aes(xmin=min(xlims), xmax=+Inf, ymin=max(ylims), ymax=+Inf), fill="black", alpha=.01) +
    geom_rect(mapping=aes(xmin=min(xlims), xmax=+Inf, ymin=-Inf, ymax=min(ylims)), fill="black", alpha=.01) +
    geom_rect(mapping=aes(xmin=max(xlims), xmax=+Inf, ymin=min(ylims), ymax=max(ylims)), fill="black", alpha=.01) 

enter image description here

require(ggplot2)

#represent some tiles based on your axes (10 x 10, by 1) deoending on resolution you want
alpha_tiles<-expand.grid(x=0:10,y=0:10,a=0.6)

#set alpha to 0 for the coordinate
alpha_tiles[alpha_tiles$x %in% 7:9 & alpha_tiles$y==7,]$a<-0

qplot(0:10,0:10, size=10, color="red") + theme_bw() +
  geom_raster(data=alpha_tiles,aes(x=x,y=y), alpha=alpha_tiles$a, fill="grey")

enter image description here

or this for a more complete answer that blanks the entire plot:

require(ggplot2)

#represent some tiles based on your axes (here 100 x 100, by 1) depending on resolution you want
resolution<-100
alpha_tiles<-expand.grid(x=0:resolution,y=0:resolution,a=0.6)

#set alpha to 0 for the coordinates to highlight
alpha_tiles[alpha_tiles$x %in% 40:70 & alpha_tiles$y %in% 70:80,]$a<-0
alpha_tiles[alpha_tiles$x %in% 10:30 & alpha_tiles$y %in% 10:25,]$a<-0

p<-qplot(0:10,0:10, size=10, color="red") + theme_bw() # background plot


qplot(0:resolution,0:resolution,geom="blank") + theme(axis.line=element_blank(),
                                     axis.text.x=element_blank(),
                                     axis.text.y=element_blank(),
                                     axis.ticks=element_blank(),
                                     axis.title.x=element_blank(),
                                     axis.title.y=element_blank(),
                                     legend.position="none",
                                     panel.background=element_blank(),
                                     panel.border=element_blank(),
                                     panel.grid.major=element_blank(),
                                     panel.grid.minor=element_blank(),
                                     plot.background=element_blank()) +
  annotation_custom(ggplotGrob(p),0,resolution,0,resolution) +
geom_raster(data=alpha_tiles,aes(x=x,y=y), alpha=alpha_tiles$a, fill="grey")

enter image description here

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top