Shading Area Between Two Lines in R

Shade region between two lines with ggplot

How about using geom_ribbon instead

ggplot(x, aes(x=x, y=twox)) + 
geom_line(aes(y = twox)) +
geom_line(aes(y = x2)) +
geom_ribbon(data=subset(x, 2 <= x & x <= 3),
aes(ymin=twox,ymax=x2), fill="blue", alpha=0.5) +
scale_y_continuous(expand = c(0, 0), limits=c(0,20)) +
scale_x_continuous(expand = c(0, 0), limits=c(0,5)) +
scale_fill_manual(values=c(clear,blue))

plot

How to color/shade the area between two lines in ggplot2?

I think it would be easier to keep the data into a wider format and then use geom_ribbon to create that shaded area:

df %>% 
as_tibble() %>%
ggplot +
geom_line(aes(Month, Model, color = 'Model')) +
geom_line(aes(Month, Observation, color = 'Observation')) +
geom_ribbon(aes(Month, ymax=`Upper Limit`, ymin=`Lower Limit`), fill="grey", alpha=0.25) +
scale_x_continuous(breaks = seq(1, 12, by = 1)) +
scale_y_continuous(breaks = seq(0, 140, by = 20)) +
scale_color_manual(values = c('Model' = 'yellow','Observation' = 'red')) +
ylab("Precipitation [mm]") +
theme_bw() +
theme(legend.title = element_blank())

Sample Image

plot() in R - how to shade an area between two vertical lines?

I'd use ggplot doing this:

    library(ggplot2)
df = cbind(1980:2019,runif(40,0,1))
df=as.data.frame(df)
ggplot() +
geom_rect(aes(xmin=1990, xmax=2001,ymin=-Inf,ymax=Inf), fill='red', alpha= 0.3)+
geom_line(data=df,aes(x=V1,y = V2), color = "darkred")+
theme_classic()

Sample Image

Shade area between two lines defined with function in ggplot

Try putting the functions into the data frame that feeds the figure. Then you can use geom_ribbon to fill in the area between the two functions.

mydata = data.frame(x=c(0:100),
func1 = sapply(mydata$x, FUN = function(x){20*sqrt(x)}),
func2 = sapply(mydata$x, FUN = function(x){50*sqrt(x)}))

ggplot(mydata, aes(x=x, y = func2)) +
geom_line(aes(y = func1)) +
geom_line(aes(y = func2)) +
geom_ribbon(aes(ymin = func2, ymax = func1), fill = "blue", alpha = .5)

Sample Image

Use ggplot2 to shade the area between two straight lines

You need to use coord_casterian instead of scale_._continous not to remove some values.

ggplot(df,aes(x=x)) + 
geom_line(aes(y=yone)) +
geom_line(aes(y=ytwo)) +
geom_ribbon(aes(ymin = yone, ymax = ytwo),fill='blue',alpha=0.5) +
geom_hline(yintercept=17) +
geom_vline(xintercept=17) +
geom_hline(yintercept=18) +
geom_vline(xintercept=18) +
coord_cartesian(xlim = c(17,18), ylim = c(17,18))

Sample Image

Additional code

df %>%
rowwise %>%
mutate(ytwo = max(17, ytwo),
yone = min(18, yone)) %>%
ggplot(aes(x=x)) +
geom_line(aes(y=yone)) +
geom_line(aes(y=ytwo)) +
geom_ribbon(aes(ymin = yone, ymax = ytwo),fill='blue',alpha=0.5) +
theme_bw() +
geom_hline(yintercept=17) +
geom_vline(xintercept=17) +
geom_hline(yintercept=18) +
geom_vline(xintercept=18) +
coord_cartesian(xlim = c(17,18), ylim = c(17,18))

Sample Image

Fill area between multiple lines in plot

Here is an approach:

a = data.frame(time = c(1:100), x = rnorm(100))
b = data.frame(time = c(1:100), y = rnorm(100))
c = data.frame(time = c(1:100), z = rnorm(100))

calculate the pmin and pmax:

min_a <- pmin(a, b, c)
max_a <- pmax(a, b, c)

construct the polygon as usual:

