是的,对于布尔数组& 和* 是相同的,因为如果两个操作数都是True,则两者都是True,否则False。
您已经发现每个操作都会创建一个临时数组(尽管新的 NumPy 版本可能会在这方面进行优化),因此您有一个临时布尔数组用于每个 <,一个用于 * 或 @987654328 @ 然后你用* 1 创建一个整数数组。如果不使用其他库,您将无法避免这种情况。 NumPy 速度很快,因为它在 C 中执行循环,但这意味着您必须处理临时数组。
但通过额外的库,您实际上可以加快速度并提高内存效率。
麻木:
import numba as nb
import numpy as np
@nb.njit
def numba_func(arr, lower, upper):
res = np.zeros(arr.size, dtype=np.int8)
arr_raveled = arr.ravel()
for idx in range(arr.size):
res[idx] = lower < arr_raveled[idx] < upper
return res.reshape(arr.shape)
>>> numba_func(i, 0.3, 0.4) # sample call
数值表达式
import numexpr as ne
ne.evaluate('((0.3<i)&(i<0.4))*1')
然而 numexpr 更像是一个黑盒,您无法控制它需要多少内存,但在大多数情况下,当您处理多个元素方面的 NumPy 操作时,它非常快速且内存效率更高 比 NumPy。
赛通
我在这里使用 IPython 魔法。如果您不使用 IPython 或 Jupyter,您可能需要自己 cythonize。
%load_ext cython
%%cython
import numpy as np
cimport numpy as cnp
cpdef cnp.int8_t[:] cython_func(double[:] arr, double lower, double upper):
cdef Py_ssize_t idx
cdef cnp.int8_t[:] res = np.empty(len(arr), dtype=np.int8)
for idx in range(len(arr)):
res[idx] = lower < arr[idx] < upper
return res
鉴于我在这里使用了 1D-memoryviews,您需要将其转换为数组并在之后对其进行整形:
np.asarray(cython_func(i.ravel(), 0.3, 0.4)).reshape(i.shape) # sample call
可能有更好的方法来绕过ravel、asarray 和reshape,但这些方法要求您知道数组的维度。
时间
我使用较小的数组,因为我没有太多 RAM,但您可以轻松更改数字:
i = np.random.random((1000, 1000, 10))
%timeit numba_func(i, 0.3, 0.4)
52.1 ms ± 3.08 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit ne.evaluate('((0.3<i)&(i<0.4))*1')
77.1 ms ± 6.59 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.asarray(cython_func(i.ravel(), 0.3, 0.4)).reshape(i.shape)
146 ms ± 3.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit ((0.3<i)&(i<0.4))*1
180 ms ± 2.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)