Numpy `logical_or` pour plus de deux arguments

88

La logical_orfonction de Numpy ne prend pas plus de deux tableaux à comparer. Comment puis-je trouver l'union de plus de deux tableaux? (La même question pourrait être posée en ce qui concerne Numpy logical_andet l'obtention de l'intersection de plus de deux tableaux.)

utilisateur3074893
la source
5
any()?
karthikr du
existe-t-il un moyen analogue à tout ()?
user3074893
@ user3074893: C'est exactement le même problème. Vous voulez que j'élargisse ma réponse?
abarnert

Réponses:

174

Si vous posez des questions numpy.logical_or, alors non, comme le disent explicitement la documentation, les seuls paramètres sont x1, x2, et éventuellement out:

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


Vous pouvez bien sûr enchaîner plusieurs logical_orappels comme ceci:

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

La façon de généraliser ce type de chaînage dans NumPy est avec reduce:

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

Et bien sûr, cela fonctionnera également si vous avez un tableau multidimensionnel au lieu de tableaux séparés - en fait, c'est ainsi qu'il est censé être utilisé:

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

Mais un tuple de trois tableaux 1D de même longueur est un array_like en termes NumPy, et peut être utilisé comme un tableau 2D.


En dehors de NumPy, vous pouvez également utiliser Python reduce:

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

Cependant, contrairement à NumPy reduce, Python n'est pas souvent nécessaire. Dans la plupart des cas, il existe un moyen plus simple de faire les choses - par exemple, pour enchaîner plusieurs oropérateurs Python , ne pas reduceterminer operator.or_, utilisez simplement any. Et quand il n'y en a pas , il est généralement plus lisible d'utiliser une boucle explicite.

Et en fait, NumPy's anypeut également être utilisé dans ce cas, bien que ce ne soit pas aussi trivial; si vous ne lui donnez pas explicitement un axe, vous vous retrouverez avec un scalaire au lieu d'un tableau. Donc:

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

Comme vous pouvez vous y attendre, logical_andc'est similaire - vous pouvez l'enchaîner, np.reducele, functools.reducele, ou le remplacer allpar un explicite axis.

Qu'en est-il des autres opérations, comme logical_xor? Encore une fois, même accord… sauf que dans ce cas, il n'y a pas de fonction all/ any-type qui s'applique. (Comment appelleriez-vous cela odd??)

Abarnert
la source
2
np.logical_or.reduce((x, y, z))était exactement ce que je cherchais!
blaylockbk
reducen'est plus une fonction interne en python 3. Utilisez plutôt:functools.reduce()
marvin
10

Dans le cas où quelqu'un encore besoin de ce - que vous avez trois tableaux booléens a, b, cavec la même forme, ce qui donne andélément par élément:

a * b * c

cela donne or:

a + b + c

c'est ce que tu veux? Empiler beaucoup logical_andou logical_orn'est pas pratique.

Alex
la source
6

Comme les algèbres booléennes sont à la fois commutatives et associatives par définition, les instructions suivantes ou l'équivalent pour les valeurs booléennes de a, b et c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Donc, si vous avez un "ou_logique" qui est dyadique et que vous devez lui passer trois arguments (a, b et c), vous pouvez appeler

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

ou n'importe quelle permutation que vous aimez.


De retour à python, si vous voulez tester si une condition (générée par une fonction testqui prend un testee et renvoie une valeur booléenne) s'applique à a ou b ou c ou à tout élément de la liste L, vous utilisez normalement

any(test(x) for x in L)
Hyperborée
la source
Mais Python orn'est pas vraiment booléen or, à la fois parce qu'il fonctionne sur des valeurs autres que bools (renvoyer asi aest vrai, bsinon) et parce qu'il court-circuite (la signification a or bpeut être True, tandis que b or adéclenche une exception).
abarnert
@abarnert Merci, j'ai édité ma réponse pour en tenir compte.
Hyperboreus
(Je ne sais pas pourquoi les gens ont voté contre cela, cependant ... l'OP semble parler spécifiquement de valeurs booléennes, qu'il appelle des "conditions logiques".)
abarnert
@abarnert Ne me demandez pas. Je suis d'avis que si vous mettez vos mathématiques au clair (dans ce cas les algèbres booléennes) en arrière-plan, de nombreux problèmes de programmation sont plus faciles à résoudre.
Hyperboreus
4

S'appuyant sur la réponse d'Abarnert pour le cas à n dimensions:

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

citynorman
la source
4

en utilisant la fonction somme:

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
la source
4

J'utilise cette solution de contournement qui peut être étendue à n tableaux:

>>> 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
la source
1

J'ai essayé les trois méthodes suivantes pour obtenir logical_andune liste l de k tableaux de taille n :

  1. Utilisation d'un récursif numpy.logical_and(voir ci-dessous)
  2. En utilisant numpy.logical_and.reduce(l)
  3. En utilisant numpy.vstack(l).all(axis=0)

Ensuite, j'ai fait la même chose pour la logical_orfonction. Curieusement, la méthode récursive est la plus rapide.

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
)

Ci-dessous les performances pour k = 4.

Performances pour k = 4

Et voici ci-dessous les performances pour k = 10.

Performances pour k = 10

Il semble qu'il y ait un temps système à peu près constant également pour n supérieur.

Giancarlo Sportelli
la source