web-dev-qa-db-ja.com

NumPyでのNaNの高速チェック

NumPy配列XでNaN(np.nan)の発生をチェックする最速の方法を探しています。 np.isnan(X)は、形状X.shapeのブール配列を作成するため、問題外です。これは潜在的に巨大です。

np.nan in Xを試しましたが、np.nan != np.nanで動作しないようです。これを行うための高速でメモリ効率の良い方法はありますか?

(「どれほど巨大な」と尋ねる人に:私にはわかりません。これはライブラリコードの入力検証です。)

103
Fred Foo

レイのソリューションは優れています。ただし、私のマシンでは、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
142
NPE

np.isnan(np.min(X))はあなたが望むことをすべきだと思います。

24
Ray

受け入れられた答えがあったとしても、次のことを実証したいと思います(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(.)ベースは最も安定しているようです。

17
eat

ここには2つの一般的なアプローチがあります。

  • 各配列項目のnanを確認し、anyを取得します。
  • nans(sumなど)を保持する累積操作を適用し、結果を確認します。

最初のアプローチは確かに最もクリーンですが、いくつかの累積操作(特にdotのようなBLASで実行される操作)の大幅な最適化により、非常に高速になります。 dotは、他のBLAS操作と同様に、特定の条件下でマルチスレッド化されることに注意してください。これは、異なるマシン間の速度の違いを説明しています。

enter image description here

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)",
)
9
Nico Schlömer
  1. .any()を使用します

    if numpy.isnan(myarray).any()

  2. numpy.isfiniteはチェックのためにisnanよりも良いかもしれません

    if not np.isfinite(prop).all()

3
woso

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関数とまったく同じです。

3
MSeifert

これに関連するのは、NaNの最初の出現を見つける方法の問題です。これは私が知っていることを処理する最も速い方法です:

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
1
vitiral