Як вибрати елементи масиву із заданою умовою?


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')

1
Це добре ... vecMask = 1 <x генерує векторну маску, як vecMask = (помилково, правда, ...), яку можна просто поєднувати з іншими векторними масками. Кожен елемент є умовою для прийому елементів джерельного вектора (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)змушує нерівності оцінювати спочатку, тому всі операції відбуваються в запланованому порядку, а результати всі чітко визначені. Дивіться документи тут.
calavicci

@ ru111 Він також працює на Python 3.6 (немає причин припиняти його роботу).
jfs

Я отримую "ValueError: Значення істинності масиву з більш ніж одним елементом неоднозначне. Використовуйте a.any () або a.all ()"
ru111

@ ru111 слід написати (0 < x) & (x < 10)(як показано у відповіді), замість 0 < x < 10чого не працює для nummy масивів у будь-якій версії 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

21

Додайте одну деталь до відповідей @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 не просто поверне масив індексів, але натомість поверне кортеж (вихід умова.nonzero ()), що містить масиви - у цьому випадку (the array of indices you want,), тож вам потрібно select_indices = np.where(...)[0]отримати бажаний результат і очікувати.
calavicci

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.]])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.