2017-01-18 7 views
15

मैं एक समस्या है, जिसमें नाव nan एक numpy.array में एक list और nan में अलग तरह से नियंत्रित किया जाता है जब इन में उपयोग किया जाता डिबग करने के लिए एक कठिन समय हो रही है itertools.groupby:क्यों समूह NumPy सरणी में सूचियों में Nans itertools.groupby लेकिन नहीं कर सकते

निम्नलिखित सूची और सरणी को देखते हुए:

from itertools import groupby 
import numpy as np 

lst = [np.nan, np.nan, np.nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, np.nan, 0.16] 
arr = np.array(lst) 

जब मैं सूची सन्निहित nan रों वर्गीकृत किया है से अधिक पुनरावृति:

>>> for key, group in groupby(lst): 
...  if np.isnan(key): 
...   print(key, list(group), type(key)) 
nan [nan, nan, nan] <class 'float'> 
nan [nan] <class 'float'> 

लेकिन अगर मैं सरणी का उपयोग यह अलग-अलग समूहों में लगातार nan रों कहते हैं:

>>> for key, group in groupby(arr): 
...  if np.isnan(key): 
...   print(key, list(group), type(key)) 
nan [nan] <class 'numpy.float64'> 
nan [nan] <class 'numpy.float64'> 
nan [nan] <class 'numpy.float64'> 
nan [nan] <class 'numpy.float64'> 

यहां तक ​​कि अगर मैं एक सूची पर वापस सरणी परिवर्तित:

>>> for key, group in groupby(arr.tolist()): 
...  if np.isnan(key): 
...   print(key, list(group), type(key)) 
nan [nan] <class 'float'> 
nan [nan] <class 'float'> 
nan [nan] <class 'float'> 
nan [nan] <class 'float'> 

मैं उपयोग कर रहा हूँ:

numpy 1.11.3 
python 3.5 

मुझे पता है कि आम तौर पर nan != nan तो ये ऑपरेशन अलग क्यों होते हैं पंख परिणाम? और यह कैसे संभव है कि groupbynan एस समूह कर सकते हैं?

+0

हम्म, मुझे आश्चर्य है कि क्या इस सवाल के लिए कहा जाए। :) – Tagc

+0

यहां प्रश्न वास्तव में क्या है: क्यों 'groupby' ने इस असंगत व्यवहार को दिखाया, या इसके आसपास कैसे काम किया? और बाद के मामले में, क्या आप चाहते हैं कि नैन समूहबद्ध हों या नहीं? –

+0

बस अगर कोई और दिलचस्पी लेता है, तो सवाल उठाने के लिए कहा गया था कि क्यों [एक उत्तर] (http://stackoverflow.com/a/41722363/5393381) एक सूची में लागू होने पर काम करता था लेकिन जब लागू नहीं होता 'np.array'। – MSeifert

उत्तर

8

पायथन सूची मेमोरी में वस्तुओं के लिए पॉइंटर्स के सरणी हैं। विशेष रूप से lst वस्तु np.nan की ओर इशारा रखती है:

>>> [id(x) for x in lst] 
[139832272211880, # nan 
139832272211880, # nan 
139832272211880, # nan 
139832133974296, 
139832270325408, 
139832133974296, 
139832133974464, 
139832133974320, 
139832133974296, 
139832133974440, 
139832272211880, # nan 
139832133974296] 

(। np.nan 139832272211880 अपने कंप्यूटर पर पर है)

दूसरी ओर, NumPy सरणी स्मृति का सिर्फ सटे क्षेत्रों रहे हैं; वे बिट्स और बाइट्स के क्षेत्र हैं जिन्हें न्यूमपी द्वारा मूल्यों (फ्लोट्स, इन्ट्स इत्यादि) के अनुक्रम के रूप में व्याख्या किया जाता है।

मुसीबत जब आप एक NumPy सरणी (एक for -loop या groupby स्तर पर) चल मूल्यों धारण करने पर पुनरावृति करने के लिए अजगर से पूछते हैं, अजगर एक उचित अजगर वस्तु में इन बाइट्स बॉक्स करने की जरूरत है। यह सरणी में प्रत्येक एकल मान के लिए मेमोरी में एक ब्रांड नई पायथन ऑब्जेक्ट बनाता है क्योंकि यह पुनरावृत्त होता है।

उदाहरण के लिए, आप देख सकते हैं कि प्रत्येक nan मूल्य के लिए कि विशिष्ट वस्तुओं जब .tolist() कहा जाता है बनाए जाते हैं:

>>> [id(x) for x in arr.tolist()] 
[4355054616, # nan 
4355054640, # nan 
4355054664, # nan 
4355054688, 
4355054712, 
4355054736, 
4355054760, 
4355054784, 
4355054808, 
4355054832, 
4355054856, # nan 
4355054880] 

itertools.groupby क्योंकि यह पहचान के लिए जाँच करता अजगर सूची के लिए np.nan पर समूह करने में सक्षम है सबसे पहले जब यह पायथन वस्तुओं की तुलना करता है। चूंकि ये पॉइंटर्स nan सभी np.nan ऑब्जेक्ट पर इंगित करते हैं, तो समूहांकन संभव है।

हालांकि, न्यूमपी सरणी पर पुनरावृत्ति इस प्रारंभिक पहचान जांच को सफल होने की अनुमति नहीं देता है, इसलिए पाइथन समानता की जांच करने के लिए वापस आ जाता है और nan != nan जैसा कि आप कहते हैं।

5

मुझे यकीन है कि इस कारण से है कि क्या नहीं हूँ, लेकिन मैं सिर्फ lst में nan और arr के बारे में इस पर ध्यान:

>>> lst[0] == lst[1], arr[0] == arr[1] 
(False, False) 
>>> lst[0] is lst[1], arr[0] is arr[1] 
(True, False) 

Ie, जबकि सभी nan inequal हैं, नियमित रूप से np.nan (प्रकार float का) सभी ही उदाहरण है, जबकि arr में nan प्रकार numpy.float64 की अलग उदाहरण हैं) कर रहे हैं। तो मेरा अनुमान यह होगा कि यदि key फ़ंक्शन दिया गया है, तो groupby अधिक महंगा समानता जांच करने से पहले पहचान के लिए परीक्षण करेगा।

