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.