NumPy'de NaN için hızlı kontrol


120

np.nanBir NumPy dizisinde NaN ( ) oluşumunu kontrol etmenin en hızlı yolunu arıyorum X. Bu, potansiyel olarak devasa boyutta olan np.isnan(X)bir boole şekil dizisi oluşturduğu için X.shapesöz konusu bile olamaz.

Denedim np.nan in Xama bu işe yaramıyor çünkü np.nan != np.nan. Bunu yapmanın hızlı ve hafıza açısından verimli bir yolu var mı?

("Ne kadar büyük" diye soranlara: Bilemiyorum. Bu, kütüphane kodu için giriş doğrulamasıdır.)


bu senaryoda kullanıcı girdisini doğrulamak işe yaramıyor mu?
Eklemeden

@ Woot4Moo: hayır, kütüphane scipy.sparsegirdi olarak NumPy dizilerini veya matrisleri alır .
Fred Foo

2
Bunu çok yapıyorsanız, Darboğaz hakkında güzel şeyler duydum ( pypi.python.org/pypi/Bottleneck )
matt

Yanıtlar:


161

Ray'in çözümü iyidir. Ancak, benim makinede kullanmak için daha hızlı 2.5x hakkındadır numpy.sumyerine 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

Aksine min, summodern donanımda oldukça pahalı olma eğiliminde olan dallanma gerektirmez. Muhtemelen sumdaha hızlı olmasının nedeni budur .

edit Yukarıdaki test, dizinin tam ortasında tek bir NaN ile gerçekleştirildi.

minNaN'lerin varlığında bunların yokluğundan daha yavaş olduğunu not etmek ilginçtir . Ayrıca, NaN'ler dizinin başlangıcına yaklaştıkça daha da yavaşlıyor gibi görünüyor. Öte yandan, sumNaN'lerin olup olmadığına ve nerede bulunduklarına bakılmaksızın işlem hacmi sabit görünüyor:

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.mindizi NaN içermediğinde daha hızlıdır, bu benim beklenen girdimdir. Ama yine de bunu kabul etmeye karar verdim, çünkü yakalıyor infve neginfaynı zamanda.
Fred Foo

2
Bu yalnızca yakalar infveya -infgiriş hem içerir ve giriş araya eklenen taşma o büyük ama sonlu değerlerini içeriyorsa o sorunları varsa.
user2357112,

4
min ve max'ın sse özellikli x86 yongalarında kayan nokta verileri için dallanmasına gerek yoktur. Yani uyuşmuş 1.8 dakika itibariyle toplamdan daha yavaş olmayacak, amd fenomumda% 20 daha hızlı.
jtaylor

1
Intel Core i5'imde, OSX'te numpy 1.9.2 ile np.sumhala yaklaşık% 30 daha hızlı np.min.
Matthew Brett

np.isnan(x).any(0)biraz daha hızlıdır np.sumve np.minbazı istenmeyen önbelleğe alma olabilir, ancak benim makinede.
jsignell

28

Bence np.isnan(np.min(X))ne istersen yapmalısın.


Hmmm ... bu her zaman O (n) 'dir, ne zaman O (1) olabilir (bazı diziler için).
user48956

17

Kabul edilmiş bir cevap olsa bile, aşağıdakileri göstermek isterim (Vista'da Python 2.7.2 ve Numpy 1.6.0 ile):

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

Bu nedenle, gerçekten verimli olan yol, işletim sistemine büyük ölçüde bağlı olabilir. Her neyse dot(.), en istikrarlı olanı temel alıyor gibi görünüyor.


1
Bunun, temeldeki BLAS uygulaması ve C derleyicisinde olduğu gibi işletim sistemine bağlı olmadığından şüpheleniyorum. Teşekkürler, ancak bir iç çarpımın xbüyük değerler içerdiğinde taşma olasılığı biraz daha yüksektir ve ben de inf'yi kontrol etmek istiyorum.
Fred Foo

1
Her zaman iç çarpımı olanlarla yapabilir ve kullanabilirsiniz isfinite(.). Sadece büyük performans açığına dikkat çekmek istedim. Teşekkürler
yiyin

Benim makinemde de aynısı.
kawing-chiu 01

1
Akıllı, değil mi? As Fred Foo anlaşılacağı, nokta ürün bazlı yaklaşımın herhangi verimlilik kazanımları neredeyse kesin ATLAS, MKL veya OpenBLAS gibi optimize edilmiş BLAS uygulaması karşı bağlantılı yerel NumPy yüklemesi sayesinde vardır. Örneğin Anaconda için durum budur. Buna göre, bu iç çarpım mevcut tüm çekirdeklerde paralelleştirilecektir . Aynı şey , tek bir çekirdekle sınırlı çalışan min- ya da - sumtabanlı yaklaşımlar için söylenemez . Ergo, bu performans farkı.
Cecil Curry

16

Burada iki genel yaklaşım vardır:

  • Her dizi öğesini kontrol edin nanve alın any.
  • nanS (beğeniyi sum) koruyan bazı kümülatif işlemler uygulayın ve sonucunu kontrol edin.

İlk yaklaşım kesinlikle en temiz olanı olsa da, bazı kümülatif işlemlerin (özellikle BLAS'da yürütülenler gibi dot) ağır optimizasyonu bunları oldukça hızlı hale getirebilir. Not dotdiğer bazı BLAS operasyonları gibi, belirli koşullar altında multithreading'i. Bu, farklı makineler arasındaki hız farkını açıklar.

görüntü açıklamasını buraya girin

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 () kullanın

    if numpy.isnan(myarray).any()

  2. numpy.isfinite, kontrol etmek için isnan'dan daha iyi olabilir

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


3

Eğer rahatsan hızlı bir kısa devre oluşturmaya izin verir (bir NaN bulunur bulunmaz durur) işlevi:

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İşlev yoksa, aslında daha yavaş olabilir np.min, bunun nedeni np.minbüyük diziler için çoklu işlemeyi kullanmasıdır:

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

Ancak dizide bir NaN olması durumunda, özellikle konumu düşük endekslerde ise, o zaman çok daha hızlıdır:

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

Benzer sonuçlar Cython veya bir C uzantısı ile elde edilebilir, bunlar biraz daha karmaşıktır (veya olduğu gibi kolayca elde edilebilir bottleneck.anynan) ancak nihai olarak benim işlevimle aynı şeyi yapar anynan.


1

Bununla ilgili olarak, NaN'nin ilk oluşumunun nasıl bulunacağı sorusudur. Bildiğim şeyle başa çıkmanın en hızlı yolu bu

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.