NumPy में NaN के लिए फास्ट चेक


120

मैं np.nanएक NumPy सरणी में NaN ( ) की घटना की जांच करने का सबसे तेज़ तरीका ढूंढ रहा हूं Xnp.isnan(X)सवाल से बाहर है, क्योंकि यह आकार की एक बूलियन सरणी बनाता है X.shape, जो संभावित रूप से विशाल है।

मैंने कोशिश की np.nan in X, लेकिन ऐसा लगता है कि काम नहीं कर रहा है np.nan != np.nan। क्या ऐसा करने के लिए एक तेज और स्मृति-कुशल तरीका है?

(जो लोग पूछते हैं "कैसे विशाल": मैं नहीं बता सकता। यह पुस्तकालय कोड के लिए इनपुट सत्यापन है।)


इस परिदृश्य में उपयोगकर्ता इनपुट को मान्य नहीं करता है? डालने से पहले NaN के लिए जाँच के रूप में
Woot4Moo

@ Woot4Moo: नहीं, लाइब्रेरी scipy.sparseइनपुट के रूप में NumPy एरेज़ या मैट्रिसेस लेता है ।
फ्रेड फू

2
यदि आप यह बहुत कुछ कर रहे हैं, तो मैंने टोंटीलेक के बारे में अच्छी बातें सुनी हैं ( pypi.python.org/pypi/Bottleneck )
मैट

जवाबों:


160

रे का हल अच्छा है। हालांकि, मेरी मशीन पर इसके numpy.sumस्थान पर उपयोग करने के लिए लगभग 2.5x तेज़ है 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

इसके विपरीत min, sumब्रांचिंग की आवश्यकता नहीं है, जो आधुनिक हार्डवेयर पर बहुत महंगा है। शायद यही कारण है कि sumतेज है।

संपादन उपरोक्त परीक्षण सरणी के बीच में एक एकल NaN अधिकार के साथ किया गया था।

यह ध्यान रखना दिलचस्प है कि minउनकी अनुपस्थिति की तुलना में NaN की उपस्थिति में धीमी है। यह भी लगता है कि NaNs सरणी की शुरुआत के करीब आने के साथ धीमी होती जा रही है। दूसरी ओर, 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 नहीं होता है, जो कि मेरा अपेक्षित इनपुट है, तो तेजी से होता है। लेकिन मैं, क्योंकि यह फैल जाती है वैसे भी यह एक स्वीकार करने के लिए तय कर लिया है infऔर neginfसाथ ही।
फ्रेड फू

2
यह केवल पकड़ता है infया -infयदि इनपुट में दोनों शामिल हैं, और इसमें समस्याएं हैं यदि इनपुट में बड़े लेकिन परिमित मूल्य हैं जो एक साथ जोड़े जाने पर अतिप्रवाह करते हैं।
user2357112

4
न्यूनतम सक्षम x86 चिप पर चल बिंदु डेटा के लिए न्यूनतम करने के लिए न्यूनतम और अधिकतम की आवश्यकता नहीं है। तो जैसा कि सुन्न 1.8 मिनट के योग की तुलना में धीमी नहीं होगी, मेरे एमड फिनोम पर इसकी 20% तेजी से भी।
jayaylor

1
OSX पर 1.9.2 के साथ मेरे इंटेल कोर i5 पर, की तुलना np.sumमें अभी भी लगभग 30% तेज है np.min
मैथ्यू ब्रेट

np.isnan(x).any(0)की तुलना में थोड़ा तेज है np.sumऔर np.min, मेरे मशीन पर यद्यपि वहाँ कुछ अवांछित कैशिंग हो सकता है।
jsignell

28

मुझे लगता np.isnan(np.min(X))है कि आपको वही करना चाहिए जो आप चाहते हैं।


हम्म ... यह हमेशा ओ (एन) है जब ओ (1) (कुछ सरणियों के लिए) हो सकता है।
user48956

17

यहां तक ​​कि एक स्वीकृत उत्तर मौजूद है, मैं निम्नलिखित प्रदर्शित करना चाहता हूं (विथ पर पायथन 2.7.2 और नेम्पी 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
मुझे संदेह है कि यह ओएस पर इतना निर्भर नहीं करता है, जितना कि अंतर्निहित बीएलएएस कार्यान्वयन और सी संकलक पर। धन्यवाद, लेकिन एक डॉट उत्पाद xबड़े मूल्यों वाले होने पर अतिप्रवाह की संभावना है , और मैं भी जांच करना चाहता हूं।
फ्रेड फू

1
ठीक है, आप हमेशा डॉट प्रोडक्ट लोगों के साथ कर सकते हैं और उपयोग कर सकते हैं isfinite(.)। मैं सिर्फ विशाल प्रदर्शन अंतर को इंगित करना चाहता था। धन्यवाद
खाएं

मेरी मशीन पर भी ऐसा ही है।
१-१४ बजे kawing-chiu

1
चतुर, नहीं? जैसा कि फ्रेड फू का सुझाव है, डॉट उत्पाद-आधारित दृष्टिकोण का कोई भी दक्षता लाभ लगभग निश्चित रूप से एक स्थानीय बीईएलपीवाई स्थापना के लिए धन्यवाद है जो एटीएलएएस, एमकेएल, या ओपनब्लास जैसे अनुकूलित बीएलएएस कार्यान्वयन के खिलाफ जुड़ा हुआ है। उदाहरण के लिए यह एनाकोंडा का मामला है। यह देखते हुए, यह डॉट उत्पाद सभी उपलब्ध कोरों के समानांतर होगा । उसी के लिए नहीं कहा जा सकता है min- या- sumआधारित दृष्टिकोण, जो एक ही कोर तक सीमित हैं। एर्गो, वह प्रदर्शन अंतराल।
सेसिल करी

16

यहाँ दो सामान्य दृष्टिकोण हैं:

  • के लिए प्रत्येक सरणी आइटम की जाँच करें nanऔर ले any
  • कुछ संचयी ऑपरेशन लागू करें जो nans (जैसे sum) को संरक्षित करता है और उसका परिणाम देखता है।

जबकि पहला दृष्टिकोण निश्चित रूप से सबसे साफ है, कुछ संचयी परिचालनों का भारी अनुकूलन (विशेष रूप से बीएलएएस में निष्पादित होने वाले, जैसे 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. जाँच के लिए isnan से बेहतर numpy.isfinite शायद बेहतर हो

    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

इसी तरह के परिणाम साइथन या सी एक्सटेंशन के साथ प्राप्त किए जा सकते हैं, ये थोड़ा अधिक जटिल हैं (या आसानी से avaiable के रूप में bottleneck.anynan) लेकिन अल्टीमेटली मेरे anynanफ़ंक्शन के समान ही करें ।


1

इससे संबंधित प्रश्न यह है कि NaN की पहली घटना का पता कैसे लगाया जाए। यह सबसे तेज़ तरीका है जिससे मैं जान सकता हूँ:

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.