Fill area between two lines, with high/low and dates

前端 未结 3 534

Forword: I provide a reasonably satisfactory answer to my own question. I understand this is acceptable practice. Naturally my hope is to invite suggestions and improvements.

3条回答
  •  遇见更好的自我
    2021-02-03 13:25

    I actually had the same question some time ago and here is the related post. It defines a function finding the intersections between two lines and an other function which takes a dataframe in input and then colors the space between the two columns using matplotand polygon

    EDIT

    Here is the code, modified a bit to allow the last polygon to be plotted

    set.seed(123456789)
    dat <- data.frame(
    Date  = seq.Date(as.Date("1950-01-01"), by = "1 month", length.out = 12*10),
    Stocks = 100 + c(0, cumsum(runif(12*10-1, -30, 30))),
    Bonds = 100 + c(0, cumsum(runif(12*10-1, -5, 5))))
    
    intersects <- function(x1, x2) {
        seg1 <- which(!!diff(x1 > x2))     # location of first point in crossing segments
        above <- x2[seg1] > x1[seg1]       # which curve is above prior to crossing
        slope1 <- x1[seg1+1] - x1[seg1]
        slope2 <- x2[seg1+1] - x2[seg1]
        x <- seg1 + ((x2[seg1] - x1[seg1]) / (slope1 - slope2))
        y <- x1[seg1] + slope1*(x - seg1)
        data.frame(x=x, y=y, pindex=seg1, pabove=(1:2)[above+1L]) 
     # pabove is greater curve prior to crossing
    }
    
    fillColor <- function(data, addLines=TRUE) {
    ## Find points of intersections
    ints <- intersects(data[,2], data[,3]) # because the first column is for Dates
    intervals <- findInterval(1:nrow(data), c(0, ints$x))
    
    ## Make plot
    matplot(data, type="n", col=2:3, lty=1, lwd=4,xaxt='n',xlab='Date')
    axis(1,at=seq(1,dim(data)[1],length.out=12),
    labels=data[,1][seq(1,dim(data)[1],length.out=12)])
    legend("topright", c(colnames(data)[2], colnames(data)[3]), col=3:2, lty=1, lwd=2)
    
    ## Draw the polygons
    for (i in seq_along(table(intervals))) {
        xstart <- ifelse(i == 1, 0, ints$x[i-1])
        ystart <- ifelse(i == 1, data[1,2], ints$y[i-1])
        xend <- ints$x[i]
        yend <- ints$y[i]
        x <- seq(nrow(data))[intervals == i]
        polygon(c(xstart, x, xend, rev(x)), c(ystart, data[x,2], yend, rev(data[x,3])),
    col=ints$pabove[i]%%2+2)
    }
    
    # add end of plot
    
    xstart <- ints[dim(ints)[1],1]
    ystart <- ints[dim(ints)[1],2]
    xend <- nrow(data)
    yend <- data[dim(data)[1],2]
    x <- seq(nrow(data))[intervals == max(intervals)]
    polygon(c(xstart, x, xend, rev(x)), c(ystart, data[x,2], yend, rev(data[x,3])),
    col=ints[dim(ints)[1]-1,4]%%2+2)
    
    ## Add lines for curves
    if (addLines)
        invisible(lapply(1:2, function(x) lines(seq(nrow(data)), data[,x], col=x%%2+2, lwd=2)))
    }
    
    ## Plot the data
    fillColor(dat,FALSE)
    

    and the final result is this (with the same data used for the question)

提交回复
热议问题