Numpy, де функціонують декілька умов


132

У мене є масив відстаней, який називається дистами. Я хочу вибрати списки, які знаходяться між двома значеннями. Я написав наступний рядок коду для цього:

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

Однак це вибирається лише для умови

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

Якщо я виконую команди послідовно, використовуючи тимчасову змінну, вона прекрасно працює. Чому вищезазначений код не працює, і як змусити його працювати?

Ура

Відповіді:


203

Найкращим способом у вашому конкретному випадку буде просто змінити два ваші критерії на один критерій:

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

Він створює лише один булевий масив, і на мою думку, це легше читати, оскільки він каже, що знаходиться distв межах drабо r? (Хоча я б переосмислив, rщоб бути центром вашої області інтересів замість початку, але r = r + dr/2.) Але це не відповідає на ваше запитання.


Відповідь на ваше запитання:
насправді вам не потрібно, whereякщо ви просто намагаєтесь відфільтрувати елементи dists, які не відповідають вашим критеріям:

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

Тому що &воля дасть вам елементарно and(круглі дужки необхідні).

Або, якщо ви хочете використовувати whereз якоїсь причини, ви можете зробити:

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

Чому:
Причина не працює в тому, що np.whereповертає список індексів, а не булевий масив. Ви намагаєтеся пройти andміж двома списками чисел, які, звичайно, не мають True/ Falseзначення, які ви очікуєте. Якщо aі bє обидва Trueзначення, то a and bповертається b. Так що сказати щось подібне [0,1,2] and [2,3,4]просто дасть вам [2,3,4]. Ось це в дії:

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]),)

Наприклад, те, що ви очікували порівняти, було просто булевим масивом

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)

Тепер ви можете зателефонувати np.whereза комбінованим булевим масивом:

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. ])

Або просто індексувати початковий масив булевим масивом, використовуючи фантазійну індексацію

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

61

Прийнята відповідь досить добре пояснила проблему. Однак тим більше Numpythonic підхід для застосування декількох умов полягає у використанні нумерологічних логічних функцій . У цій асі ви можете використовувати np.logical_and:

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

11

Тут можна зазначити одну цікаву річ; звичайний спосіб використання АБО та І теж працюватиме в цьому випадку, але з невеликою зміною. Замість "і" і замість "або" скоріше скористайтеся Ampersand (&) та Pipe Operator (|), і воно буде працювати.

Коли ми використовуємо "і" :

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

Коли ми використовуємо 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')

І це те саме в тому випадку, коли ми намагаємося застосувати декілька фільтрів у випадку панд Dataframe. Тепер аргументація цього має щось робити з Логічними Операторами та Бітовими Операторами, а для більшого розуміння того ж, я б запропонував пройти цю відповідь або подібний Q / A в стадії потоку.

ОНОВЛЕННЯ

Користувач запитав, чому виникає потреба введення (ar> 3) та (ar <6) всередині дужок. Ну ось у чому річ. Перш ніж почати говорити про те, що відбувається тут, потрібно знати про пріоритет оператора в Python.

Подібно до того, що стосується BODMAS, python також надає перевагу тому, що слід виконати спочатку. Елементи всередині дужок виконуються спочатку, а потім побітний оператор приходить до роботи. Я покажу нижче, що відбувається в обох випадках, коли ви користуєтесь, а не використовуєте "(", ")".

Випадок1:

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)

Оскільки немає дужок тут, оператор побітового ( &) буде плутатися тут то , що ви навіть НЕ запитавши його , щоб отримати логічні І, оскільки в таблиці оператора старшинства , якщо ви бачите, &дається пріоритет над <або >операторами. Ось таблиця від найнижчого до найвищого.

введіть тут опис зображення

Це навіть не виконуючи <і >операцію , і попросили , щоб виконати логічну операцію. Отож, саме тому вона дає таку помилку.

Можна ознайомитись із наступного посилання, щоб дізнатися більше про: пріоритет оператора

Тепер до справи 2:

Якщо ви скористаєтеся дужкою, ви чітко бачите, що відбувається.

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)

Два масиви True та False. І ви можете легко виконати логічні операції І на них. Що дає вам:

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

І інше, ви знаєте, np. drugde, для даних випадків, де True, призначає перше значення (тобто тут 'yo'), а якщо False, інше (тобто тут, зберігаючи оригінал).

Це все. Я сподіваюся, що я добре пояснив запит.


1
Чому ви повинні покласти ()навколо (ar>3)і (ar>6)?
RTrain3k

Це справді гарне запитання. Це настільки гарне питання, що я повинен був подумати сам, що те, що на землі, є в цьому потребою. Так я і попросив колегу, і ми обговорили amd, тепер у мене є рішення для вас. Поставивши це у відповіді як ОНОВЛЕННЯ. Це дійсно просто, але важко зрозуміти насправді.
Аміт Амола

Оформити оновлення RTrain3k, я відповів на ваш запит.
Аміт Амола

5

Мені подобається використовувати np.vectorizeдля таких завдань. Розглянемо наступне:

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

Ви також можете використовувати np.argwhereзамість np.whereдля чіткого виводу. Але це ваш дзвінок :)

Сподіваюся, це допомагає.



2

Це має працювати:

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

Найелегантніший спосіб ~~


2

Спробуйте:

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

Вихід: (масив ([2, 3]),)

Ви можете побачити функції логіки для отримання більш детальної інформації.


0

Я опрацював цей простий приклад

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]

6
У цьому випадку не потрібно повторювати. NumPy має булеву індексацію.
M456
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.