Determine whether an element is in an array

Hi

I am thinking how to achieve similar thing to numpy’s where function in Julia. Suppose I have an array A and now I want to determine if there is any element of A belong to the regime [a-1e-12,a+1e-12]. If yes I want to also get the position. Thanks

numpy.where

Return elements chosen from x or y depending on condition.

I am unclear if you want get all elements in the array meeting the condition or not. filter will do that.

If you just want to know if there is any element of A that meets the condition, then any is what you want.

If you want to get the position, then you findfirst or findall.

julia> A = rand(100);

julia> a = A[50]
0.1703964738775896

julia> filter(x -> a-1e-12 <= x <= a+1e-12, A)
1-element Vector{Float64}:
 0.1703964738775896

julia> any(x -> a-1e-12 <= x <= a+1e-12, A)
true

julia> findfirst(x -> a-1e-12 <= x <= a+1e-12, A) # thanks abraemer
50

Also, you want to consider isapprox or .

julia> findall(≈(A[50]), A)
1-element Vector{Int64}:
 50
8 Likes

I am not totally sure what the a is in your condition but you should be able to use something like findfirst. That function takes a function as first argument and an array/iterable as second and gives you the first index such that the function return true for the value in this position. E.g.

A = rand(100)
findfirst(x -> x>0.9, A)

Gives the index of the first element that is larger than 0.9.

There’s also findall if you want the locations of all elements fulfilling the condition.

3 Likes

It looks like numpy.where is actually two functions, based on whether you use it with three arguments or a single one. From the docs:

numpy.where(condition, [x, y, ]/ )

Return elements chosen from x or y depending on condition.

Note: When only condition is provided, this function is a shorthand for np.asarray(condition).nonzero(). …

In Julia the analog to the three argument version would be (broadcasted) ifelse, i.e.:

x = rand(10)
ifelse.(x .> 0.5, 2 .* x, - 0.5)  # np.where(x > 0.5, 2 * x, - 0.5)

The one-argument version behaves like findall as explained already.

1 Like

Your description actually covers multiple NumPy functions. Given scalar a and array A:

  1. you would do 2 elementwise comparisons b = (a-1e-12) <= A <= (a+1e-12) (if A is not a NumPy array, you would need numpy.less_equal for automatic array conversion). The Julia equivalent is to broadcast the 2 scalar comparisons: b = (a-1e-12) .<= A .<= (a+1e-12).
  2. To find if any of the comparisons computed true, you do numpy.any(b) to reduce logical-or over all the elements. Julia equivalent is any(b), though it instead stops on the first true. (If you want to replicate numpy.any along dimensions specified in the axis argument, I think you’ll need to use mapslices(any, ...) or any.(eachslice(...)), I haven’t tested examples).
  3. To get the indices where comparisons computed true, you use the 1-argument numpy.where(b). The Julia equivalent is the 1-argument findall(b).

The 2-argument findall is for lazily testing elements of an array that isn’t necessarily containing Bool; this saves an allocation of a Bool container. The 3-argument numpy.where does something much different from the 1-argument version, and bertschi provided the right equivalent: broadcasted ifelse. The advantage of Julia’s broadcasting is that when you have a tree of dotted function calls like bertschi’s example, they fuse into one kernel function that is broadcasted once over the input containers, no intermediate allocating broadcasts. Compare that with NumPy where you need to allocate x > 0.5 then 2*x before input to numpy.where. You can check Meta.@lower <insert broadcasting code here> to see this fusion; there should be a series of broadcasted calls setting up the tree followed by only 1 materialize call performing the broadcast loop.

Bear in mind that NumPy’s automatic conversion of array-like inputs to NumPy arrays prior to the primary computation means there’s no full Julia equivalent because Julia instead is implemented to work on many input types and return appropriate output types. If you need to stick to a particular type like Array, then you have to be more careful about your input types, possibly even manually convert them; Julia’s collect would be the equivalent of NumPy’s numpy.array. I can’t recommend an equivalent function to numpy.asarray that avoids a copy when no conversion is needed for an input NumPy array; convert(Array, x) does not work on as many input types as collect does, so I’d probably go with if x isa Array x else collect(x) end.