Search code examples
rclip

Is a multiple clip on a plot possible?


I am editing this to provide a better example of what I need. I will keep the original message at the bottom in case that helps.

I have the following data:

x=c(1,2,7,3,4,8,9,5,6,7,11,13,15,8,9,10,11,12,13,15)
y=c(2:10,9,8,7,6,8,10,11,12,13,14,1)
date=strptime(20010101:20010120,'%Y%m%d')
z=data.frame(date,x,y)
z$diff=z$y-z$x
z$min=pmin(x,y)
z$max=pmax(x,y)

So my data is this:

         date  x  y diff min max
1  2001-01-01  1  2    1   1   2
2  2001-01-02  2  3    1   2   3
3  2001-01-03  7  4   -3   4   7
4  2001-01-04  3  5    2   3   5
5  2001-01-05  4  6    2   4   6
6  2001-01-06  8  7   -1   7   8
7  2001-01-07  9  8   -1   8   9
8  2001-01-08  5  9    4   5   9
9  2001-01-09  6 10    4   6  10
10 2001-01-10  7  9    2   7   9
11 2001-01-11 11  8   -3   8  11
12 2001-01-12 13  7   -6   7  13
13 2001-01-13 15  6   -9   6  15
14 2001-01-14  8  8    0   8   8
15 2001-01-15  9 10    1   9  10
16 2001-01-16 10 11    1  10  11
17 2001-01-17 11 12    1  11  12
18 2001-01-18 12 13    1  12  13
19 2001-01-19 13 14    1  13  14
20 2001-01-20 15  1  -14   1  15

I want to create a polygon plot where the color of the polygon changes based on when z$diff is less than zero. So the plot should look like this:

Polygon plot with different color based on condition

I know segments can do this with lines, but unfortunately for me I need to do it with a polygon.

Original message:

Let's say I have this data:

x=rnorm(100)
y=rnorm(100)
date=strptime(20010101:20010410,'%Y%m%d')
date=date[complete.cases(date)]
z=data.frame(date,x,y)
z$max=apply(z[2:3],1,which.max)
z$min=apply(z[2:3],1,which.min)
z$v=z$max-z$min
w=z[z$v<0,]

Then I try to make a polygon consisting of two colors, one for when x>y, and another for when y>x. I do this:

plot(z$date,z$x,type='n')
polygon(c(z$date,z$date[nrow(z):1]),c(z$x,z$y[nrow(z):1]),col='skyblue',border=NA)
polygon(c(w$date,w$date[nrow(w):1]),c(w$x,w$y[nrow(w):1]),col='salmon',border=NA)

What happens is that when there are gaps in data frame w the polygon covers those gaps. I know how to use clip to clip one region, but can it be used to clip multiple gaps in a data frame?

Ideally the w polygon should overlap on the z polygon whenever y>x.


Solution

  • It is possible to separate polygons by a line in the data consisting only of NA.

    library(reshape2)
    library(ggplot2)
    
    z <- data.frame(
        date=date,
        min=pmin(x, y),
        max=pmax(x, y),
        series=ifelse(x>y, 1, 2)
    )
    
    # Helper function to create closed polygon, optionally adding NA line at bottom
    rdata <- function(dat, addNA=FALSE){
      rdat <- dat[nrow(dat):1, ]
      ret <- rbind(
          data.frame(x= dat$date, y= dat$max, series= dat$series), 
          data.frame(x=rdat$date, y=rdat$min, series=rdat$series)
      )
      if(addNA) ret <- rbind(ret, c(NA, NA, NA))
      ret
    }
    
    # Closed polygon 1
    rz <- rdata(z)
    
    #Closed polygon 2
    z2 <- z
    rlez <- rle(z$series)$lengths
    z2$chunk <- rep(seq_along(rlez), times=rlez)
    rz2 <- ddply(z2, .(chunk), rdata, addNA=TRUE)
    rz2 <- rz2[rz2$series!=1, ]
    

    Create plot

    ggplot(rz, aes(x, y, fill="Less than")) + geom_polygon() + 
        geom_polygon(data=rz2, aes(x, y, fill="Greater than")) +
        scale_fill_discrete("Legend") +
        xlab("Date") +
        ylab("Value")
    

    enter image description here


    PS. I don't know what your data represents in real life, but my hunch is you can visualize it better (or at least as well), with much less effort if you use geom_linerange instead of polygons.

    ggplot(z, aes(x=date, ymin=min, ymax=max, colour=factor(series))) + 
        geom_linerange(size=5)
    

    enter image description here