Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add a transparent window/keyhole ggplot2 (grid)

Tags:

r

ggplot2

r-grid

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

like image 660
Tyler Rinker Avatar asked Jan 26 '14 21:01

Tyler Rinker


People also ask

How do you make an image transparent in R?

Method 1: Using rect with theme() In this approach, after the plot has been created normally, theme() function is added to it with rect parameter. To rect, element_rect() function is passed with parameter fill set to transparent.

How do I get rid of the GREY grid in ggplot2?

To remove a particular panel grid, use element_blank() for the corresponding theme argument. For example to remove the major grid lines for the x axis, use this: p + theme(panel.


2 Answers

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

like image 93
jlhoward Avatar answered Sep 20 '22 21:09

jlhoward


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

like image 38
Ricardo Saporta Avatar answered Sep 19 '22 21:09

Ricardo Saporta