Numpy `logic_or` per più di due argomenti


90

La logical_orfunzione di Numpy non richiede più di due array per il confronto. Come posso trovare l'unione di più di due array? (La stessa domanda potrebbe essere posta riguardo a Numpy logical_ande all'ottenimento dell'intersezione di più di due array.)



c'è un modo analogo a any ()?
user3074893

@ user3074893: È esattamente lo stesso problema. Vuoi che espanda la mia risposta?
abarnert

Risposte:


180

Se stai chiedendo informazioni numpy.logical_or, allora no, come dicono esplicitamente i documenti, gli unici parametri sono x1, x2, e facoltativamente out:

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


Ovviamente puoi concatenare più logical_orchiamate in questo modo:

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

Il modo per generalizzare questo tipo di concatenamento in NumPy è con reduce:

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

E naturalmente questo funziona anche se si dispone di un array multi-dimensionale, invece di array, anzi separati, è così che è destinata ad essere utilizzati:

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

Ma una tupla di tre array 1D di uguale lunghezza è un array_like in termini di NumPy e può essere usata come array 2D.


Al di fuori di NumPy, puoi anche usare Python reduce:

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

Tuttavia, a differenza di NumPy reduce, spesso non è necessario Python. Per la maggior parte dei casi, c'è un modo più semplice per fare le cose, ad esempio per concatenare più oroperatori Python , non reducefinire operator.or_, usa e basta any. E quando non c'è , di solito è più leggibile usare un ciclo esplicito.

E infatti NumPy anypuò essere utilizzato anche in questo caso, sebbene non sia così banale; se non gli dai esplicitamente un asse, ti ritroverai con uno scalare invece di un array. Così:

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

Come ci si potrebbe aspettare, logical_andè simile: puoi concatenarlo, np.reduceesso, functools.reduceesso o sostituirlo allcon un esplicito axis.

E le altre operazioni, come logical_xor? Ancora una volta, stesso affare ... tranne che in questo caso non esiste una funzione all/ any-type che si applica. (Come lo chiameresti odd??)


3
np.logical_or.reduce((x, y, z))era proprio quello che stavo cercando!
blaylockbk

reducenon è più una funzione interna in python 3. Usa invece:functools.reduce()
marvin

10

Nel caso in cui qualcuno ancora bisogno di questo - dire di avere tre array booleani a, b, ccon la stessa forma, questo dà andelemento-saggio:

a * b * c

questo dà or:

a + b + c

È questo che vuoi? Impilare molto logical_ando logical_ornon è pratico.


6

Poiché le algebre booleane sono sia commutative che associative per definizione, le seguenti istruzioni o equivalenti per i valori booleani di a, be c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

Quindi, se hai un "logico_or" che è diadico e devi passargli tre argomenti (a, b e c), puoi chiamare

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

o qualunque permutazione ti piaccia.


Tornando a python, se vuoi verificare se una condizione (prodotta da una funzione testche accetta un testato e restituisce un valore booleano) si applica a aob o c oa qualsiasi elemento della lista L, normalmente usi

any(test(x) for x in L)

Ma Python ornon è realmente booleano or, sia perché funziona su valori diversi da bools (restituendo ase aè vero, baltrimenti), sia perché cortocircuita (il significato a or bpuò essere True, mentre b or asolleva un'eccezione).
abarnert

@abarnert Grazie, ho modificato la mia risposta per tenerne conto.
Hyperboreus

(Non sono sicuro del motivo per cui le persone hanno votato in modo negativo, tuttavia ... l'OP sembra parlare specificamente di valori booleani, che chiama "condizioni logiche".)
abarnert

@abarnert Non chiedermelo. Sono dell'opinione che se si fanno chiarezza sulla matematica (in questo caso le algebre booleane) in background, molti problemi di programmazione sono più facili da risolvere.
Hyperboreus

5

Basandosi sulla risposta di abarnert per il caso n-dimensionale:

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


4

utilizzando la funzione somma:

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

Uso questa soluzione alternativa che può essere estesa a n array:

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

2

Ho provato i seguenti tre metodi diversi per ottenere l' logical_andelenco l di k array di dimensione n :

  1. Utilizzando un ricorsivo numpy.logical_and(vedi sotto)
  2. Utilizzando numpy.logical_and.reduce(l)
  3. Utilizzando numpy.vstack(l).all(axis=0)

Poi ho fatto lo stesso per la logical_orfunzione. Sorprendentemente, il metodo ricorsivo è il più veloce.

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
)

Di seguito le prestazioni per k = 4.

Prestazioni per k = 4

E qui sotto le prestazioni per k = 10.

Prestazioni per k = 10

Sembra che ci sia un overhead di tempo approssimativamente costante anche per n.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.