NumPy配列X
でNaN(np.nan
)の発生をチェックする最速の方法を探しています。 np.isnan(X)
は、形状X.shape
のブール配列を作成するため、問題外です。これは潜在的に巨大です。
np.nan in X
を試しましたが、np.nan != np.nan
で動作しないようです。これを行うための高速でメモリ効率の良い方法はありますか?
(「どれほど巨大な」と尋ねる人に:私にはわかりません。これはライブラリコードの入力検証です。)
レイのソリューションは優れています。ただし、私のマシンでは、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
が速い理由です。
edit上記のテストは、配列の中央に1つのNaNを配置して実行されました。
min
は、NaNが存在する場合、存在しない場合よりも遅いことに注意してください。また、NaNが配列の開始点に近づくにつれて遅くなるようです。一方、sum
のスループットは、NaNが存在するかどうか、および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.isnan(np.min(X))
はあなたが望むことをすべきだと思います。
受け入れられた答えがあったとしても、次のことを実証したいと思います(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(.)
ベースは最も安定しているようです。
ここには2つの一般的なアプローチがあります。
nan
を確認し、any
を取得します。nan
s(sum
など)を保持する累積操作を適用し、結果を確認します。最初のアプローチは確かに最もクリーンですが、いくつかの累積操作(特にdot
のようなBLASで実行される操作)の大幅な最適化により、非常に高速になります。 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)",
)
.any()を使用します
if numpy.isnan(myarray).any()
numpy.isfiniteはチェックのためにisnanよりも良いかもしれません
if not np.isfinite(prop).all()
numba に慣れている場合は、高速短絡(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
関数とまったく同じです。
これに関連するのは、NaNの最初の出現を見つける方法の問題です。これは私が知っていることを処理する最も速い方法です:
index = next((i for (i,n) in enumerate(iterable) if n!=n), None)