Numpy `logical_or` voor meer dan twee argumenten

Numpy’s logical_orfunctie heeft niet meer dan twee arrays nodig om te vergelijken. Hoe kan ik de vereniging van meer dan twee arrays vinden? (Dezelfde vraag kan worden gesteld met betrekking tot Numpy’s logical_anden het verkrijgen van de kruising van meer dan twee arrays.)


Antwoord 1, autoriteit 100%

Als je een vraag hebt over numpy.logical_or, dan nee, zoals de documenten expliciet zeggen, de enige parameters zijn x1, x2, en optioneel out:

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


Je kunt natuurlijk meerdere logical_oraanroepen als volgt aan elkaar koppelen:

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

De manier om dit soort ketens in NumPy te generaliseren is met reduce:

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

En dit werkt natuurlijk ook als je één multidimensionale array hebt in plaats van afzonderlijke arrays – in feite is dit hoe het bedoeldgebruikt wordt:

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

Maar een tupel van drie 1D-arrays van gelijke lengte is een array_likein NumPy-termen en kan worden gebruikt als een 2D-array.


Buiten NumPy kun je ook Python’s reduce:

gebruiken

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

In tegenstelling tot NumPy’s reduce, is Python’s echter niet vaak nodig. In de meeste gevallen is er een eenvoudigere manier om dingen te doen, bijvoorbeeld om meerdere Python or-operators aan elkaar te koppelen, reduceniet over operator.or_, gebruik gewoon any. En als er nietis, is het meestal beter leesbaar om een ​​expliciete lus te gebruiken.

En in feite NumPy’s anykan ook voor dit geval worden gebruikt, hoewel het niet zo triviaal is; als je het niet expliciet een as geeft, krijg je een scalair in plaats van een array. Dus:

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

Zoals je zou verwachten, logical_andis vergelijkbaar: je kunt het koppelen, np.reduceit, functools.reduceit, of vervangen door allmet een expliciete axis.

Hoe zit het met andere bewerkingen, zoals logical_xor? Nogmaals, dezelfde deal… behalve dat er in dit geval geen all/any-type functie van toepassing is. (Hoe zou je het noemen? odd?)


Antwoord 2, autoriteit 8%

In het geval iemand dit nog steeds nodig heeft – Stel dat je drie Booleaanse arrays a, b, cmet dezelfde vorm hebt, dan geeft dit andelementsgewijs:

a * b * c

dit geeft or:

a + b + c

Is dit wat je wilt?
Veel logical_andof logical_orstapelen is niet praktisch.


Antwoord 3, autoriteit 4%

Voortbouwend op het antwoord van abarnert voor n-dimensionaal geval:

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


Antwoord 4, autoriteit 3%

Omdat booleaanse algebra’s per definitie zowel commutatief als associatief zijn, volgen de volgende uitspraken of equivalenten voor booleaansewaarden van a, b en c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Dus als je een “logische_or” hebt die dyadisch is en je moet er drie argumenten (a, b en c) aan doorgeven, kun je aanroepen

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

of welke permutatie je maar wilt.


Terug naar python, als je wilt testen of een voorwaarde (opgeleid door een functie testdie een testee neemt en een booleaanse waarde retourneert) van toepassing is op a of b of c of op een willekeurig element van de lijst L, je gebruikt normaal gesproken

any(test(x) for x in L)

Antwoord 5, autoriteit 3%

Ik gebruik deze tijdelijke oplossing die kan worden uitgebreid tot n arrays:

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

Antwoord 6, autoriteit 2%

met behulp van de somfunctie:

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)

Antwoord 7, autoriteit 2%

Ik heb de volgende drie verschillende methoden geprobeerd om de logical_andvan een lijst lvan karrays met de grootte n te krijgen :

  1. Een recursieve numpy.logical_andgebruiken (zie hieronder)
  2. Gebruik numpy.logical_and.reduce(l)
  3. numpy.vstack(l).all(axis=0)

Vervolgens deed ik hetzelfde voor de functie logical_or. Verrassend genoeg is de recursieve methode de snelste.

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
)

Hieronder de prestaties voor k = 4.

Prestaties voor k=4

En hieronder de prestaties voor k = 10.

Prestaties voor k=10

Het lijkt erop dat er ook voor hogere n een ongeveer constante tijdoverhead is.


Antwoord 8

Als u een korte (misschien niet optimale) functie wilt voor het uitvoeren van logische AND op multidimensionale booleaanse maskers, kunt u deze recursieve lambda-functie gebruiken:

masks_and = lambda *masks : masks[0] if len(masks) == 1 else masks_and(np.logical_and(masks[0], masks[-1]), *masks[1:-1])
result = masks_and(mask1, mask2, ...)

Je kunt de lambda-functie ook generaliseren voor het toepassen van een willekeurige operator (functie van 2 argumenten) met distributieve eigenschap (zoals vermenigvuldiging/AND, som/OF enzovoort), ervan uitgaande dat de volgorde ook belangrijk is, op objecten zoals deze :

fn2args_reduce = lambda fn2args, *args : args[0] if len(args) == 1 else fn2args_reduce(fn2args, fn2args(args[0], args[1]), *args[2:])
result = fn2args_reduce(np.dot, matrix1, matrix2, ... matrixN)

wat u hetzelfde resultaat geeft als wanneer u @numpy-operator gebruikt):

np.dot(...(np.dot(np.dot(matrix1, matrix2), matrix3)...), matrixN)

Bijvoorbeeld fn2args_reduce(lambda a,b: a+b, 1,2,3,4,5)geeft je 15 – de som van deze getallen (je hebt natuurlijk een veel efficiëntere sumfunctie hiervoor, maar ik vind het leuk).

Een nog algemener model voor functies van N-argumenten zou er als volgt uit kunnen zien:

fnNargs_reduce = lambda fnNargs, N, *args : args[0] if len(args) == 1 else fnNargs_reduce(fnNargs, N, fnNargs(*args[:N]), *args[N:])
fnNargs = lambda x1, x2, x3=neutral, ..., xN=neutral: x1 (?) x2 (?) ... (?) xN

Waar neutraal betekent dat het een neutraal element is voor (?) operator, bijv. 0 voor +, 1 voor * enz.

Waarom? Gewoon voor de lol 🙂


Antwoord 9

a = np.array([True, False, True])
b = np.array([False, False, True])
c = np.array([True, True, True])
d = np.array([True, True, True])
# logical or
lor = (a+b+c+d).astype(bool)
# logical and
land = (a*b*c*d).astype(bool)

Other episodes