我正在寻找最快的方法来检查np.nan
NumPy数组中NaN()的出现X
。np.isnan(X)
毫无疑问,因为它会构建一个shape的布尔数组X.shape
,这可能是巨大的。
我试过了np.nan in X
,但这似乎不起作用,因为np.nan != np.nan
。有没有一种快速且节省内存的方法来做到这一点?
(对于那些问“多么巨大”的人:我不知道。这是库代码的输入验证。)
scipy.sparse
矩阵作为输入。
我正在寻找最快的方法来检查np.nan
NumPy数组中NaN()的出现X
。np.isnan(X)
毫无疑问,因为它会构建一个shape的布尔数组X.shape
,这可能是巨大的。
我试过了np.nan in X
,但这似乎不起作用,因为np.nan != np.nan
。有没有一种快速且节省内存的方法来做到这一点?
(对于那些问“多么巨大”的人:我不知道。这是库代码的输入验证。)
scipy.sparse
矩阵作为输入。
Answers:
雷的解决方案很好。但是,在我的机器上numpy.sum
,代替numpy.min
:使用的速度大约快2.5倍:
In [13]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 244 us per loop
In [14]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 97.3 us per loop
不像min
,sum
不需要分支,而分支在现代硬件上往往非常昂贵。这可能是为什么sum
速度更快的原因。
编辑上面的测试是使用单个NaN在阵列中间进行的。
有趣的min
是,NaNs的存在比NaNs的存在慢。随着NaN越来越接近数组的开始,它似乎也变得越来越慢。另一方面,sum
无论是否存在NaN及其位于何处,的吞吐量似乎都是恒定的:
In [40]: x = np.random.rand(100000)
In [41]: %timeit np.isnan(np.min(x))
10000 loops, best of 3: 153 us per loop
In [42]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop
In [43]: x[50000] = np.nan
In [44]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 239 us per loop
In [45]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.8 us per loop
In [46]: x[0] = np.nan
In [47]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 326 us per loop
In [48]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop
np.min
当数组不包含NaN时,速度更快,这是我的预期输入。但我已经决定无论如何都要接受这一个,因为它抓住inf
并neginf
为好。
inf
或-inf
输入包含两者时才捕获,并且如果输入包含较大但有限的值(将它们加在一起时会溢出),则可能会出现问题。
np.sum
它的速度仍然比上快30%np.min
。
np.isnan(x).any(0)
略快np.sum
和np.min
我的机器上,虽然可能有一些不必要的缓存。
即使存在公认的答案,我也想演示以下内容(在Vista上使用Python 2.7.2和Numpy 1.6.0):
In []: x= rand(1e5)
In []: %timeit isnan(x.min())
10000 loops, best of 3: 200 us per loop
In []: %timeit isnan(x.sum())
10000 loops, best of 3: 169 us per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 134 us per loop
In []: x[5e4]= NaN
In []: %timeit isnan(x.min())
100 loops, best of 3: 4.47 ms per loop
In []: %timeit isnan(x.sum())
100 loops, best of 3: 6.44 ms per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 138 us per loop
因此,真正有效的方法可能在很大程度上取决于操作系统。无论如何,dot(.)
似乎是最稳定的。
x
包含大值时,点乘积有点可能溢出,我也想检查inf。
isfinite(.)
。我只想指出巨大的性能差距。谢谢
这里有两种通用方法:
nan
获取any
。nan
s的累积操作(如sum
)并检查其结果。尽管第一种方法肯定是最干净的,但是对某些累积操作(特别是在BLAS中执行的那些操作)进行大量优化dot
可以使这些操作非常快。请注意dot
,与某些其他BLAS操作一样,它们在某些条件下也是多线程的。这解释了不同机器之间的速度差异。
import numpy
import perfplot
def min(a):
return numpy.isnan(numpy.min(a))
def sum(a):
return numpy.isnan(numpy.sum(a))
def dot(a):
return numpy.isnan(numpy.dot(a, a))
def any(a):
return numpy.any(numpy.isnan(a))
def einsum(a):
return numpy.isnan(numpy.einsum("i->", a))
perfplot.show(
setup=lambda n: numpy.random.rand(n),
kernels=[min, sum, dot, any, einsum],
n_range=[2 ** k for k in range(20)],
logx=True,
logy=True,
xlabel="len(a)",
)
如果您满意 麻巴 它允许创建快速短路(找到NaN时立即停止)功能:
import numba as nb
import math
@nb.njit
def anynan(array):
array = array.ravel()
for i in range(array.size):
if math.isnan(array[i]):
return True
return False
如果没有NaN
该函数,实际上可能会比慢np.min
,这是因为np.min
对大型数组使用了多重处理:
import numpy as np
array = np.random.random(2000000)
%timeit anynan(array) # 100 loops, best of 3: 2.21 ms per loop
%timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.45 ms per loop
%timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.64 ms per loop
但是,如果数组中存在NaN,特别是如果它的位置在低索引处,那么它会快得多:
array = np.random.random(2000000)
array[100] = np.nan
%timeit anynan(array) # 1000000 loops, best of 3: 1.93 µs per loop
%timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.57 ms per loop
%timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.65 ms per loop
用Cython或C扩展可以实现类似的结果,这些结果稍微复杂一些(或容易获得bottleneck.anynan
),但最终与我的anynan
功能相同。