यह भी अवलोकन है कि, या तो नहीं arr.tolist() में समूह है, क्योंकि भले ही उन nan अब float फिर से कर रहे हैं, वे अब एक ही उदाहरण हैं के अनुरूप है।

>>> atl = arr.tolist() 
>>> atl[0] is atl[1] 
False 
+1

तो 'math.nan' एक सिंगलटन है, और 'np.nan' एक सिंगलटन है, लेकिन' np.array' तक पहुंचने से एक अस्थायी फ्लोट बनाता है * * एक सिंगलटन नहीं है? होश उड़ जाना। –

+1

@MarkRansom न केवल 'np.array .__ getitem__' जब आप इसे एक्सेस करना चाहते हैं तो नैन के लिए एक अलग ऑब्जेक्ट बनाता है। लेकिन हर बार जब आप मैन्युअल रूप से 'नान' बनाते हैं (उदाहरण के लिए 'फ्लोट (' नान ') ') एक नया _not-singleton_ नैन बनाया गया है। मजेदार तथ्य: 'एनपी.नान गणित नहीं है।' – MSeifert

6

tobias_k और ajcr का उत्तर सही हैं, क्योंकि यह सूची में nan रों ही id है, जबकि वे अलग अलग आईडी है, जब वे कर रहे हैं numpy सरणी में "पर दोहराया"।

यह उत्तर इन उत्तरों के लिए एक पूरक के रूप में है।

>>> from itertools import groupby 
>>> import numpy as np 

>>> lst = [np.nan, np.nan, np.nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, np.nan, 0.16] 
>>> arr = np.array(lst) 

>>> for key, group in groupby(lst): 
...  if np.isnan(key): 
...   print(key, id(key), [id(item) for item in group]) 
nan 1274500321192 [1274500321192, 1274500321192, 1274500321192] 
nan 1274500321192 [1274500321192] 

>>> for key, group in groupby(arr): 
...  if np.isnan(key): 
...   print(key, id(key), [id(item) for item in group]) 
nan 1274537130480 [1274537130480] 
nan 1274537130504 [1274537130504] 
nan 1274537130480 [1274537130480] 
nan 1274537130480 [1274537130480] # same id as before but these are not consecutive 

>>> for key, group in groupby(arr.tolist()): 
...  if np.isnan(key): 
...   print(key, id(key), [id(item) for item in group]) 
nan 1274537130336 [1274537130336] 
nan 1274537130408 [1274537130408] 
nan 1274500320904 [1274500320904] 
nan 1274537130168 [1274537130168] 

समस्या यह है कि अजगर जब वस्तु पहचान के लिए मान है, जो केवल परीक्षण की तुलना करता है, तो == विफल रहता है क्योंकि इसे लागू नहीं कर रहा है PyObject_RichCompare -operation का उपयोग करता है। itertools.groupby दूसरी ओर PyObject_RichCompareBool का उपयोग करता है (स्रोत देखें: 1, 2) जो वस्तु पहचान के लिए पहले परीक्षण करता है और == का परीक्षण है।

यह एक छोटा cython साथ सत्यापित किया जा सकता स्निपेट:

%load_ext cython 
%%cython 

from cpython.object cimport PyObject_RichCompareBool, PyObject_RichCompare, Py_EQ 

def compare(a, b): 
    return PyObject_RichCompare(a, b, Py_EQ), PyObject_RichCompareBool(a, b, Py_EQ) 

>>> compare(np.nan, np.nan) 
(False, True) 

PyObject_RichCompareBool के लिए स्रोत कोड इस तरह पढ़ता है:

/* Perform a rich comparison with object result. This wraps do_richcompare() 
    with a check for NULL arguments and a recursion check. */ 

/* Perform a rich comparison with integer result. This wraps 
    PyObject_RichCompare(), returning -1 for error, 0 for false, 1 for true. */ 
int 
PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) 
{ 
    PyObject *res; 
    int ok; 

    /* Quick result when objects are the same. 
     Guarantees that identity implies equality. */ 
    /**********************That's the difference!****************/ 
    if (v == w) { 
     if (op == Py_EQ) 
      return 1; 
     else if (op == Py_NE) 
      return 0; 
    } 

    res = PyObject_RichCompare(v, w, op); 
    if (res == NULL) 
     return -1; 
    if (PyBool_Check(res)) 
     ok = (res == Py_True); 
    else 
     ok = PyObject_IsTrue(res); 
    Py_DECREF(res); 
    return ok; 
} 

वस्तु पहचान परीक्षण (if (v == w)) वास्तव में सामान्य से पहले किया जाता है पायथन तुलना PyObject_RichCompare(v, w, op); का उपयोग its documentation:

में किया गया है और इसका उल्लेख किया गया है

नोट:

O1 और O2 एक ही वस्तु है, तो PyObject_RichCompareBool() हमेशा 1 Py_EQ के लिए लौट सकते हैं और Py_NE के लिए 0 जाएगा।

संबंधित मुद्दे