Boolean indexing with multidimensional array

I have been trying to filter a 2d-array with boolean indexing without much success. For instance,

B = randn(5,5)
s = B .> 0
B[s] # this produces a 1d array with those values that give s == true

However, since I want is to plot with contourf those values in B[s] keeping those that does not satisfy the the s condition as well. The problem is that I get the 1d-array satisfying the s conditions instead of the whole 2d-array and the plot does not work with this. What I tried to do to obtain the desired result is

B.*s

which gives the filtered matrix with the same original size of B. But not sure if this is an effective way of doing things, especially when lengths of arrays goes up to, say 10^4.

It’s a bit hard to understand what you want the end result to be for the cases where the element is < 0.

Perhaps you are looking for

julia> clamp.(B, 0, Inf)
5Ă—5 Array{Float64,2}:
 0.0       1.63247    0.0       0.0       0.0     
 1.23743   0.0191958  0.417646  0.0       1.17548 
 0.0       0.0        0.42163   0.601486  0.726729
 0.0       0.0        0.0       0.0       0.360118
 0.420498  0.0        0.0       2.44904   0.0 
2 Likes

I was trying to plot with contourf a complex variable showing only those values that have zero imaginary part, discarding the others, but keeping the same size of the original array. The working example wasn’t probably appropriate enough (noted). Here it goes again:

julia> B
3Ă—3 Array{Complex{Float64},2}:
  0.542545+0.0im        0.925355-0.487208im    1.65225+0.806615im
 -0.927869+0.847093im  -0.309946+1.53378im    0.444565+0.0im     
 -0.048035+0.260424im   0.937434+0.0im       -0.153685-1.71329im

Now, if I do s = imag.(B) .== 0, then

B[s]
3-element Array{Complex{Float64},1}:
  0.5425454 + 0.0im
   0.937434 + 0.0im
 0.44456454 + 0.0im

But is hard to plot this way. What I really want is the original matrix B with those values that have zero imaginary part sent to zero. For instance,

julia> B.*s
3Ă—3 Array{Complex{Float64},2}:
 0.542545+0.0im       0.0-0.0im       0.0+0.0im
     -0.0+0.0im      -0.0+0.0im  0.444565+0.0im
     -0.0+0.0im  0.937434+0.0im      -0.0-0.0im

Hope is illustrative enough.

Indexing is, at its core, simply a selection API. It chooses which locations you want to have returned. Or modified, in the case of setindex!. As such, you can use boolean indexing to change the values as you’d like:

B[imag.(B) .== 0] .= 0

Broadcasted clamp is another great tool as @kristoffer.carlsson mentions, as is broadcasted ifelse:

ifelse.(imag.(B) .== 0, missing, B)
1 Like

If you’re worried about runtime, the best way to avoid it is to plot fewer points. The plotting library runtime will be orders of magnitude larger than the difference in runtime between indexing methods. Your screen only has so many pixels–you won’t lose much by downsampling your array to the desired plotting resolution.

using Plots, BenchmarkTools

function full_contour!(A)
    A[imag(A) .< 0] .= 0
    contourf(abs.(A))
end

function resampled_contour(A, res)
    B = A[1:(size(A,1)Ă·res):end, 1:(size(A,2)Ă·res):end]
    B[imag(B) .< 0] .= 0
    contourf(abs.(B))
end

@btime full_contour!(A)           setup=(A = randn(ComplexF64, 5000, 5000));
@btime resampled_contour($A, 500) setup=(A = randn(ComplexF64, 5000, 5000));
1.180 s (2848 allocations: 670.74 MiB)    # full_contour!
12.141 ms (2850 allocations: 10.72 MiB)   # resampled_contour

Yeah, both worked. Thank you guys.

Don’t forget comprehension! (Which creates a new matrix.) Also note that you can use isreal to check for zero imaginary part.

C = [isreal(x) ? 0.0im : x for x in B]
1 Like

I took for granted that if the variable is actually complex, isreal would be false independently of the values. Good to know. Thanks.