Numpy `logical_or` untuk lebih dari dua argumen


90

Fungsi Numpy logical_ortidak membutuhkan lebih dari dua array untuk dibandingkan. Bagaimana saya bisa menemukan gabungan lebih dari dua array? (Pertanyaan yang sama dapat ditanyakan berkenaan dengan Numpy logical_anddan mendapatkan persimpangan lebih dari dua larik.)



apakah ada cara yang dapat dianalogikan dengan ()?
pengguna3074893

@ user3074893: Ini masalah yang persis sama. Anda ingin saya memperluas jawaban saya?
abarnert

Jawaban:


180

Jika Anda bertanya tentang numpy.logical_or, maka tidak, seperti yang dikatakan dokumen secara eksplisit, satu-satunya parameter adalah x1, x2, dan secara opsional out:

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


Anda tentu saja dapat menyatukan beberapa logical_orpanggilan seperti ini:

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

Cara untuk menggeneralisasi rangkaian jenis ini di NumPy adalah dengan reduce:

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

Dan tentu saja ini juga akan berfungsi jika Anda memiliki satu larik multi-dimensi, bukan larik terpisah — faktanya, begitulah maksud penggunaannya:

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

Tapi tuple dari tiga array 1D yang sama panjangnya adalah array_like dalam istilah NumPy, dan dapat digunakan sebagai array 2D.


Di luar NumPy, Anda juga dapat menggunakan Python reduce:

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

Namun, tidak seperti NumPy reduce, Python tidak sering dibutuhkan. Untuk kebanyakan kasus, ada cara yang lebih sederhana untuk melakukan sesuatu — misalnya, untuk menyatukan beberapa oroperator Python , jangan reduceselesai operator.or_, cukup gunakan any. Dan ketika ada tidak , itu biasanya lebih mudah dibaca menggunakan loop eksplisit.

Dan nyatanya NumPy's anybisa digunakan untuk kasus ini juga, meski tidak sepele; jika Anda tidak secara eksplisit memberikan sumbu, Anda akan berakhir dengan skalar, bukan array. Begitu:

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

As you might expect, logical_and is similar—you can chain it, np.reduce it, functools.reduce it, or substitute all with an explicit axis.

What about other operations, like logical_xor? Again, same deal… except that in this case there is no all/any-type function that applies. (What would you call it? odd?)


3
np.logical_or.reduce((x, y, z)) was just what I was looking for!
blaylockbk

reduce is no longer an internal function in python 3. Instead use: functools.reduce()
marvin

10

In case someone still need this - Say you have three Boolean arrays a, b, c with the same shape, this gives and element-wise:

a * b * c

this gives or:

a + b + c

Is this what you want? Stacking a lot of logical_and or logical_or is not practical.


6

As boolean algebras are both commutative and associative by definition, the following statements or equivalent for boolean values of a, b and c.

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

So if you have a "logical_or" which is dyadic and you need to pass it three arguments (a, b, and c), you can call

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

or whatever permutation you like.


Back to python, if you want to test whether a condition (yielded by a function test that takes a testee and returns a boolean value) applies to a or b or c or any element of list L, you normally use

any(test(x) for x in L)

But Python or isn't really boolean or, both because it works on values other than bools (returning a if a is truthy, b otherwise), and because it short-circuits (meaning a or b may be True, while b or a raises an exception).
abarnert

@abarnert Thank you, I have edited my answer to account for that.
Hyperboreus

(I'm not sure why people downvoted this, however… the OP seems to be specifically talking about boolean values, which he calls "logical conditions".)
abarnert

@abarnert Don't ask me. I am of the opinion that if you get your maths straight (in this case boolean algebras) in the background, a lot of programming issues are easier to solve.
Hyperboreus

5

Building on abarnert's answer for n-dimensional case:

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


4

using the sum function:

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

I use this workaround which can be extended to 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)

2

I've tried the following three different methods to get the logical_and of a list l of k arrays of size n:

  1. Using a recursive numpy.logical_and (see below)
  2. Using numpy.logical_and.reduce(l)
  3. Using numpy.vstack(l).all(axis=0)

Then I did the same for the logical_or function. Surprisingly enough, the recursive method is the fastest one.

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
)

Here below the performances for k = 4.

Performances for k=4

And here below the performances for k = 10.

Performances for k=10

It seems that there is an approximately constant time overhead also for higher n.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.