Numpy `logisches_oder` für mehr als zwei Argumente

87

Die logical_orFunktion von Numpy benötigt nicht mehr als zwei Arrays zum Vergleichen. Wie kann ich die Vereinigung von mehr als zwei Arrays finden? (Dieselbe Frage könnte in Bezug auf Numpys logical_andund den Schnittpunkt von mehr als zwei Arrays gestellt werden.)

user3074893
quelle
5
any()?
Karthikr
Gibt es einen analogen Weg zu irgendeinem ()?
user3074893
@ user3074893: Es ist genau das gleiche Problem. Sie möchten, dass ich meine Antwort erweitere?
abarnert

Antworten:

173

Wenn Sie nachfragen numpy.logical_or, dann nein, wie in den Dokumenten ausdrücklich angegeben, sind die einzigen Parameter x1, x2und optional out:

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


Sie können natürlich mehrere logical_orAnrufe wie folgt verketten:

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

Die Art und Weise, diese Art der Verkettung in NumPy zu verallgemeinern, ist mit reduce:

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

Und natürlich wird dies auch arbeiten , wenn Sie ein mehrdimensionales Array statt separater Arrays in der Tat, das ist , wie es gemeint verwendet werden:

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

Ein Tupel aus drei 1D-Arrays gleicher Länge ist jedoch in NumPy-Begriffen array_like und kann als 2D-Array verwendet werden.


Außerhalb von NumPy können Sie auch Pythons verwenden reduce:

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

Im Gegensatz zu NumPy's reducewird Python jedoch nicht oft benötigt. In den meisten Fällen gibt es eine einfachere Möglichkeit, Dinge zu tun - z. B. mehrere Python- orOperatoren miteinander zu verketten , nicht zu reduceEnde zu gehen operator.or_, sondern nur zu verwenden any. Und wenn dies nicht der Fall ist , ist es normalerweise besser lesbar, eine explizite Schleife zu verwenden.

Tatsächlich kann NumPy's anyauch für diesen Fall verwendet werden, obwohl es nicht ganz so trivial ist. Wenn Sie ihm keine Achse explizit zuweisen, erhalten Sie einen Skalar anstelle eines Arrays. Damit:

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

Wie zu erwarten ist, logical_andist es ähnlich - Sie können es verketten, np.reducees, functools.reducees oder alldurch ein explizites ersetzen axis.

Was ist mit anderen Operationen logical_xor? Wieder das gleiche Geschäft ... außer dass in diesem Fall keine all/ any-Typ-Funktion gilt. (Wie würdest du es nennen odd?)

abarnert
quelle
2
np.logical_or.reduce((x, y, z))war genau das, wonach ich gesucht habe!
Blaylockbk
reduceist keine interne Funktion mehr in Python 3. Verwenden Sie stattdessen:functools.reduce()
Marvin
10

Im Fall , dass jemand noch diese - sagen Sie drei Boolesche Arrays a, b, cmit der gleichen Form, das gibt andelementweise:

a * b * c

das gibt or:

a + b + c

Ist das was du willst? Viel zu stapeln logical_andoder logical_orist nicht praktikabel.

Alex
quelle
6

Da boolesche Algebren per Definition sowohl kommutativ als auch assoziativ sind, gelten die folgenden Aussagen oder Äquivalente für boolesche Werte von a, b und c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Wenn Sie also ein "logisches_oder" haben, das dyadisch ist und drei Argumente (a, b und c) übergeben muss, können Sie es aufrufen

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

oder welche Permutation du magst.


Zurück zu Python: Wenn Sie testen möchten, ob eine Bedingung (die von einer Funktion erhalten wird test, die einen Testempfänger akzeptiert und einen booleschen Wert zurückgibt) für a oder b oder c oder ein Element der Liste L gilt, verwenden Sie normalerweise

any(test(x) for x in L)
Hyperboreus
quelle
Aber Python orist nicht wirklich boolesch or, sowohl weil es mit anderen Werten als bools arbeitet (Rückgabe, awenn aes wahr ist, bsonst), als auch weil es kurzschließt (was bedeutet, a or bdass es wahr ist, während b or aeine Ausnahme ausgelöst wird ).
Abarnert
@abarnert Danke, ich habe meine Antwort bearbeitet, um dies zu berücksichtigen.
Hyperboreus
(Ich bin mir nicht sicher, warum die Leute dies abgelehnt haben, aber ... das OP scheint speziell über boolesche Werte zu sprechen, die er "logische Bedingungen" nennt.)
abarnert
@abarnert Frag mich nicht. Ich bin der Meinung, dass viele Programmierprobleme leichter zu lösen sind, wenn Sie Ihre Mathematik (in diesem Fall boolesche Algebren) im Hintergrund klarstellen.
Hyperboreus
4

Aufbauend auf abargerts Antwort für den n-dimensionalen Fall:

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

Stadtmensch
quelle
4

mit der Summenfunktion:

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
quelle
4

Ich verwende diese Problemumgehung, die auf n Arrays erweitert werden kann:

>>> 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
quelle
1

Ich habe die folgenden drei verschiedenen Methoden versucht , die erhalten logical_andeine Liste l von k - Arrays der Größe n :

  1. Verwenden einer rekursiven numpy.logical_and(siehe unten)
  2. Verwenden von numpy.logical_and.reduce(l)
  3. Verwenden von numpy.vstack(l).all(axis=0)

Dann habe ich dasselbe für die logical_orFunktion gemacht. Überraschenderweise ist die rekursive Methode die schnellste.

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
)

Hier unten die Leistungen für k = 4.

Leistungen für k = 4

Und hier unten die Leistungen für k = 10.

Leistungen für k = 10

Es scheint, dass es auch für höheres n einen annähernd konstanten Zeitaufwand gibt.

Giancarlo Sportelli
quelle