# Contour plot of separate, intersecting levels

### Problem

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
pyplot()

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

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)))
``````

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)
end
``````

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 `false`s and one for the `true`s. 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!

1 Like