# Numpy where function multiple conditions

## Question:

I have an array of distances called `dists`. I want to select `dists` which are within a range.

`````` dists[(np.where(dists >= r)) and (np.where(dists <= r + dr))]
``````

However, this selects only for the condition

`````` (np.where(dists <= r + dr))
``````

If I do the commands sequentially by using a temporary variable it works fine. Why does the above code not work, and how do I get it to work?

The best way in your particular case would just be to change your two criteria to one criterion:

``````dists[abs(dists - r - dr/2.) <= dr/2.]
``````

It only creates one boolean array, and in my opinion is easier to read because it says, is `dist` within a `dr` or `r`? (Though I’d redefine `r` to be the center of your region of interest instead of the beginning, so `r = r + dr/2.`) But that doesn’t answer your question.

You don’t actually need `where` if you’re just trying to filter out the elements of `dists` that don’t fit your criteria:

``````dists[(dists >= r) & (dists <= r+dr)]
``````

Because the `&` will give you an elementwise `and` (the parentheses are necessary).

Or, if you do want to use `where` for some reason, you can do:

`````` dists[(np.where((dists >= r) & (dists <= r + dr)))]
``````

Why:
The reason it doesn’t work is because `np.where` returns a list of indices, not a boolean array. You’re trying to get `and` between two lists of numbers, which of course doesn’t have the `True`/`False` values that you expect. If `a` and `b` are both `True` values, then `a and b` returns `b`. So saying something like `[0,1,2] and [2,3,4]` will just give you `[2,3,4]`. Here it is in action:

``````In : dists = np.arange(0,10,.5)
In : r = 5
In : dr = 1

In : np.where(dists >= r)
Out: (array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),)

In : np.where(dists <= r+dr)
Out: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

In : np.where(dists >= r) and np.where(dists <= r+dr)
Out: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)
``````

What you were expecting to compare was simply the boolean array, for example

``````In : dists >= r
Out:
array([False, False, False, False, False, False, False, False, False,
False,  True,  True,  True,  True,  True,  True,  True,  True,
True,  True], dtype=bool)

In : dists <= r + dr
Out:
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
True,  True,  True,  True, False, False, False, False, False,
False, False], dtype=bool)

In : (dists >= r) & (dists <= r + dr)
Out:
array([False, False, False, False, False, False, False, False, False,
False,  True,  True,  True, False, False, False, False, False,
False, False], dtype=bool)
``````

Now you can call `np.where` on the combined boolean array:

``````In : np.where((dists >= r) & (dists <= r + dr))
Out: (array([10, 11, 12]),)

In : dists[np.where((dists >= r) & (dists <= r + dr))]
Out: array([ 5. ,  5.5,  6. ])
``````

Or simply index the original array with the boolean array using fancy indexing

``````In : dists[(dists >= r) & (dists <= r + dr)]
Out: array([ 5. ,  5.5,  6. ])
``````

I have worked out this simple example

``````import numpy as np

ar = np.array([3,4,5,14,2,4,3,7])

print [X for X in list(ar) if (X >= 3 and X <= 6)]

>>>
[3, 4, 5, 4, 3]
``````

Try:

``````np.intersect1d(np.where(dists >= r),np.where(dists <= r + dr))
``````

The accepted answer explained the problem well enough. However, the more Numpythonic approach for applying multiple conditions is to use numpy logical functions. In this case, you can use `np.logical_and`:

``````np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))
``````

I like to use `np.vectorize` for such tasks. Consider the following:

``````>>> # function which returns True when constraints are satisfied.
>>> func = lambda d: d >= r and d<= (r+dr)
>>>
>>> # Apply constraints element-wise to the dists array.
>>> result = np.vectorize(func)(dists)
>>>
>>> result = np.where(result) # Get output.
``````

You can also use `np.argwhere` instead of `np.where` for clear output.

This should work:

``````dists[((dists >= r) & (dists <= r+dr))]
``````

Try:

``````import numpy as np
dist = np.array([1,2,3,4,5])
r = 2
dr = 3
np.where(np.logical_and(dist> r, dist<=r+dr))
``````

Output: (array([2, 3, 4]),)

You can see Logic functions for more details.

One interesting thing to point here; the usual way of using OR and AND too will work in this case, but with a small change. Instead of “and” and instead of “or”, rather use Ampersand(&) and Pipe Operator(|) and it will work.

When we use ‘and’:

``````ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) and (ar<6), 'yo', ar)

Output:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
``````

When we use Ampersand(&):

``````ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) & (ar<6), 'yo', ar)

Output:
array(['3', 'yo', 'yo', '14', '2', 'yo', '3', '7'], dtype='<U11')
``````

And this is same in the case when we are trying to apply multiple filters in case of pandas Dataframe. Now the reasoning behind this has to do something with Logical Operators and Bitwise Operators and for more understanding about same, I’d suggest to go through this answer or similar Q/A in stackoverflow.

## UPDATE

A user asked, why is there a need for giving (ar>3) and (ar<6) inside the parenthesis. Well here’s the thing. Before I start talking about what’s happening here, one needs to know about Operator precedence in Python.

Similar to what BODMAS is about, python also gives precedence to what should be performed first. Items inside the parenthesis are performed first and then the bitwise operator comes to work. I’ll show below what happens in both the cases when you do use and not use “(“, “)”.

Case1:

``````np.where( ar>3 & ar<6, 'yo', ar)
np.where( np.array([3,4,5,14,2,4,3,7])>3 & np.array([3,4,5,14,2,4,3,7])<6, 'yo', ar)
``````

Since there are no brackets here, the bitwise operator(`&`) is getting confused here that what are you even asking it to get logical AND of, because in the operator precedence table if you see, `&` is given precedence over `<` or `>` operators. Here’s the table from from lowest precedence to highest precedence. It’s not even performing the `<` and `>` operation and being asked to perform a logical AND operation. So that’s why it gives that error.

precedence

Now to Case 2:

If you do use the bracket, you clearly see what happens.

``````np.where( (ar>3) & (ar<6), 'yo', ar)
np.where( (array([False,  True,  True,  True, False,  True, False,  True])) & (array([ True,  True,  True, False,  True,  True,  True, False])), 'yo', ar)
``````

Two arrays of True and False. And you can easily perform logical AND operation on them. Which gives you:

``````np.where( array([False,  True,  True, False, False,  True, False, False]),  'yo', ar)
``````

And rest you know, np.where, for given cases, wherever True, assigns first value(i.e. here ‘yo’) and if False, the other(i.e. here, keeping the original).

That’s all. I hope I explained the query well.

To get `np.where()` working with multiple conditions, just do the following:

``````np.where((condition 1) & (condition 2)) # for and
np.where((condition 1) | (condition 2)) # for or
``````

I know this repeats some other answers, but I put this simple answer here for people still wondering, "Why am I getting that annoying error message about `The truth value of an array with more than one element is ambiguous`" who are confused by the very verbose and complex answers that are addressing the somewhat specialized nature of the original post.

Now, as to why numpy breaks when you use `and` instead of `&`, I will not try to answer that here. It just does 🙂 See other answers here for explanation. It seems like something they should just fix instead of forcing it for consistency IMHO. Or at least they should make a better error message. 🙂

Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.