簡體   English   中英

用於兩個以上參數的 Numpy `logical_or`

[英]Numpy `logical_or` for more than two arguments

Numpy 的logical_or函數最多需要兩個數組進行比較。 如何找到兩個以上數組的並集? (關於 Numpy 的logical_and和獲得兩個以上數組的交集,可以提出同樣的問題。)

如果您要詢問numpy.logical_or ,那么不,正如文檔明確指出的那樣,唯一的參數是x1, x2和可選out

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


您當然可以將多個logical_or調用鏈接在一起,如下所示:

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

在 NumPy 中泛化這種鏈接的方法是使用reduce

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

當然,如果你有一個多維數組而不是單獨的數組,這也將起作用——事實上,這就是它的用途:

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

但是三個等長一維數組的元組在NumPy術語中是一個類似數組的數組,可以用作二維數組。


在 NumPy 之外,您還可以使用 Python 的reduce

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

但是,與 NumPy 的reduce不同,Python 並不經常需要。 對於大多數情況,有一種更簡單的方法來做事——例如,將多個 Python or運算符鏈接在一起,不要通過operator.or_進行reduce ,只需使用any 沒有時,使用顯式循環通常更具可讀性。

事實上,NumPy 的any也可以用於這種情況,盡管它不是那么簡單; 如果你沒有明確地給它一個軸,你最終會得到一個標量而不是一個數組。 所以:

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

如你所料, logical_and是相似的——你可以鏈接它, np.reduce它, functools.reduce它,或者用一個明確的axis替換all

其他操作呢,比如logical_xor 同樣,同樣的交易......除了在這種情況下沒有適用的all / any類型的函數。 (你會怎么稱呼它? odd ?)

如果有人仍然需要這個 - 假設你有三個布爾數組abc具有相同的形狀,這給出and元素方面的:

a * b * c

這給出or

a + b + c

這是你想要的嗎? 堆疊大量的logical_andlogical_or是不切實際的。

基於 abarnert 對 n 維案例的回答:

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

由於布爾代數在定義上既是交換的又是結合的,以下語句或等效於布爾值 a、b 和 c。

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

因此,如果您有一個二元的“logical_or”並且您需要向它傳遞三個參數(a、b 和 c),您可以調用

logical_or(logical_or(a, b), c)

logical_or(a, logical_or(b, c))

logical_or(c, logical_or(b, a))

或任何你喜歡的排列。


回到 python,如果你想測試一個條件(由一個接受被測試者並返回一個布爾值的函數test產生)是否適用於 a 或 b 或 c 或列表 L 的任何元素,你通常使用

any(test(x) for x in L)

我使用這個可以擴展到 n 個數組的解決方法:

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

我嘗試了以下三種不同的方法來獲取大小為nk個數組列表llogical_and

  1. 使用遞歸numpy.logical_and (見下文)
  2. 使用numpy.logical_and.reduce(l)
  3. 使用numpy.vstack(l).all(axis=0)

然后我對logical_or函數做了同樣的事情。 令人驚訝的是,遞歸方法是最快的一種。

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
)

下面是 k = 4 的性能。

k=4 的性能

下面是 k = 10 的表現。

k=10 的性能

似乎對於較高的 n 也存在近似恆定的時間開銷。

使用求和函數:

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)

如果您想要一個簡短的(可能不是最佳的)函數來對多維布爾掩碼執行邏輯與,您可以使用這個遞歸 lambda 函數:

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

假設順序也很重要,您還可以將 lambda 函數推廣到應用具有分配屬性(例如乘法/AND、sum/OR 等)的任何運算符(2 個參數的函數)到任何像這樣的對象:

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)

這給您與使用@ numpy 運算符相同的結果):

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

例如fn2args_reduce(lambda a,b: a+b, 1,2,3,4,5)給你 15 - 這些數字的總和(當然你有一個更有效的sum函數,但我喜歡它) .

N 個參數的函數的更通用模型可能如下所示:

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

中性意味着它是(?)運算符的中性元素,例如。 0 表示 +,1 表示 * 等。

為什么? 純娛樂 :-)

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM