juliaplots.jl

How to fill area between curves with Plots.jl?


Suppose I have a curve y, and two other curves u and l in the form of vectors. How to plot:

plot(y, lab="estimate")
plot!(y-l, lab="lower bound")
plot!(y+u, lab="upper bound")

That is, an asymmetric confidence interval? I know how to plot the symmetric case with the option ribbon as explained here.


Solution

  • The current answers are NOT correct. Here are two ways that are correct (as of v1.10.1 of Plots.jl):

    Method 1: Using fillrange

    plot(x, l, fillrange = u, fillalpha = 0.35, c = 1, label = "Confidence band")
    

    Method 2: Using ribbon

    plot(x, (l .+ u) ./ 2, ribbon = (l .- u) ./ 2, fillalpha = 0.35, c = 1, label = "Confidence band")
    

    (Here, l and u denote the the "lower" and "upper" y values, respectively, and x denotes their common x values.) The key difference between these two methods is that fillrange shades the region between l and u, while the ribbon argument is a radius, i.e. half the width of the ribbon (or in other words, the vertical deviation from the midpoints).

    Example using fillrange:

    x = collect(range(0, 2, length= 100))
    y1 = exp.(x)
    y2 = exp.(1.3 .* x)
    
    plot(x, y1, fillrange = y2, fillalpha = 0.35, c = 1, label = "Confidence band", legend = :topleft)
    

    Let's scatter y1 and y2 on top of the plot, just to make sure we're filling in the right region.

    plot!(x,y1, line = :scatter, msw = 0, ms = 2.5, label = "Lower bound")
    plot!(x,y2, line = :scatter, msw = 0, ms = 2.5, label = "Upper bound")
    

    Result:

    enter image description here

    Example using ribbon:

    mid = (y1 .+ y2) ./ 2   #the midpoints (usually representing mean values)
    w = (y2 .- y1) ./ 2     #the vertical deviation around the means
    
    plot(x, mid, ribbon = w , fillalpha = 0.35, c = 1, lw = 2, legend = :topleft, label = "Mean")
    plot!(x,y1, line = :scatter, msw = 0, ms = 2.5, label = "Lower bound")
    plot!(x,y2, line = :scatter, msw = 0, ms = 2.5, label = "Upper bound")
    

    (Here, x, y1, and y2 are the same as before.)

    Result:

    enter image description here

    Notice that the labels for ribbon and fillrange are different in the legends: the former labels the midpoints/means, while the latter labels the shaded region itself.

    Some additional comments:

    1. The OP's answer of plot(y, ribbon=(l,u), lab="estimate") is not correct (at least for Plots v1.10.1.). I realize this thread is over 3 years old, so perhaps it worked in the earlier version of Plots.jl that the OP was using at the time)

    2. Similar to one of the answers given,

    plot(x, [mid mid], fillrange=[mid .- w, mid .+ w], fillalpha=0.35, c = [1 4], label = ["Band 1" "Band 2"], legend = :topleft, dpi = 80)
    

    will work, but this actually creates TWO ribbons (and hence, two icons in the legend) which may or may not be what the OP was looking for. To illustrate the point:

    enter image description here