Как выбрать элементы массива с заданным условием?

156

Предположим , у меня есть Numpy массив x = [5, 2, 3, 1, 4, 5], y = ['f', 'o', 'o', 'b', 'a', 'r']. Я хочу выбрать элементы, yсоответствующие элементам x, которые больше 1 и меньше 5.

Я попытался

x = array([5, 2, 3, 1, 4, 5])
y = array(['f','o','o','b','a','r'])
output = y[x > 1 & x < 5] # desired output is ['o','o','a']

но это не работает Как бы я это сделал?

боб
источник

Ответы:

220

Ваше выражение работает, если вы добавите скобки:

>>> y[(1 < x) & (x < 5)]
array(['o', 'o', 'a'], 
      dtype='|S1')
JFS
источник
1
Это хорошо .. vecMask = 1 <x генерирует векторную маску наподобие vecMask = (False, True, ...), которую можно просто комбинировать с другими векторными масками. Каждый элемент является условием для взятия элементов исходного вектора (True) или нет (False). Это также можно использовать с полной версией numpy.extract (vecMask, vecSrc) или numpy.where (vecMask, vecSrc, vecSrc2).
MasterControlProgram
6
@JennyYueJin: это происходит из-за приоритета. (Побитовый) &имеет более высокий приоритет, чем <и >, которые, в свою очередь, имеют более высокий приоритет, чем (логический) and. x > 1 and x < 5сначала оценивает неравенства, а затем логическое соединение; x > 1 & x < 5оценивает побитовое соединение 1и (значения в) x, а затем неравенства. (x > 1) & (x < 5)сначала вынуждает оценивать неравенства, поэтому все операции выполняются в намеченном порядке, и результаты все четко определены. Смотрите документы здесь.
Калавиччи
@ ru111 Он работает и на Python 3.6 (нет причин прекращать работу).
JFS
Я получаю «ValueError: Истинное значение массива с более чем одним элементом неоднозначно. Используйте a.any () или a.all ()»
ru111
@ ru111, вы должны написать (0 < x) & (x < 10)(как показано в ответе) вместо того, 0 < x < 10чтобы не работать для пустых массивов в любой версии Python.
JFS
34

IMO OP на самом деле не хочет np.bitwise_and()(ака &), но на самом деле хочет, np.logical_and()потому что они сравнивают логические значения, такие как Trueи False- см. Этот пост SO на логическом и побитовом, чтобы увидеть разницу.

>>> x = array([5, 2, 3, 1, 4, 5])
>>> y = array(['f','o','o','b','a','r'])
>>> output = y[np.logical_and(x > 1, x < 5)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

И эквивалентный способ сделать это с np.all()помощью установки axisаргумента соответствующим образом.

>>> output = y[np.all([x > 1, x < 5], axis=0)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

по номерам:

>>> %timeit (a < b) & (b < c)
The slowest run took 32.97 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 1.15 µs per loop

>>> %timeit np.logical_and(a < b, b < c)
The slowest run took 32.59 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.17 µs per loop

>>> %timeit np.all([a < b, b < c], 0)
The slowest run took 67.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 5.06 µs per loop

так что использование np.all()медленнее, но &и logical_andпримерно одинаково.

Марк Микофски
источник
7
Вы должны быть немного осторожны в том, как вы говорите о том, что оценивается. К примеру, в output = y[np.logical_and(x > 1, x < 5)], x < 5 как оценивается (возможно создание огромного массива), несмотря на то, что это второй аргумент, потому что оценка происходит за пределами функции. IOW logical_andполучает два уже оцененных аргумента. Это отличается от обычного случая a and b, в котором bне оценивается, если aэто правда.
DSM
15
нет никакой разницы между bitwise_and () и logic_and () для логических массивов
jfs
22

Добавьте одну деталь к ответам @JF Себастьяна и @Mark Mikofski:
если кто-то хочет получить соответствующие индексы (а не фактические значения массива), подойдет следующий код:

Для удовлетворения нескольких (всех) условий:

select_indices = np.where( np.logical_and( x > 1, x < 5) )[0] #   1 < x <5

Для удовлетворения нескольких (или) условий:

select_indices = np.where( np.logical_or( x < 1, x > 5 ) )[0] # x <1 or x >5
Добрая воля
источник
2
Обратите внимание, что numpy.where не просто вернет массив индексов, но вместо этого вернет кортеж (вывод condition.nonzero ()), содержащий массивы - в этом случае (the array of indices you want,), поэтому вам нужно select_indices = np.where(...)[0]получить желаемый результат и ожидать.
Калавиччи
5

Мне нравится использовать np.vectorizeдля таких задач. Учтите следующее:

>>> # Arrays
>>> x = np.array([5, 2, 3, 1, 4, 5])
>>> y = np.array(['f','o','o','b','a','r'])

>>> # Function containing the constraints
>>> func = np.vectorize(lambda t: t>1 and t<5)

>>> # Call function on x
>>> y[func(x)]
>>> array(['o', 'o', 'a'], dtype='<U1')

Преимущество заключается в том, что вы можете добавить гораздо больше типов ограничений в векторизованную функцию.

Надеюсь, поможет.


источник
1
Это не очень хороший способ индексирования в NumPy (он будет очень медленным).
Алекс Райли
1

На самом деле я бы сделал это так:

L1 - индексный список элементов, удовлетворяющих условию 1; (возможно, вы можете использовать somelist.index(condition1)или, np.where(condition1)чтобы получить L1.)

Точно так же вы получите L2, список элементов, удовлетворяющих условию 2;

Тогда вы найдете пересечение, используя intersect(L1,L2).

Вы также можете найти пересечение нескольких списков, если выполняете несколько условий.

Затем вы можете применить индекс в любом другом массиве, например, x.

Шуо Ян
источник
0

Для 2D-массивов вы можете сделать это. Создайте 2D маску, используя условие. Введите маску условия в int или float, в зависимости от массива, и умножьте ее на исходный массив.

In [8]: arr
Out[8]: 
array([[ 1.,  2.,  3.,  4.,  5.],
       [ 6.,  7.,  8.,  9., 10.]])

In [9]: arr*(arr % 2 == 0).astype(np.int) 
Out[9]: 
array([[ 0.,  2.,  0.,  4.,  0.],
       [ 6.,  0.,  8.,  0., 10.]])
Гаутам Срикумар
источник