Pemeriksaan cepat untuk NaN di NumPy

120

Saya mencari cara tercepat untuk memeriksa terjadinya NaN ( np.nan) dalam array NumPy X. np.isnan(X)tidak mungkin, karena ia membangun larik bentuk boolean X.shape, yang berpotensi sangat besar.

Saya mencoba np.nan in X, tetapi tampaknya tidak berhasil karena np.nan != np.nan. Apakah ada cara yang cepat dan hemat memori untuk melakukan ini sama sekali?

(Untuk mereka yang bertanya "seberapa besar": Saya tidak tahu. Ini adalah validasi input untuk kode perpustakaan.)

Fred Foo
sumber
apakah memvalidasi input pengguna tidak berfungsi dalam skenario ini? Seperti dalam memeriksa NaN sebelum penyisipan
Woot4Moo
@ Woot4Moo: tidak, perpustakaan mengambil array atau scipy.sparsematriks NumPy sebagai input.
Fred Foo
2
Jika Anda sering melakukan ini, saya telah mendengar hal-hal baik tentang Hambatan ( pypi.python.org/pypi/Bottleneck )
matt

Jawaban:

161

Solusi Ray bagus. Namun, di komputer saya, ini sekitar 2,5x lebih cepat untuk digunakan numpy.sumsebagai pengganti 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

Berbeda dengan min, sumtidak memerlukan percabangan, yang pada perangkat keras modern cenderung cukup mahal. Ini mungkin alasan mengapa sumlebih cepat.

sunting Tes di atas dilakukan dengan satu NaN tepat di tengah-tengah array.

Menarik untuk dicatat bahwa minkeberadaan NaN lebih lambat dibandingkan saat tidak ada. Tampaknya juga menjadi lebih lambat karena NaN semakin mendekati awal larik. Di sisi lain, sumthroughput tampak konstan terlepas dari apakah ada NaN dan di mana lokasinya:

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
NPE
sumber
1
np.minlebih cepat ketika array tidak berisi NaN, yang merupakan masukan yang saya harapkan. Tetapi saya telah memutuskan untuk menerima yang ini, karena itu menangkap infdan neginfjuga.
Fred Foo
2
Ini hanya menangkap infatau -infjika masukan berisi keduanya, dan akan bermasalah jika masukan berisi nilai besar tapi terbatas yang meluap saat ditambahkan bersama.
user2357112 mendukung Monica
4
min dan max tidak perlu melakukan branch data floating point pada chip x86 yang mendukung sse. Jadi pada numpy 1,8 menit tidak akan lebih lambat dari jumlah, pada amd phenom saya bahkan 20% lebih cepat.
jtaylor
1
Di Intel Core i5 saya, dengan numpy 1.9.2 di OSX, np.summasih sekitar 30% lebih cepat daripada np.min.
Matthew Brett
np.isnan(x).any(0)sedikit lebih cepat daripada np.sumdan np.mindi komputer saya, meskipun mungkin ada beberapa cache yang tidak diinginkan.
jsignell
28

Saya pikir np.isnan(np.min(X))harus melakukan apa yang Anda inginkan.

sinar
sumber
Hmmm ... ini selalu O (n) bila bisa O (1) (untuk beberapa array).
pengguna48956
17

Bahkan ada jawaban yang diterima, saya ingin menunjukkan yang berikut (dengan Python 2.7.2 dan Numpy 1.6.0 di Vista):

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

Jadi, cara yang sangat efisien mungkin sangat bergantung pada sistem operasi. Pokoknya dot(.)berbasis tampaknya yang paling stabil.

makan
sumber
1
Saya menduga itu tidak terlalu tergantung pada OS, seperti pada implementasi BLAS yang mendasari dan compiler C. Terima kasih, tetapi produk titik hanya sedikit lebih cenderung meluap ketika xberisi nilai besar, dan saya juga ingin memeriksa inf.
Fred Foo
1
Nah, Anda selalu dapat melakukan perkalian titik dengan satu dan penggunaan isfinite(.). Saya hanya ingin menunjukkan kesenjangan kinerja yang besar. Terima kasih
makan
Hal yang sama di mesin saya.
kawing-chiu
1
Pintar, bukan? Seperti yang disarankan Fred Foo , keuntungan efisiensi apa pun dari pendekatan berbasis produk titik hampir pasti berkat instalasi NumPy lokal yang ditautkan ke implementasi BLAS yang dioptimalkan seperti ATLAS, MKL, atau OpenBLAS. Ini adalah kasus Anaconda, misalnya. Mengingat, perkalian titik ini akan diparalelkan di semua inti yang tersedia. Hal yang sama tidak dapat dikatakan untuk pendekatan berbasis- minatau sum, yang berjalan terbatas pada satu inti. Ergo, kesenjangan kinerja itu.
Cecil Curry
16

Ada dua pendekatan umum di sini:

  • Periksa setiap item array untuk nandan ambil any.
  • Terapkan beberapa operasi kumulatif yang mempertahankan nans (suka sum) dan periksa hasilnya.

Meskipun pendekatan pertama tentu yang paling bersih, pengoptimalan yang berat dari beberapa operasi kumulatif (terutama yang dijalankan di BLAS, seperti dot) dapat membuatnya cukup cepat. Perhatikan bahwa dot, seperti beberapa operasi BLAS lainnya, multithread dalam kondisi tertentu. Ini menjelaskan perbedaan kecepatan antara mesin yang berbeda.

masukkan deskripsi gambar di sini

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)",
)
Nico Schlömer
sumber
4
  1. gunakan .any ()

    if numpy.isnan(myarray).any()

  2. numpy.isfinite mungkin lebih baik daripada isnan untuk diperiksa

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

woso
sumber
3

Jika Anda merasa nyaman dengan memungkinkan untuk membuat korsleting cepat (berhenti segera setelah NaN ditemukan) fungsi:

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

Jika tidak ada NaNfungsi yang sebenarnya mungkin lebih lambat dari np.min, saya pikir itu karena np.minmenggunakan multiprocessing untuk array besar:

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

Tetapi jika ada NaN dalam array, terutama jika posisinya berada pada indeks rendah, maka akan lebih cepat:

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

Hasil serupa dapat dicapai dengan Cython atau ekstensi C, ini sedikit lebih rumit (atau mudah tersedia bottleneck.anynan) tetapi pada umumnya melakukan hal yang sama seperti anynanfungsi saya .

MSeifert
sumber
1

Terkait dengan ini adalah pertanyaan bagaimana menemukan kemunculan pertama NaN. Ini adalah cara tercepat untuk menangani yang saya tahu:

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