polygon(c(c$time, rev(c$time)), c(max_a$x ,rev(min_a$x)), col = rgb(1, 0, 0,0.5) )

Sample Image

or using ggplot:

library(tidyverse)
data.frame(a, b, c) %>% #combine the three data frames
group_by(time) %>% # group by time for next step
mutate(max = max(x, y, z), # calculate max of x, y, z in each time
min = min(x, y, z)) %>% #same as above
select(-time.1, - time.2) %>% #discard redundant columns
gather(key, value, 2:4) %>% #convert to long format so you can color by key in the geom line call
ggplot()+
geom_ribbon(aes(x = time, ymin= min, ymax = max), fill= "red", alpha = 0.3)+
geom_line(aes(x = time, y = value, color = key))

Sample Image

shading area between two lines in r

If I understand you correctly, you can get what you want by using the little-known panel.first= argument to plot.default():

plot(x,type="h", 
panel.first = {
usr <- par('usr')
rect(c(1,7), usr[3], c(3,10), usr[4], col='green', border=NA)
})

Or, to avoid any mucking around with par('usr') values, just do:

plot(x, type="h", 
panel.first = rect(c(1,7), -1e6, c(3,10), 1e6, col='green', border=NA))

Sample Image

Graph with a shaded the area occupied by multiple lines

You just need to group per individual time unit and calculate the minimum / maximum values. This allows you to plot a geom_ribbon:

example %>% 
group_by(values) %>%
summarize(min = min(value), max = max(value)) %>%
ggplot() +
geom_ribbon(aes(x = values, ymin = min, ymax = max), size = 2,
fill = "#29c8e5", color = "black") +
theme_classic()

Sample Image

If you would rather have the ribbon overlying your original plot, you could do:

ribbon <- example %>% 
group_by(values) %>%
summarize(min = min(value), max = max(value))

graph1 +
geom_ribbon(aes(x = values, ymin = min, ymax = max),
data = ribbon, size = 0, fill = "#29c8e5",
color = NA, alpha = 0.3, inherit.aes = FALSE)

Sample Image

For what it's worth, I think the first option is more visually striking.

Shade area between 2 curves

Because, in this case, there isn't really any curve to the line you could use something very simple (that highlights how polygon works).

x <- c(0,1,1,0)
y <- c(x[1:2]/2, x[3:4]/4)
polygon(x,y, col = 'green', border = NA)

Now, if you had a curve you'd need more vertices.

curve(x^2, from=0 , to =1, col="darkblue")
curve(x^4, from=0 , to =1, add=T, col="darkred")
x <- c(seq(0, 1, 0.01), seq(1, 0, -0.01))
y <- c(x[1:101]^2, x[102:202]^4)
polygon(x,y, col = 'green', border = NA)

(extend the range of that last curve and see how using similar code treats the crossing curves yourself)

R - Fill area between lines based on top value

The reason you're getting the wrong shading is probably because the data is a bit on the coarse side. My advice would be to interpolate the data first. Assuming dat1 is from your example.

library(ggplot2)

# From long data to wide data
dat2 <- tidyr::pivot_wider(dat1, values_from = value, names_from = var)

# Setup interpolated data (tibble because we can then reference column x)
dat3 <- tibble::tibble(
x = seq(min(dat2$month), max(dat2$month), length.out = 1000),
rainfall = with(dat2, approx(month, rainfall, xout = x)$y),
evaporation = with(dat2, approx(month, evaporation, xout = x)$y)
)

Then, we need to find a way to identify groups, and here is a helper function for that. Group IDs are based on the runs in run length encoding.

# Make function to identify groups
rle_id <- function(x) {
x <- rle(x)
rep.int(seq_along(x$lengths), x$lengths)
}

And now we can plot it.

ggplot(dat3, aes(x)) +
geom_ribbon(aes(ymin = pmin(evaporation, rainfall),
ymax = pmax(evaporation, rainfall),
group = rle_id(sign(rainfall - evaporation)),
fill = as.factor(sign(rainfall - evaporation))))

Sample Image

Created on 2021-02-14 by the reprex package (v1.0.0)



Related Topics



Leave a reply



Submit