将 np.all 与嵌套 np.less 短路以在 numpy 中进行大型数组比较
Short-circuiting an np.all with a nested np.less for large array comparisons in numpy
在我当前的代码中(参见 MWE)我遇到了一个瓶颈,我在执行 np.all
时使用嵌套的 np.less
来处理大型二维数组。我知道如果 np.less
中只有一个 false
值,我们可以停止检查,因为索引中的其余值代码将计算为 false
(因为我是 AND-将给定维度的单个索引中的所有值合并在一起)。
有没有 numba 或 numpy 的方法,我可以利用这种“早期 exit/short-circuit”条件在此计算中产生有意义的加速?
MWE 中的倒数第二行是我要加速的内容。请注意 N
和 M
可能非常大,但只有很少的比较会实际评估为 true
。
import numpy as np
N = 10000
M = 10 # Reduced to small value to show that sometimes the comparisons evaluate to 'True'
array = np.random.uniform(low=0.0, high=10.0, size=(N, M))
comparison_array = np.random.uniform(low=0.0, high=10.0, size=(M))
# Can we apply an early exit condition on this?
mask = np.all(np.less(array, comparison_array), axis=-1)
print(f"Number of 'True' comparisons: {np.sum(mask)}")
这里是 numba
版本,开发得足够好,不一定优化:
@numba.njit
def foo(arr, carr):
N, M = arr.shape
mask = np.ones(N, dtype=np.bool_)
for i in range(N):
for j in range(M):
if arr[i,j]>=carr[j]:
mask[i]=False
break
return mask
测试:
In [178]: np.sum(foo(array, comparison_array))
Out[178]: 2
In [179]: np.sum(np.all(np.less(array, comparison_array), axis=1))
Out[179]: 2
时间:
In [180]: timeit np.sum(foo(array, comparison_array))
155 µs ± 6.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [181]: timeit np.sum(np.all(np.less(array, comparison_array), axis=1))
451 µs ± 5.19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
这是一个不错的改进。
在我当前的代码中(参见 MWE)我遇到了一个瓶颈,我在执行 np.all
时使用嵌套的 np.less
来处理大型二维数组。我知道如果 np.less
中只有一个 false
值,我们可以停止检查,因为索引中的其余值代码将计算为 false
(因为我是 AND-将给定维度的单个索引中的所有值合并在一起)。
有没有 numba 或 numpy 的方法,我可以利用这种“早期 exit/short-circuit”条件在此计算中产生有意义的加速?
MWE 中的倒数第二行是我要加速的内容。请注意 N
和 M
可能非常大,但只有很少的比较会实际评估为 true
。
import numpy as np
N = 10000
M = 10 # Reduced to small value to show that sometimes the comparisons evaluate to 'True'
array = np.random.uniform(low=0.0, high=10.0, size=(N, M))
comparison_array = np.random.uniform(low=0.0, high=10.0, size=(M))
# Can we apply an early exit condition on this?
mask = np.all(np.less(array, comparison_array), axis=-1)
print(f"Number of 'True' comparisons: {np.sum(mask)}")
这里是 numba
版本,开发得足够好,不一定优化:
@numba.njit
def foo(arr, carr):
N, M = arr.shape
mask = np.ones(N, dtype=np.bool_)
for i in range(N):
for j in range(M):
if arr[i,j]>=carr[j]:
mask[i]=False
break
return mask
测试:
In [178]: np.sum(foo(array, comparison_array))
Out[178]: 2
In [179]: np.sum(np.all(np.less(array, comparison_array), axis=1))
Out[179]: 2
时间:
In [180]: timeit np.sum(foo(array, comparison_array))
155 µs ± 6.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [181]: timeit np.sum(np.all(np.less(array, comparison_array), axis=1))
451 µs ± 5.19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
这是一个不错的改进。