Numpy `logic_or` для более чем двух аргументов

88

Функция Numpy logical_orтребует для сравнения не более двух массивов. Как найти объединение более двух массивов? (Тот же вопрос можно задать относительно Numpy logical_andи получения пересечения более чем двух массивов.)

user3074893
источник
5
any()?
karthikr
есть ли способ, аналогичный any ()?
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)

Но набор из трех одномерных массивов одинаковой длины является в терминах NumPy array_like и может использоваться как двухмерный массив.


Вне NumPy вы также можете использовать Python reduce :

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

Однако, в отличие от NumPy reduce, Python не часто нужен. В большинстве случаев есть более простой способ сделать что-то - например, связать вместе несколько orоператоров Python , не 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-типа, которая применяется. (Как бы вы это назвали odd?)

Abarnert
источник
2
np.logical_or.reduce((x, y, z))было именно то, что я искал!
blaylockbk
reduceбольше не является внутренней функцией в python 3. Вместо этого используйте:functools.reduce()
marvin 03
10

В случае , если кто - то все - таки нужно это - у вас есть три логических массивов a, b, cс одной и той же формы, что дает andпоэлементно:

a * b * c

это дает or:

a + b + c

Это то, что вы хотите? Укладывать много logical_andили logical_orнецелесообразно.

Alex
источник
6

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

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Итак, если у вас есть "logic_or", который является диадическим, и вам нужно передать ему три аргумента (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 (возвращение, aесли aистинно, в bпротивном случае), так и потому, что он закорачивает (это a or bможет быть True, а b or aвызывает исключение).
abarnert
@abarnert Спасибо, я отредактировал свой ответ, чтобы учесть это.
Hyperboreus
(Я не уверен, почему люди проголосовали против этого, однако… OP, похоже, конкретно говорит о логических значениях, которые он называет «логическими условиями».)
abarnert
@abarnert Не спрашивай меня. Я считаю, что если вы разберетесь с математикой (в данном случае с булевыми алгебрами) на заднем плане, многие проблемы программирования будет легче решить.
Hyperboreus
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 существуют приблизительно постоянные накладные расходы по времени.

Джанкарло Спортелли
источник