NumPyでのNaNの高速チェック


120

np.nanNumPy配列内のNaN()の発生を確認する最速の方法を探していXます。それは、潜在的に巨大なnp.isnan(X)shapeのブール配列を構築するので、問題外ですX.shape

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

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


このシナリオでは、ユーザー入力の検証が機能しませんか?挿入前のNaNのチェックのように
Woot4Moo

@ Woot4Moo:いいえ、ライブラリはNumPy配列またはscipy.sparse行列を入力として受け取ります。
Fred Foo

2
あなたがこれをたくさんやっているなら、私はボトルネック(pypi.python.org/pypi/Bottleneck)について良いことを聞いたことがあります
matt

回答:


160

レイのソリューションは良いです。ただし、私のマシンではnumpy.sum、の代わりに使用する方が約2.5倍高速ですnumpy.min

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

とは異なりminsum最新のハードウェアではかなり高価になる傾向がある分岐は必要ありません。これがおそらくsum速い理由です。

編集上記のテストは、配列の中央にある単一のNaNで実行されました。

minNaNが存在する場合は、存在しない場合よりもが遅いことに注意してください。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

1
np.min配列にNaNが含まれていない場合は、より高速です。これは、私の予想される入力です。それがキャッチので、しかし、私は、とにかくこれを受け入れることを決めたinfneginf同様。
Fred Foo

2
これはキャッチinfする-infか、入力に両方が含まれている場合にのみ発生します。また、入力に大きな値が含まれている場合に加算するとオーバーフローする有限の値が含まれる場合に問題があります。
user2357112は、モニカの2013

4
sse対応のx86チップでは、浮動小数点データのために最小値と最大値を分岐する必要はありません。したがって、numpyの時点で1.8分は合計よりも遅くなりません。私のamdフェノムでは、さらに20%速くなります。
jtaylor

1
OSXのnumpy 1.9.2を搭載した私のIntel Core i5では、それnp.sumよりも約30%高速ですnp.min
マシューブレット

np.isnan(x).any(0)よりわずかに速いですnp.sumし、np.minいくつかの不要なキャッシュがあるかもしれませんが、私のマシン上で。
jsignell

28

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


うーん... O(1)(一部の配列の場合)の場合、これは常にO(n)です。
user48956 2017年

17

受け入れられた回答が存在する場合でも、私は以下をデモンストレーションしたいと思います(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(.)ベースのものが最も安定しているようです。


1
基盤となるBLASの実装やCコンパイラに依存しているように、OSにはそれほど依存していないと思います。ありがとうございます。しかし、ドット積は、x大きな値が含まれていると少しだけオーバーフローする可能性が高く、INF もチェックしたいと思います。
Fred Foo

1
さて、あなたはいつでもドット積を使ってドット積を行うことができますisfinite(.)。パフォーマンスの大きなギャップを指摘したかっただけです。ありがとう
食べる

私のマシンでも同じです。
kawing-chiu 2016

1
賢い、違う?フレッド・フーが示唆、ドット積ベースのアプローチのいずれかの効率向上は、ほぼ確実にATLAS、MKL、またはOpenBLASなどの最適化BLASの実装に対してリンクローカルnumpyのインストールに感謝しています。これは、例えば、アナコンダの場合です。そのため、このドット積は、すべての利用可能なコア全体で並列化されます。同じことは、単一コアに限定して実行されるmin-または- sumベースのアプローチについては言えません。エルゴ、そのパフォーマンスのギャップ。
セシルカレー

16

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

  • 各配列項目をチェックしてnan、を取得しanyます。
  • nans を保持する累積演算を適用して(など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)",
)

4
  1. .any()を使用

    if numpy.isnan(myarray).any()

  2. numpy.isfiniteはisnanよりもチェックに優れている

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


3

あなたが快適なら これにより、高速短絡(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的には私の機能と同じです。


1

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

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.