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. ])
Answer from askewchan on Stack OverflowThe 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. ])
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)))
python - np.where multiple return values - Stack Overflow
python - Numpy "where" with multiple conditions - Stack Overflow
python - How to give multiple conditions to numpy.where() - Stack Overflow
What is the correct way to apply np.select() on one row at a time in numpy and pandas?
I think you can use loc:
df.loc[(df['col_1'] == 1, 'new_col')] = a
df.loc[(df['col_1'] == 2, 'new_col')] = b
Or:
df['new_col'] = np.where(df['col_1'] == 1, a, np.where(df['col_1'] == 2, b, np.nan))
Or numpy.select:
df['new_col'] = np.select([df['col_1'] == 1, df['col_1'] == 2],[a, b], default=np.nan)
Or use Series.map, if no match get NaN by default:
d = { 0 : 'a', 1 : 'b'}
df['new_col'] = df['col_1'].map(d)
I think numpy choose() is the best option for you.
import numpy as np
choices = 'abcde'
N = 10
np.random.seed(0)
data = np.random.randint(1, len(choices) + 1, size=N)
print(data)
print(np.choose(data - 1, choices))
Output:
[5 1 4 4 4 2 4 3 5 1]
['e' 'a' 'd' 'd' 'd' 'b' 'd' 'c' 'e' 'a']
Try this: Using the setup from @Maxu
col = 'consumption_energy'
conditions = [ df2[col] >= 400, (df2[col] < 400) & (df2[col]> 200), df2[col] <= 200 ]
choices = [ "high", 'medium', 'low' ]
df2["energy_class"] = np.select(conditions, choices, default=np.nan)
consumption_energy energy_class
0 459 high
1 416 high
2 186 low
3 250 medium
4 411 high
5 210 medium
6 343 medium
7 328 medium
8 208 medium
9 223 medium
You can use a ternary:
np.where(consumption_energy > 400, 'high',
(np.where(consumption_energy < 200, 'low', 'medium')))
You can use np.in1d:
np.where(np.in1d(letters, ['A', 'C']))[0]
Out[]: array([0, 2, 3, 5, 6, 8], dtype=int64)
If you've got multiple more complex conditions, you can use functools.reduce
from functools import reduce
conditions = [letters == 'A', letters == 'C']
np.where(reduce(np.logical_or, conditions))[0]
Out[]: array([0, 2, 3, 5, 6, 8], dtype=int64)
numpy.any will return True/False, so that won't be suitable to use here.
You can stick to just numpy.where, but with a small tweak in the condition -- you introduce the bitwise OR operator into the condition like this:
np.where((letters == 'A') | (letters == 'C'))
In fact, numpy has this built in as well, as numpy.bitwise_or:
np.where(np.bitwise_or(letters == 'A', letters == 'C'))
Here's a short working example:
import numpy as np
A = 'A'
B = 'B'
C = 'C'
letters = np.array([A, B, C, A, B, C, A, B, C])
# Either this:
letter_indexes = np.where((letters == 'A') | (letters == 'C'))
# Or this:
letter_indexes = np.where(np.bitwise_or(letters == 'A', letters == 'C'))
print(letter_indexes[0])
# [0 2 3 5 6 8]