Change a vector CartesianIndex

I was wondering if I could get help with this problem.
I have a matrix and find every index where its value is negative.

mat = [1 2 3 ; -1 -2 3]

ind = findall(<(0),mat)

2-element Vector{CartesianIndex{2}}:
 CartesianIndex(2, 1)
 CartesianIndex(2, 2)

Something I want to do is to change the row before each CartesianIndex; CartesianIndex(1,1), CartesianIndex(1,2). I tried to following

function ind_ready(;ind = ind)
        
    A = hcat(getindex.(ind,1),getindex.(ind,2))
    A[:,1] = A[:,1] .- 1   
    B = Any[]
    C = CartesianIndex[] 

    for i in 1:length(ind) 
        B = push!(B,(A[i,1],A[i,2]))
    end


    for i in 1:length(B) 
        C = push!(C,CartesianIndex(B[i]))
    end

    return C

end

Then I get the following

2-element Vector{CartesianIndex}:
 CartesianIndex(1, 1)
 CartesianIndex(1, 2)

However, with this

mat[ind_ready()]

returns the following error

ArgumentError: unable to check bounds for indices of type CartesianIndex{2}

How can I fix this problem?

Any suggestion would be greatly helpful.

ind .= CartesianIndex.(getindex.(ind, 1) .- 1, getindex.(ind, 2))

or equivalently

@. ind = CartesianIndex(getindex(ind, 1) - 1, getindex(ind, 2))

which gives

julia> mat[ind]
2-element Vector{Int64}:
 1
 2

Two problems: your ind_ready() function actually decrements the second index (with A[:,2] = A[:,2] .- 1 ), not the first, resulting in invalid 0 indices:

julia> i2 = ind_ready()
2-element Vector{CartesianIndex}:
 CartesianIndex(2, 0)
 CartesianIndex(2, 1)

Second, Julia doesn’t like that your ind_ready returns an abstractly typed CartesianIndex[] vector and not a concretely typed CartesianIndex{2}[] vector:

julia> mat[CartesianIndex[CartesianIndex(1,1)]]
ERROR: ArgumentError: unable to check bounds for indices of type CartesianIndex{2}

julia> mat[CartesianIndex{2}[CartesianIndex(1,1)]]
1-element Vector{Int64}:
 1

But this error message is confusing and should be fixed: confusing error for array[CartesianIndex[...]] indexing · Issue #48655 · JuliaLang/julia · GitHub

1 Like

You could simply do:

ind .-= Ref(CartesianIndex(1,0))
mat[ind]
3 Likes

Oh, right, I forgot that CartesianIndex supports vector operations (± and multiplication by scalars).

Thank you very much!

I figured out that my example has the problem you mentioned, so I changed the example to a row. I’ll edit the code in the question accordingly.

I was just wondering one more thing. Is there a way that I can put conditions? I want to reduce by one if the cartesian index is bigger than one?

Thank you so much. Now I can cut my messy codes into half :slight_smile:

Yes, of course. One solution would be to include a call to max (or ifelse or similar):

ind .= CartesianIndex.(max.(1, getindex.(ind, 1) .- 1), getindex.(ind, 2))

but when things become sufficiently complicated at some point it becomes clearer to write an “scalar” expression that does what you want, and either broadcast it or use a comprehension or map. For example, one of:

newind = map!(ind) do c
    CartesianIndex(max(1, c[1]), c[2])
end

map!(ind, ind) do c
    CartesianIndex(max(1, c[1]), c[2])
end

newind = [CartesianIndex(max(1, c[1]), c[2]) for c in ind]

f(c) = CartesianIndex(max(1, c[1]), c[2])
ind .= f.(c)

ind .= (c -> CartesianIndex(max(1, c[1]), c[2])).(ind)

and of course once you write it like this you can use if statements or whatever you want to transform c.

1 Like

If your need is to handle cases where some negative value is in the first row, you could make use of a variant of @rafael.guerra solution


mat3 = [-2 -3 4; 1 2 3 ; -1 -2 3]

ind3 = findall(<(0),mat3)

ind3 .-= Ref(CartesianIndex(1,0))

nind=[i - CartesianIndex(1,0) for i in ind3 if i.I[1] > 1]
1 Like

I thank all of you so much !