Numpy `logical_or` para mais de dois argumentos

88

A logical_orfunção de Numpy leva não mais do que dois arrays para comparar. Como posso encontrar a união de mais de duas matrizes? (A mesma pergunta pode ser feita em relação ao Numpy logical_ande obter a interseção de mais de duas matrizes.)

user3074893
fonte
5
any()?
karthikr
existe uma forma análoga a qualquer ()?
user3074893
@ user3074893: É exatamente o mesmo problema. Você quer que eu expanda minha resposta?
abarnert

Respostas:

174

Se você está perguntando sobre numpy.logical_or, então não, como os documentos dizem explicitamente, os únicos parâmetros são x1, x2, e opcionalmente out:

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


Claro, você pode encadear várias logical_orchamadas como esta:

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

A maneira de generalizar este tipo de encadeamento no NumPy é com reduce:

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

E é claro que isso também funcionará se você tiver um array multidimensional em vez de arrays separados - na verdade, é assim que ele deve ser usado:

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

Mas uma tupla de três matrizes 1D de comprimento igual é um array_like em termos NumPy e pode ser usada como uma matriz 2D.


Fora do NumPy, você também pode usar Python reduce:

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

No entanto, ao contrário do NumPy reduce, o Python não é frequentemente necessário. Para a maioria dos casos, há uma maneira mais simples de fazer as coisas - por exemplo, encadear vários oroperadores Python , não reducesobre operator.or_, apenas use any. E quando não , geralmente é mais legível usar um loop explícito.

E, de fato, o NumPy anypode ser usado para este caso também, embora não seja tão trivial; se você não der explicitamente um eixo, você acabará com um escalar em vez de um array. Então:

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

Como você pode esperar, logical_andé semelhante - você pode encadear, np.reduceit, functools.reduceit ou substituir allpor um explicit axis.

E quanto a outras operações, como logical_xor? Novamente, o mesmo negócio ... exceto que, neste caso, não há função all/ any-type que se aplica. (Como você chamaria odd?)

abarnert
fonte
2
np.logical_or.reduce((x, y, z))era exatamente o que eu estava procurando!
blaylockbk
reducenão é mais uma função interna no python 3. Em vez disso, use:functools.reduce()
marvin
10

No caso de alguém ainda precisa disso - Digamos que você tenha três matrizes de booleanos a, b, ccom a mesma forma, isso dá andelemento-wise:

a * b * c

isso dá or:

a + b + c

É isso que voce quer? Empilhar muito logical_andou logical_ornão é prático.

Alex
fonte
6

Como as álgebras booleanas são comutativas e associativas por definição, as seguintes instruções ou equivalentes para valores booleanos de a, be c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Então, se você tiver um "lógico_ou" que é diádico e precisar passar três argumentos (a, b e c), você pode chamar

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

ou qualquer permutação que você quiser.


De volta ao python, se você quiser testar se uma condição (produzida por uma função testque leva um testado e retorna um valor booleano) se aplica a a ou b ou c ou qualquer elemento da lista L, você normalmente usa

any(test(x) for x in L)
Hyperboreus
fonte
Mas o Python ornão é realmente booleano or, tanto porque funciona com valores diferentes de bools (retornar aif aé verdadeiro, bcaso contrário) e porque causa curto-circuito (o significado a or bpode ser True, enquanto b or agera uma exceção).
abarnert
@abarnert Obrigado, editei minha resposta para explicar isso.
Hyperboreus
(Não tenho certeza de por que as pessoas votaram contra isso, no entanto ... o OP parece estar falando especificamente sobre valores booleanos, que ele chama de "condições lógicas".)
abarnert
@abarnert Não me pergunte. Eu sou da opinião que se você fizer sua matemática corretamente (neste caso, álgebras booleanas) em segundo plano, muitos problemas de programação serão mais fáceis de resolver.
Hyperboreus
4

Com base na resposta da abarnert para o caso n-dimensional:

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

Citynorman
fonte
4

usando a função soma:

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)
alexk
fonte
4

Eu uso esta solução alternativa que pode ser estendida para n matrizes:

>>> 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)
Giovanni Tardini
fonte
1

Tentei os três métodos diferentes a seguir para obter o logical_andde uma lista l de k arrays de tamanho n :

  1. Usando um recursivo numpy.logical_and(veja abaixo)
  2. Usando numpy.logical_and.reduce(l)
  3. Usando numpy.vstack(l).all(axis=0)

Então fiz o mesmo para a logical_orfunção. Surpreendentemente, o método recursivo é o mais rápido.

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
)

Aqui, abaixo, os desempenhos para k = 4.

Performances para k = 4

E aqui abaixo os desempenhos para k = 10.

Performances para k = 10

Parece que há uma sobrecarga de tempo aproximadamente constante também para n mais alto.

Giancarlo Sportelli
fonte