# 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?

## Answers:

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.**The answer to 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 [230]: dists = np.arange(0,10,.5)
In [231]: r = 5
In [232]: dr = 1
In [233]: np.where(dists >= r)
Out[233]: (array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),)
In [234]: np.where(dists <= r+dr)
Out[234]: (array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),)
In [235]: np.where(dists >= r) and np.where(dists <= r+dr)
Out[235]: (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 [236]: dists >= r
Out[236]:
array([False, False, False, False, False, False, False, False, False,
False, True, True, True, True, True, True, True, True,
True, True], dtype=bool)
In [237]: dists <= r + dr
Out[237]:
array([ True, True, True, True, True, True, True, True, True,
True, True, True, True, False, False, False, False, False,
False, False], dtype=bool)
In [238]: (dists >= r) & (dists <= r + dr)
Out[238]:
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 [239]: np.where((dists >= r) & (dists <= r + dr))
Out[239]: (array([10, 11, 12]),)
In [240]: dists[np.where((dists >= r) & (dists <= r + dr))]
Out[240]: array([ 5. , 5.5, 6. ])
```

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

```
In [241]: dists[(dists >= r) & (dists <= r + dr)]
Out[241]: 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)[0],np.where(dists <= r + dr)[0])
```

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.

One can check out the following link to learn more about: operator

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. ðŸ™‚