Numpy `logic_or` для більш ніж двох аргументів


88

Для порівняння logical_orфункції Numpy потрібно не більше двох масивів. Як я можу знайти об'єднання більше двох масивів? (Те саме питання можна поставити стосовно Numpy logical_andта отримання перетину більше двох масивів.)



чи є спосіб, аналогічний будь-якому ()?
user3074893

@ user3074893: Це абсолютно та сама проблема. Ви хочете, щоб я розширив свою відповідь?
abarnert

Відповіді:


174

Якщо ви запитуєте про numpy.logical_or, тоді ні, як прямо сказано в документах, єдиними параметрами є x1, x2, і необов’язково out:

numpy.logical_or( x1, x2[, out]) =<ufunc 'logical_or'>


Звичайно, ви можете зв’язати кілька logical_orдзвінків, як це:

>>> x = np.array([True, True, False, False])
>>> y = np.array([True, False, True, False])
>>> z = np.array([False, False, False, False])
>>> np.logical_or(np.logical_or(x, y), z)
array([ True,  True,  True,  False], dtype=bool)

Шлях узагальнення такого типу ланцюжків у NumPy полягає у reduce:

>>> np.logical_or.reduce((x, y, z))
array([ True,  True,  True,  False], dtype=bool)

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

>>> xyz = np.array((x, y, z))
>>> xyz
array([[ True,  True, False, False],
       [ True, False,  True, False],
       [False, False, False, False]], dtype=bool)
>>> np.logical_or.reduce(xyz)
array([ True,  True,  True,  False], dtype=bool)

Але кортеж з трьох однакових масивів однакової довжини є як array_like в термінах NumPy і може використовуватися як 2D масив.


Поза NumPy ви також можете використовувати Python reduce:

>>> functools.reduce(np.logical_or, (x, y, z))
array([ True,  True,  True,  False], dtype=bool)

Однак, на відміну від NumPy reduce, Python не часто потрібен. У більшості випадків, є більш простий спосіб зробити що - то, наприклад, в ланцюзі разом кілька Python orоператорів, які не reduceбільше operator.or_, просто використовувати any. А коли цього немає , як правило, більш зрозумілим є використання явного циклу.

І насправді NumPy anyможна використовувати і для цього випадку, хоча це не настільки тривіально; якщо ви явно не дасте йому вісь, ви отримаєте скаляр замість масиву. Тому:

>>> np.any((x, y, z), axis=0)
array([ True,  True,  True,  False], dtype=bool)

Як і слід було очікувати, logical_andсхоже - ви можете зв’язати його, np.reduceвоно, functools.reduceвоно або замінити allявним axis.

А як щодо інших операцій, наприклад logical_xor? Знову ж та сама угода ... за винятком того, що в цьому випадку не застосовується функція all/ any-type. (Як би ви це назвали odd?)


2
np.logical_or.reduce((x, y, z))було саме те, що я шукав!
blaylockbk

reduceбільше не є внутрішньою функцією в Python 3. Замість того, щоб використовувати:functools.reduce()
Marvin

10

У разі , якщо хто - то все - таки потрібно це - у вас є три логічних масивів a, b, cз одного і тієї ж форми, що дає andпоелементно:

a * b * c

це дає or:

a + b + c

Це те, що ти хочеш? Укладання багато logical_andабо logical_orне є практичним.


6

Оскільки булеві алгебри є комутативними та асоціативними за визначенням, наступні твердження або еквіваленти для булевих значень a, b і c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Отже, якщо у вас є "логічний_або", який є діадичним, і вам потрібно передати йому три аргументи (a, b і c), ви можете зателефонувати

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

або будь-яка інша перестановка, яка вам подобається.


Повернувшись до python, якщо ви хочете перевірити, чи застосовується умова ( testотримана функцією, яка приймає тестуваного і повертає логічне значення) до a, b або c або до будь-якого елементу списку L, ви зазвичай використовуєте

any(test(x) for x in L)

Але Python orнасправді не булевий or, як тому, що він працює з значеннями, відмінними від bools (повертаючи aif aє істинним, bінакше), і тому, що він замикає (значення a or bможе бути True, тоді як b or aвикликає виняток).
abarnert

@abarnert Дякую, я відредагував свою відповідь, щоб пояснити це.
Гіперборей

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

@abarnert Не питай мене. Я вважаю, що якщо ви отримаєте математику прямо (у даному випадку логічні алгебри) у фоновому режимі, багато проблем з програмуванням буде простіше вирішити.
Гіперборей

4

Спираючись на відповідь Абарнерта для n-мірного випадку:

TL; DR: np.logical_or.reduce(np.array(list))


4

за допомогою функції сума:

a = np.array([True, False, True])
b = array([ False, False,  True])
c = np.vstack([a,b,b])

Out[172]: 
array([[ True, False,  True],
   [False, False,  True],
   [False, False,  True]], dtype=bool)

np.sum(c,axis=0)>0
Out[173]: array([ True, False,  True], dtype=bool)

4

Я використовую цей обхідний шлях, який можна розширити до n масивів:

>>> a = np.array([False, True, False, False])
>>> b = np.array([True, False, False, False])
>>> c = np.array([False, False, False, True])
>>> d = (a + b + c > 0) # That's an "or" between multiple arrays
>>> d
array([ True,  True, False,  True], dtype=bool)

1

Я спробував наступні три різні методи для отримання logical_andсписку l з k масивів розміром n :

  1. Використання рекурсивного numpy.logical_and(див. Нижче)
  2. Використовуючи numpy.logical_and.reduce(l)
  3. Використовуючи numpy.vstack(l).all(axis=0)

Потім я зробив те саме для logical_orфункції. Як не дивно, але рекурсивний метод є найшвидшим.

import numpy
import perfplot

def and_recursive(*l):
    if len(l) == 1:
        return l[0].astype(bool)
    elif len(l) == 2:
        return numpy.logical_and(l[0],l[1])
    elif len(l) > 2:
        return and_recursive(and_recursive(*l[:2]),and_recursive(*l[2:]))

def or_recursive(*l):
    if len(l) == 1:
        return l[0].astype(bool)
    elif len(l) == 2:
        return numpy.logical_or(l[0],l[1])
    elif len(l) > 2:
        return or_recursive(or_recursive(*l[:2]),or_recursive(*l[2:]))

def and_reduce(*l):
    return numpy.logical_and.reduce(l)

def or_reduce(*l):
    return numpy.logical_or.reduce(l)

def and_stack(*l):
    return numpy.vstack(l).all(axis=0)

def or_stack(*l):
    return numpy.vstack(l).any(axis=0)

k = 10 # number of arrays to be combined

perfplot.plot(
    setup=lambda n: [numpy.random.choice(a=[False, True], size=n) for j in range(k)],
    kernels=[
        lambda l: and_recursive(*l),
        lambda l: and_reduce(*l),
        lambda l: and_stack(*l),
        lambda l: or_recursive(*l),
        lambda l: or_reduce(*l),
        lambda l: or_stack(*l),
    ],
    labels = ['and_recursive', 'and_reduce', 'and_stack', 'or_recursive', 'or_reduce', 'or_stack'],
    n_range=[2 ** j for j in range(20)],
    logx=True,
    logy=True,
    xlabel="len(a)",
    equality_check=None
)

Тут нижче показники для k = 4.

Вистави для k = 4

А тут нижче вистави для k = 10.

Вистави для k = 10

Здається, існує приблизно постійний накладний час і для вищих n.

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