Curve fill area based on value

We are trying to make a plot with ggplot2, where the positive areas above the x axis are one color and the negative areas are different.

Given this dataset, I would like the region graph to be shaded with different colors on each side of the axis.

I see a way to divide the data set into two subsets: one positive, where all negative values ​​are zero, and one negative with all positive zero values, and then build them separately on the same axis, but it would seem to be more similar there on ggplot way.

The solution published in this question does not give exact results (see below).

Sample data that accurately displays as a bar graph

Raw data

Generated by the following code:

# create some fake data with zero-crossings yvals=c(2,2,-1,2,2,2,0,-1,-2,2,-2) test = data.frame(x=seq(1,length(yvals)),y=yvals) # generate the bar plot ggplot(data=test,aes(x=x,y=y)) + geom_bar(data=test[test$y>0,],aes(y=y), fill="blue",stat="identity", width=.5) + geom_bar(data=test[test$y<0,],aes(y=y), fill="red",stat="identity", width=.5) 

RLE approach is not common

The RLE approach proposed in another question creates artifacts associated with null intersections when applied to our dataset:

Ribbon RLE plot

Generated by the following code ( do not use ):

 # set up grouping function rle.grp <- function(x) { xx <- rle(x) xx$values = seq_along(xx$values) inverse.rle(xx) } # generate ribbon plot ggplot(test, aes(x=x,y=y,group = factor(rle.grp(sign(y))))) + geom_ribbon(aes(ymax = pmax(0,y),ymin = pmin(0,y), fill = factor(sign(y), levels = c(-1,0,1), labels = c('-','0','+')))) + scale_fill_brewer(name = 'sign', palette = 'RdBu') 

See the final answer below suggested by @baptiste and Kohske.

+6
source share
2 answers

For @baptiste's comment (since deleted), I would say that this is the best answer. This is based on this post by Kohske. It adds new xy pairs to the dataset when crossing zero and creates a graph below:

 # create some fake data with zero-crossings yvals = c(2,2,-1,2,2,2,0,-1,-2,2,-2) d = data.frame(x=seq(1,length(yvals)),y=yvals) rx <- do.call("rbind", sapply(1:(nrow(d)-1), function(i){ f <- lm(x~y, d[i:(i+1),]) if (f$qr$rank < 2) return(NULL) r <- predict(f, newdata=data.frame(y=0)) if(d[i,]$x < r & r < d[i+1,]$x) return(data.frame(x=r,y=0)) else return(NULL) })) d2 <- rbind(d,rx) ggplot(d2,aes(x,y)) + geom_area(data=subset(d2, y<=0), fill="pink") + geom_area(data=subset(d2, y>=0), fill="lightblue") + geom_point() 

Generates the following output: example plot

+11
source

I made a pretty similar story using the following easy-to-understand logic. I created the following two objects for positive and negative values. Note that there is a "very small number" to avoid these transitions from one point to another without going through zeros.

 pos <- mutate(df, y = ifelse(ROI >= 0, y, 0.0001)) neg <- mutate(df, y = ifelse(ROI < 0, y, -0.0001)) 

Then just add geom_area to your ggplot object:

 ggplot(..., aes(y = y)) + geom_area(data = pos, fill = "#3DA4AB") + geom_area(data = neg, fill = "tomato") 

Hope this works for you! ;)

0
source

Source: https://habr.com/ru/post/950671/


All Articles