多于两个参数的 Numpy‘ logic_or’

Numpy 的 logical_or函数只需要两个数组进行比较。如何找到两个以上数组的并集?(关于 Numpy 的 logical_and和获得两个以上数组的交集,也可以提出同样的问题。)

72126 次浏览

由于布尔代数根据定义既是交换代数又是结合代数,下面的语句或等价于 a、 b 和 c 的 布尔型值。

a or b or c

(a or b) or c

a or (b or c)

(b or a) or c

因此,如果您有一个二进制的“ Logic _ 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生成)是否应用于列表 L 的 a、 b、 c 或任何元素,通常使用

any(test(x) for x in L)

如果您询问的是 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)

但是一个由三个等长1D 数组组成的元组在 NumPy 术语中是 类似 array _ like,可以用作2D 数组。


除了 NumPy,您还可以使用 Python 的 reduce:

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

然而,与 NumPy 的 reduce不同,并不经常需要 Python 的 reduce。在大多数情况下,有一种更简单的方法来做事情ーー例如,将多个 Python or运算符链接在一起,不要使用 reduce而使用 operator.or_,只需使用 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?)

基于 abarnert 对 n 维情形的回答:

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

如果有人仍然需要这个-假设你有三个布尔数组 abc具有相同的形状,这给了 and元素明智的:

a * b * c

这给了 or:

a + b + c

这就是你想要的吗? 堆叠大量的 logical_andlogical_or是不切实际的。

使用和函数:

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)

我使用这个可以扩展到 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数组的 列表的 logical_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的表现。

Performances for k=4

下面是 k = 10的表现。

Performances for k=10

对于更高的 n,似乎有一个近似恒定的时间开销。

如果你想要一个简短的(也许不是最优的)函数来对多维布尔掩码执行逻辑 AND,你可以使用这个递归的 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 函数推广到任何带分配律的运算符(2个参数的函数)(比如乘/AND,sum/OR 等等) ,假设顺序也很重要,应用到任何类似下面这样的对象:

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)