Contour plot of separate, intersecting levels


Hi, I’m having a hard time figuring out how to do the following, though I’m sure there must be some “easy” solution that I just cannot find.

I have a 3-dimensional array of booleans and I’d like to see the “contours” of it (i.e. when it changes from false to true) along the first two-dimensions and how such contours change along the third.

Something in the lines of:

using Plots

V = falses(20, 10, 3);
[V[i, j, k] = i/20 + j/10 - k/3 > 0 for i in 1:20, j in 1:10, k in 1:3]

pl = plot(V[:, :, 1], st = :contour, levels = 1)
plot!(pl, V[:, :, 2], st = :contour, levels = 1)
plot!(pl, V[:, :, 3], st = :contour, levels = 1)

I have a couple of issues with the above plot:

  1. Each contour plot actually includes 2 contour lines and I’d like that to be only 1
  2. I’d like to label each contour line for its k-th level
  3. It seems clunky and excessive

Imperfect solutions

One option I could do is change the value of V to match the k-th level, something like:

VV = [V[i, j, k].*k for i in 1:20, j in 1:10, k in 1:3]

pl = plot(VV[:, :, 1], st = :contour, levels = 1)
plot!(pl, VV[:, :, 2], st = :contour, levels = 1)
plot!(pl, VV[:, :, 3], st = :contour, levels = 1)

however that doesn’t solve problem 1 and it looks like an especially clunky workaround.

A somewhat better option could be to just define the sum and do the contour plot of that.

VVV = sum(V, dims = 3)
plot(VVV[:, :], st = :contour, levels = 2)

This works in this simple example but in my actual use case the levels might actually intersect and I’d still like to know which level goes where.

Thanks a lot in advance for any help :slight_smile:

Is this fundamentally true in your problem, or is it created like V, where you have a floating point value and you are checking its relation to 0? I ask because working directly with the floating point values and setting levels = [0.0] might just be a lot easier.

I’m not seeing this with any backend I try, including PyPlot. I see only one contour per plot command…

In the legend? You can set the color with e.g. color = :blue if that’s what you mean (assuming based on your second solution). I don’t think you’ll be able to add a legend/label to a contour plot, although I’m not sure of this.

I think you can still accomplish this with a sum, as long as you first differentiate each layer sufficiently. For example, if you multiple each slice by 2^k before taking the sum, intersections should remain unique. I.e. there is only one case that will give 0, 1, 4 etc., (I think this rule is general for any number of slices K, but that’s just instinct…)

julia> V2 = [V[i,j,k] * 2^k for i in axes(V, 1), j in axes(V,2), k in axes(V, 3)];

julia> sumV = dropdims(sum(V2, dims = 3), dims=3);

julia> contour(sumV, levels = sort(unique(sumV)))

You get the color “labels” for free using this method.

I’m not sure how readable the result is, but I think setting the color manually for each slice suffers from the same problem, depending on what you wanted? A cummulative sum might be another way

julia> let V = cumsum(V, dims=3)
       pl = plot(V[:, :, 2], st = :contour, levels = 1, colorbar = false)
       plot!(pl, V[:, :, 3], st = :contour, levels = 1, color = :blue)

Yes, in my problem I also have arrays of booleans

Weird, this is what I get from the first MWE I posted on julia@v1.5.2, Plots@v1.6.7:

As you see I have two contour lines for each “level” of V, one for the falses and one for the trues. Does yours look different?

What I mean is if I have 1 contour line for each k-level then I can label them singularly (kinda like your example with sumV)

This is actually a nice solution thank you, but it doesn’t really work because, if the contour lines intersect than I lose the ability to know which was which. For example here I changed it slightly to make the contour lines intersect

V = falses(20, 10, 3);
[V[i, j, k] = i/20 + (-1/2)^(k - 1)*j/10 - k/3 > 0 for i in 1:20, j in 1:10, k in 1:3]
V2 = [V[i,j,k] * 2^k for i in axes(V, 1), j in axes(V,2), k in axes(V, 3)];
sumV = dropdims(sum(V2, dims = 3), dims=3);
contour(sumV, levels = sort(unique(sumV)))

As you see it does appropriately separate the levels but now there is an additional level which becomes even harder to track once the number of levels and intersections increase .

Did you try the cumsum method as well?

Yes, that also doesn’t work in the presence of intersections:

Works if you take the cumsum of V2 though, right?

Ah. You might be right, now I only need to figure out a way of “isolating” the levels, but that should just be bookkeeping.

Thanks a lot! :grinning:

1 Like