NumPy 1.14 - Iterating Over Arrays

ओवररेट्स पर Iterating




numpy

ओवररेट्स पर Iterating

NumPy 1.6 में पेश किया गया nditer ऑब्जेक्ट nditer , एक व्यवस्थित तरीके से एक या एक से अधिक सरणियों के सभी तत्वों को देखने के लिए कई लचीले तरीके प्रदान करता है। यह पृष्ठ पायथन में सरणियों पर अभिकलन के लिए ऑब्जेक्ट का उपयोग करने के लिए कुछ बुनियादी तरीकों का परिचय देता है, फिर निष्कर्ष निकालता है कि साइथन में आंतरिक लूप को कैसे तेज किया जा सकता है। चूँकि nditer का पाइथन एक्सपोज़र C सरणी इटरेटर API की अपेक्षाकृत सीधी मैपिंग है, इसलिए ये आइडिया C या C ++ से ऐरे nditer साथ काम करने में सहायता प्रदान करेंगे।

एकल ऐरे इटरेशन

सबसे बुनियादी कार्य जो nditer साथ किया जा सकता है, nditer है सरणी के प्रत्येक तत्व का दौरा करना। प्रत्येक तत्व मानक पायथन इटरेटर इंटरफ़ेस का उपयोग करके एक-एक करके प्रदान किया जाता है।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a):
...     print x,
...
0 1 2 3 4 5

इस पुनरावृत्ति के लिए एक महत्वपूर्ण बात यह है कि ऑर्डर को मानक C या फोरट्रान ऑर्डर का उपयोग करने के बजाय सरणी के मेमोरी लेआउट से मिलान करने के लिए चुना जाता है। यह एक्सेस दक्षता के लिए किया जाता है, इस विचार को दर्शाता है कि डिफ़ॉल्ट रूप से एक विशेष ऑर्डरिंग के लिए चिंता किए बिना प्रत्येक तत्व का दौरा करना चाहता है। C क्रम में उस स्थानान्तरण की एक प्रति लेने की तुलना में, हम अपने पिछले सरणी के पारगमन पर पुनरावृत्ति करके इसे देख सकते हैं।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a.T):
...     print x,
...
0 1 2 3 4 5
>>> for x in np.nditer(a.T.copy(order='C')):
...     print x,
...
0 3 1 4 2 5

aT और aT दोनों के तत्व a ही क्रम में ट्रेस हो जाते हैं, अर्थात् वे स्मृति में संग्रहीत किए जाते हैं, जबकि aTcopy(order='C') के तत्व एक अलग क्रम में मिलते हैं क्योंकि उन्हें एक अलग मेमोरी में डाल दिया गया है लेआउट।

Iteration आदेश को नियंत्रित करना

ऐसे समय होते हैं जब किसी विशिष्ट क्रम में किसी सरणी के तत्वों का दौरा करना महत्वपूर्ण होता है, भले ही स्मृति में तत्वों के लेआउट के बावजूद। nditer ऑब्जेक्ट पुनरावृत्ति के इस पहलू को नियंत्रित करने के लिए एक order पैरामीटर प्रदान करता है। डिफ़ॉल्ट, जो ऊपर वर्णित व्यवहार है, मौजूदा आदेश रखने के लिए आदेश = 'के' है। यह आदेश के साथ ओवरराइड किया जा सकता है = सी ऑर्डर और ऑर्डर के लिए 'सी' = फोरट्रान ऑर्डर के लिए 'एफ'।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, order='F'):
...     print x,
...
0 3 1 4 2 5
>>> for x in np.nditer(a.T, order='C'):
...     print x,
...
0 3 1 4 2 5

सरणी मानों को संशोधित करना

डिफ़ॉल्ट रूप से, nditer इनपुट सरणी को केवल-पढ़ने योग्य ऑब्जेक्ट के रूप में मानता है। सरणी तत्वों को संशोधित करने के लिए, आपको रीड-राइट या राइट-ओनली मोड निर्दिष्ट करना होगा। इसे प्रति-ऑपरेंड झंडे के साथ नियंत्रित किया जाता है।

पायथन में नियमित रूप से असाइनमेंट केवल स्थान में मौजूदा चर को संशोधित करने के बजाय स्थानीय या वैश्विक चर शब्दकोश में एक संदर्भ बदलता है। इसका मतलब यह है कि केवल x को असाइन करने से एरे के तत्व में वैल्यू नहीं आयेगी, बल्कि ए x एलिमेंट रेफरेंस से x को स्विच करने से आपके द्वारा असाइन किए गए वैल्यू का संदर्भ होगा। वास्तव में एरे के तत्व को संशोधित करने के लिए, x को एलिप्सिस के साथ अनुक्रमित किया जाना चाहिए।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> a
array([[0, 1, 2],
       [3, 4, 5]])
>>> for x in np.nditer(a, op_flags=['readwrite']):
...     x[...] = 2 * x
...
>>> a
array([[ 0,  2,  4],
       [ 6,  8, 10]])

बाहरी लूप का उपयोग करना

अब तक के सभी उदाहरणों में, एक के तत्वों को एक समय में पुनरावृत्तिकर्ता द्वारा प्रदान किया जाता है, क्योंकि सभी लूपिंग तर्क इट्रेटर के लिए आंतरिक है। हालांकि यह सरल और सुविधाजनक है, यह बहुत कुशल नहीं है। एक बेहतर तरीका यह है कि एक-आयामी अंतरतम लूप को अपने कोड में स्थानांतरित किया जाए, जो पुनरावृत्ति करने वाले के लिए बाहरी है। इस तरह, नुम्पी के सदिश संचालनों का उपयोग किए जाने वाले तत्वों के बड़े हिस्से पर किया जा सकता है।

nditer आंतरिक लूप के लिए संभव के रूप में बड़े हैं कि विखंडू प्रदान करने की कोशिश करेंगे। 'C' और 'F' ऑर्डर को मजबूर करके, हमें विभिन्न बाहरी लूप आकार मिलते हैं। यह मोड इटरेटर ध्वज निर्दिष्ट करके सक्षम है।

ध्यान रखें कि देशी मेमोरी ऑर्डर को रखने के डिफ़ॉल्ट के साथ, इटरेटर एक सिंगल-आयामी चंक प्रदान करने में सक्षम है, जबकि फोरट्रान ऑर्डर को मजबूर करते समय, इसे दो तत्वों में से प्रत्येक के तीन भाग प्रदान करना होगा।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop']):
...     print x,
...
[0 1 2 3 4 5]
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print x,
...
[0 3] [1 4] [2 5]

एक सूचकांक या बहु-सूचकांक पर नज़र रखना

पुनरावृत्ति के दौरान, आप गणना में वर्तमान तत्व के सूचकांक का उपयोग करना चाह सकते हैं। उदाहरण के लिए, आप किसी सरणी के तत्वों को स्मृति क्रम में देखना चाहते हैं, लेकिन किसी भिन्न सरणी में मान देखने के लिए C-आदेश, फ़ोर्ट्रान-ऑर्डर या बहुआयामी सूचकांक का उपयोग कर सकते हैं।

पायथन इटरेटर प्रोटोकॉल में इटरेटर से इन अतिरिक्त मूल्यों को क्वेरी करने का एक प्राकृतिक तरीका नहीं है, इसलिए हम nditer साथ पुनरावृत्ति के लिए एक वैकल्पिक वाक्यविन्यास nditer । यह सिंटैक्स स्पष्ट रूप से पुनरावृत्ति ऑब्जेक्ट के साथ काम करता है, इसलिए इसके गुण पुनरावृत्ति के दौरान आसानी से सुलभ हैं। इस लूपिंग निर्माण के साथ, वर्तमान मान multi_index में अनुक्रमित करके पहुंच योग्य है, और ट्रैक किए जा रहे index संपत्ति index या multi_index है जो अनुरोध किया गया था multi_index आधार पर।

पायथन इंटरएक्टिव इंटरप्रेटर दुर्भाग्य से लूप के प्रत्येक पुनरावृत्ति के दौरान लूप के अंदर अभिव्यक्तियों के मूल्यों को प्रिंट करता है। हमने इस लूपिंग निर्माण का उपयोग करके उदाहरणों में आउटपुट को संशोधित किया है ताकि अधिक पठनीय हो।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> it = np.nditer(a, flags=['f_index'])
>>> while not it.finished:
...     print "%d <%d>" % (it[0], it.index),
...     it.iternext()
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5>
>>> it = np.nditer(a, flags=['multi_index'])
>>> while not it.finished:
...     print "%d <%s>" % (it[0], it.multi_index),
...     it.iternext()
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)>
>>> it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly'])
>>> while not it.finished:
...     it[0] = it.multi_index[1] - it.multi_index[0]
...     it.iternext()
...
>>> a
array([[ 0,  1,  2],
       [-1,  0,  1]])

इंडेक्स या मल्टी-इंडेक्स को ट्रैक करना बाहरी लूप का उपयोग करने के साथ असंगत है, क्योंकि इसके लिए प्रति तत्व एक अलग इंडेक्स वैल्यू की आवश्यकता होती है। यदि आप इन झंडों को संयोजित करने का प्रयास करते हैं, तो nditer ऑब्जेक्ट एक अपवाद बढ़ाएगा

उदाहरण

>>> a = np.zeros((2,3))
>>> it = np.nditer(a, flags=['c_index', 'external_loop'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Iterator flag EXTERNAL_LOOP cannot be used if an index or multi-index is being tracked

ऐरे तत्वों को बफर करना

जब एक पुनरावृत्ति क्रम को मजबूर करते हैं, तो हमने देखा कि बाहरी लूप विकल्प तत्वों को छोटी मात्रा में प्रदान कर सकता है क्योंकि तत्वों को निरंतर क्रम से उचित क्रम में नहीं देखा जा सकता है। सी कोड लिखते समय, यह आम तौर पर ठीक है, हालांकि शुद्ध पायथन कोड में यह प्रदर्शन में महत्वपूर्ण कमी का कारण बन सकता है।

बफरिंग मोड को सक्षम करके, इट्रेटर द्वारा आंतरिक लूप को प्रदान किए गए विखंडन को पायथन इंटरप्रेटर के ओवरहेड को कम करते हुए, बड़ा बनाया जा सकता है। फोरट्रान पुनरावृत्ति क्रम को मजबूर करने वाले उदाहरण में, आंतरिक लूप सभी तत्वों को एक बार में देखने को मिलता है जब बफरिंग सक्षम होती है।

उदाहरण

>>> a = np.arange(6).reshape(2,3)
>>> for x in np.nditer(a, flags=['external_loop'], order='F'):
...     print x,
...
[0 3] [1 4] [2 5]
>>> for x in np.nditer(a, flags=['external_loop','buffered'], order='F'):
...     print x,
...
[0 3 1 4 2 5]

विशिष्ट डेटा प्रकार के रूप में Iterating

ऐसे समय होते हैं जब किसी सरणी को भिन्न डेटा प्रकार के रूप में संग्रहित किया जाना आवश्यक होता है। उदाहरण के लिए, कोई 64-बिट फ़्लोट पर सभी संगणनाएँ करना चाहेगा, भले ही जोड़तोड़ किया जा रहा है 32-बिट फ़्लोट्स। निम्न-स्तरीय सी कोड लिखते समय, आमतौर पर यह बेहतर होता है कि इट्रेटर को कॉपी करने या बफ़र करने की बजाय डेटा टाइप को आंतरिक लूप में रखने की अनुमति दें।

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

बफ़रिंग मोड मेमोरी उपयोग समस्या को कम करता है और अस्थायी प्रतियां बनाने की तुलना में अधिक कैश-फ्रेंडली है। विशेष मामलों को छोड़कर, जहां पुनरावृत्त के बाहर पूरे सरणी की आवश्यकता होती है, अस्थायी नकल पर बफरिंग की सिफारिश की जाती है। NumPy के भीतर, न्यूनतम मेमोरी ओवरहेड के साथ लचीले इनपुट का समर्थन करने के लिए ufuncs और अन्य कार्यों द्वारा बफरिंग का उपयोग किया जाता है।

हमारे उदाहरण में, हम एक जटिल डेटा प्रकार के साथ इनपुट एरे का इलाज करेंगे, ताकि हम नकारात्मक संख्याओं के वर्गमूल ले सकें। कॉपियों या बफरिंग मोड को सक्षम किए बिना, डेटा टाइप ठीक से मेल नहीं खाता है, तो इट्रेटर एक अपवाद बढ़ाएगा।

उदाहरण

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_dtypes=['complex128']):
...     print np.sqrt(x),
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand required copying or buffering, but neither copying nor buffering was enabled

प्रतिलिपि मोड में, 'प्रति' एक प्रति-ध्वज के रूप में निर्दिष्ट किया गया है। यह एक प्रति-संचालन फैशन में नियंत्रण प्रदान करने के लिए किया जाता है। बफ़रिंग मोड को इटरेटर ध्वज के रूप में निर्दिष्ट किया गया है।

उदाहरण

>>> a = np.arange(6).reshape(2,3) - 3
>>> for x in np.nditer(a, op_flags=['readonly','copy'],
...                 op_dtypes=['complex128']):
...     print np.sqrt(x),
...
1.73205080757j 1.41421356237j 1j 0j (1+0j) (1.41421356237+0j)
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['complex128']):
...     print np.sqrt(x),
...
1.73205080757j 1.41421356237j 1j 0j (1+0j) (1.41421356237+0j)

यह निर्धारित करने के लिए कि क्या कोई विशिष्ट रूपांतरण की अनुमति है, इटमर नोमपी के कास्टिंग नियमों का उपयोग करता है। डिफ़ॉल्ट रूप से, यह 'सुरक्षित' कास्टिंग को लागू करता है। इसका अर्थ है, उदाहरण के लिए, यदि आप 64-बिट फ्लोट सरणी को 32-बिट फ्लोट सरणी के रूप में मानने का प्रयास करते हैं, तो यह एक अपवाद को बढ़ाएगा। कई मामलों में, नियम 'same_kind' का उपयोग करने के लिए सबसे उचित नियम है, क्योंकि यह 64 से 32-बिट फ्लोट में रूपांतरण की अनुमति देगा, लेकिन फ्लोट से इंट में या जटिल से फ्लोट तक नहीं।

उदाहरण

>>> a = np.arange(6.)
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32']):
...     print x,
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('float32') according to the rule 'safe'
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32'],
...                 casting='same_kind'):
...     print x,
...
0.0 1.0 2.0 3.0 4.0 5.0
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['int32'], casting='same_kind'):
...     print x,
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Iterator operand 0 dtype could not be cast from dtype('float64') to dtype('int32') according to the rule 'same_kind'

पढ़ने-लिखने या लिखने के लिए केवल ऑपरेंड का उपयोग करते समय मूल डेटा प्रकार पर रूपांतरण देखने के लिए एक बात है। एक सामान्य मामला 64-बिट फ़्लोट के संदर्भ में आंतरिक लूप को लागू करना है, और अन्य फ़्लोटिंग-पॉइंट प्रकारों को भी संसाधित करने की अनुमति देने के लिए 'समान_हिन्डी' कास्टिंग का उपयोग करना है। रीड-ओनली मोड में रहते हुए, एक पूर्णांक सरणी प्रदान की जा सकती है, रीड-राइट मोड एक अपवाद को बढ़ाएगा क्योंकि सरणी में वापस रूपांतरण कास्टिंग नियम का उल्लंघन करेगा।

उदाहरण

>>> a = np.arange(6)
>>> for x in np.nditer(a, flags=['buffered'], op_flags=['readwrite'],
...                 op_dtypes=['float64'], casting='same_kind'):
...     x[...] = x / 2.0
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: Iterator requested dtype could not be cast from dtype('float64') to dtype('int64'), the operand 0 dtype, according to the rule 'same_kind'

प्रसारण ऐरे इटरेशन

NumPy में भिन्न भिन्न आकार वाले सरणियों से निपटने के लिए नियमों का एक समूह होता है, जिन्हें जब भी लागू किया जाता है, तो कई ऑपरेंड लेते हैं जो तत्व-वार को जोड़ते हैं। इसे broadcasting कहा जाता broadcasting nditer ऑब्जेक्ट आपके लिए इन नियमों को लागू कर सकता है जब आपको इस तरह के फ़ंक्शन को लिखने की आवश्यकता होती है।

एक उदाहरण के रूप में, हम एक एक और दो आयामी सरणी को एक साथ प्रसारित करने के परिणाम का प्रिंट आउट लेते हैं।

उदाहरण

>>> a = np.arange(3)
>>> b = np.arange(6).reshape(2,3)
>>> for x, y in np.nditer([a,b]):
...     print "%d:%d" % (x,y),
...
0:0 1:1 2:2 0:3 1:4 2:5

जब एक प्रसारण त्रुटि होती है, तो इट्रेटर एक अपवाद उठाता है जिसमें समस्या का निदान करने में मदद करने के लिए इनपुट आकृतियों को शामिल किया जाता है।

उदाहरण

>>> a = np.arange(2)
>>> b = np.arange(6).reshape(2,3)
>>> for x, y in np.nditer([a,b]):
...     print "%d:%d" % (x,y),
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (2) (2,3)

Iterator-Allocated Output Arrays

NumPy फ़ंक्शन में एक सामान्य मामला इनपुट के प्रसारण के आधार पर आबंटित आउटपुट है, और इसके अतिरिक्त एक वैकल्पिक पैरामीटर है जिसे 'आउट' कहा जाता है जहां परिणाम प्रदान किए जाने पर रखा जाएगा। nditer ऑब्जेक्ट एक सुविधाजनक मुहावरा प्रदान करता है जो इस तंत्र का समर्थन करना बहुत आसान बनाता है।

हम यह दिखाते हैं कि यह एक फ़ंक्शन square बनाकर कैसे काम करता है जो इसके इनपुट को स्क्वायर करता है। चलो 'आउट' पैरामीटर समर्थन को छोड़कर न्यूनतम फ़ंक्शन परिभाषा के साथ शुरू करते हैं।

उदाहरण

>>> def square(a):
...     it = np.nditer([a, None])
...     for x, y in it:
...          y[...] = x*x
...     return it.operands[1]
...
>>> square([1,2,3])
array([1, 4, 9])

डिफ़ॉल्ट रूप से, nditer उन nditer लिए 'आवंटित' और 'राइटली' nditer का उपयोग करता है जो किसी के रूप में पारित नहीं होते हैं। इसका मतलब है कि हम सिर्फ दो ऑपरेटर को पुनरावृत्त प्रदान करने में सक्षम थे, और इसने बाकी को संभाला।

'आउट' पैरामीटर को जोड़ते समय, हमें स्पष्ट रूप से उन झंडों को प्रदान करना होगा, क्योंकि यदि कोई किसी सरणी में 'आउट' के रूप में गुजरता है, तो इट्रेटर डिफ़ॉल्ट रूप से 'आसानी से' पढ़ जाएगा, और हमारा आंतरिक लूप विफल हो जाएगा। इनपुट एरियर्स के लिए 'रीडोनॉली' डिफॉल्ट डिफॉल्ट ऑपरेशन को रोकने के बारे में भ्रम को रोकने के लिए डिफ़ॉल्ट है। यदि डिफ़ॉल्ट 'रीडराइट' था, तो किसी भी प्रसारण ऑपरेशन में कमी भी आएगी, एक विषय जो इस दस्तावेज़ में बाद में शामिल किया गया है।

जब हम इस पर होते हैं, तो हम 'no_broadcast' ध्वज भी पेश करते हैं, जो आउटपुट को प्रसारित होने से रोकेगा। यह महत्वपूर्ण है, क्योंकि हम केवल प्रत्येक आउटपुट के लिए एक इनपुट मूल्य चाहते हैं। एक से अधिक इनपुट मूल्य एकत्र करना एक कमी ऑपरेशन है जिसके लिए विशेष हैंडलिंग की आवश्यकता होती है। यह पहले से ही एक त्रुटि बढ़ाएगा क्योंकि कटौती को इट्रेटर ध्वज में स्पष्ट रूप से सक्षम किया जाना चाहिए, लेकिन प्रसारण को अक्षम करने के परिणामस्वरूप होने वाला त्रुटि संदेश अंत उपयोगकर्ताओं के लिए बहुत अधिक समझने योग्य है। यह देखने के लिए कि स्क्वायर फ़ंक्शन को कैसे कम किया जाए, साइथन के बारे में अनुभाग में वर्गों के योग को देखें।

पूर्णता के लिए, हम 'external_loop' और 'buffered' झंडे भी जोड़ेंगे, क्योंकि ये वही हैं जो आप आमतौर पर प्रदर्शन कारणों से चाहते हैं।

उदाहरण

>>> def square(a, out=None):
...     it = np.nditer([a, out],
...             flags = ['external_loop', 'buffered'],
...             op_flags = [['readonly'],
...                         ['writeonly', 'allocate', 'no_broadcast']])
...     for x, y in it:
...         y[...] = x*x
...     return it.operands[1]
...
>>> square([1,2,3])
array([1, 4, 9])
>>> b = np.zeros((3,))
>>> square([1,2,3], out=b)
array([ 1.,  4.,  9.])
>>> b
array([ 1.,  4.,  9.])
>>> square(np.arange(6).reshape(2,3), out=b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in square
ValueError: non-broadcastable output operand with shape (3) doesn't match the broadcast shape (2,3)

बाहरी उत्पाद Iteration

किसी भी बाइनरी ऑपरेशन को बाहरी की तरह बाहरी उत्पाद फैशन में एक सरणी ऑपरेशन तक बढ़ाया जा सकता है, और nditer ऑब्जेक्ट ऑपरेंड के अक्षों को स्पष्ट रूप से मैप करके इसे पूरा करने का एक तरीका प्रदान करता है। यह भी संभव है कि यह newaxis indexing के साथ किया newaxis , लेकिन हम आपको दिखाएंगे कि बिना किसी मध्यवर्ती विचार के इसे पूरा करने के लिए सीधे nditer op_axes पैरामीटर का उपयोग कैसे करें।

हम दूसरे ऑपरेटर के आयामों से पहले पहले ऑपरेंड के आयामों को रखते हुए एक साधारण बाहरी उत्पाद करेंगे। op_axes पैरामीटर को प्रत्येक ऑपरेंड के लिए कुल्हाड़ियों की एक सूची की आवश्यकता होती है, और op_axes के कुल्हाड़ियों से op_axes की कुल्हाड़ियों से एक मैपिंग प्रदान करता है।

मान लीजिए पहला ऑपरेंड एक आयामी है और दूसरा ऑपरेंड दो आयामी है। op_axes तीन आयाम होंगे, इसलिए op_axes की दो 3-तत्व सूचियाँ होंगी। पहली सूची पहले ऑपरेंड की एक धुरी को बाहर निकालती है, और बाकी आइटर की कुल्हाड़ियों के लिए -1 है, [0, -1, -1] के अंतिम परिणाम के साथ। दूसरी सूची दूसरे ऑपरेंड की दो कुल्हाड़ियों को बाहर निकालती है, लेकिन पहले ऑपरेंड में निकाली गई कुल्हाड़ियों के साथ ओवरलैप नहीं होना चाहिए। इसकी सूची [-1, 0, 1] है। आउटपुट ऑपरेटर्स इट्रेटर कुल्हाड़ियों पर मानक तरीके से मैप करता है, इसलिए हम दूसरी सूची बनाने के बजाय कोई भी प्रदान कर सकते हैं।

आंतरिक लूप में ऑपरेशन एक सीधा गुणा है। बाहरी उत्पाद के साथ सब कुछ करने के लिए iterator सेटअप द्वारा नियंत्रित किया जाता है।

उदाहरण

>>> a = np.arange(3)
>>> b = np.arange(8).reshape(2,4)
>>> it = np.nditer([a, b, None], flags=['external_loop'],
...             op_axes=[[0, -1, -1], [-1, 0, 1], None])
>>> for x, y, z in it:
...     z[...] = x*y
...
>>> it.operands[2]
array([[[ 0,  0,  0,  0],
        [ 0,  0,  0,  0]],
       [[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],
       [[ 0,  2,  4,  6],
        [ 8, 10, 12, 14]]])

रिडक्शन इटरेशन

जब भी एक लेखन योग्य ऑपरेंड में पूर्ण पुनरावृत्ति स्थान की तुलना में कम तत्व होते हैं, तो उस ऑपरेंड में कमी आ रही है। nditer ऑब्जेक्ट के लिए आवश्यक है कि किसी भी कमी ऑपरेंड को पढ़ने-लिखने के रूप में चिह्नित किया जाए, और केवल तभी कटौती को अनुमति देता है जब 'कम करें' को 'इट्रेटर फ्लैग' के रूप में प्रदान किया जाता है।

एक साधारण उदाहरण के लिए, सभी तत्वों के योग को एक सरणी में लेने पर विचार करें।

उदाहरण

>>> a = np.arange(24).reshape(2,3,4)
>>> b = np.array(0)
>>> for x, y in np.nditer([a, b], flags=['reduce_ok', 'external_loop'],
...                     op_flags=[['readonly'], ['readwrite']]):
...     y[...] += x
...
>>> b
array(276)
>>> np.sum(a)
276

कटौती और आवंटित ऑपरेंड को मिलाकर चीजें थोड़ी अधिक मुश्किल हैं। पुनरावृत्ति शुरू होने से पहले, किसी भी कमी ऑपरेंड को अपने शुरुआती मूल्यों के लिए आरंभीकृत किया जाना चाहिए। यहां बताया गया है कि हम ऐसा कैसे कर सकते हैं, अंतिम अक्ष के साथ रकम ले रहे हैं।

उदाहरण

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> it.operands[1][...] = 0
>>> for x, y in it:
...     y[...] += x
...
>>> it.operands[1]
array([[ 6, 22, 38],
       [54, 70, 86]])
>>> np.sum(a, axis=2)
array([[ 6, 22, 38],
       [54, 70, 86]])

कमी करने के लिए सेटअप के दौरान एक और समायोजन की आवश्यकता है। आम तौर पर पुनरावृत्ति निर्माण में पठनीय सरणियों से डेटा के पहले बफर को बफर में कॉपी करना शामिल होता है। कोई कमी ऑपरेंड पठनीय है, इसलिए इसे बफर में पढ़ा जा सकता है। दुर्भाग्य से, इस बफरिंग ऑपरेशन के पूरा होने के बाद ऑपरेंड का इनिशियलाइज़ेशन उस बफर में परिलक्षित नहीं होगा जो पुनरावृत्ति के साथ शुरू होता है, और कचरा परिणाम उत्पन्न होगा।

इटरेटर फ्लैग “delay_bufalloc” है बेटरिंग के साथ मिलकर इटरेटर-आवंटित कमी ऑपरेंड को अनुमति देने के लिए। जब यह ध्वज सेट किया जाता है, तो इट्रेटर अपने बफ़र्स को बिना रीसेट के तब तक निर्विवाद रूप से छोड़ देगा, जिसके बाद वह नियमित पुनरावृत्ति के लिए तैयार हो जाएगा। यहां बताया गया है कि पिछला उदाहरण कैसा दिखता है यदि हम बफरिंग को सक्षम करते हैं।

उदाहरण

>>> a = np.arange(24).reshape(2,3,4)
>>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop',
...                                  'buffered', 'delay_bufalloc'],
...             op_flags=[['readonly'], ['readwrite', 'allocate']],
...             op_axes=[None, [0,1,-1]])
>>> it.operands[1][...] = 0
>>> it.reset()
>>> for x, y in it:
...     y[...] += x
...
>>> it.operands[1]
array([[ 6, 22, 38],
       [54, 70, 86]])

साइथन में इनर लूप लगाना

जो लोग अपने निचले स्तर के संचालन से वास्तव में अच्छा प्रदर्शन चाहते हैं, उन्हें C में प्रदान की गई iteration API का उपयोग करते हुए सीधे विचार करना चाहिए, लेकिन जो लोग C या C ++ के साथ सहज नहीं हैं, उनके लिए Cython उचित प्रदर्शन वाले ट्रेडऑफ़ के साथ एक अच्छा मध्य मैदान है। nditer ऑब्जेक्ट के लिए, इसका मतलब यह है कि पुनरावृत्ति करने वाले को इनर लूप को प्रसारित करते समय प्रसारण, dtype रूपांतरण और बफरिंग का ध्यान रखना चाहिए।

हमारे उदाहरण के लिए, हम एक वर्ग का योग बनाएँगे। शुरू करने के लिए, चलो सीधे पायथन में इस फ़ंक्शन को लागू करते हैं। हम एक 'अक्ष' पैरामीटर का समर्थन करना चाहते हैं, जो संख्यात्मक sum फ़ंक्शन के समान है, इसलिए हमें op_axes पैरामीटर के लिए एक सूची बनाने की आवश्यकता होगी। यहाँ है कि यह कैसे दिखता है।

उदाहरण

>>> def axis_to_axeslist(axis, ndim):
...     if axis is None:
...         return [-1] * ndim
...     else:
...         if type(axis) is not tuple:
...             axis = (axis,)
...         axeslist = [1] * ndim
...         for i in axis:
...             axeslist[i] = -1
...         ax = 0
...         for i in range(ndim):
...             if axeslist[i] != -1:
...                 axeslist[i] = ax
...                 ax += 1
...         return axeslist
...
>>> def sum_squares_py(arr, axis=None, out=None):
...     axeslist = axis_to_axeslist(axis, arr.ndim)
...     it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop',
...                                       'buffered', 'delay_bufalloc'],
...                 op_flags=[['readonly'], ['readwrite', 'allocate']],
...                 op_axes=[None, axeslist],
...                 op_dtypes=['float64', 'float64'])
...     it.operands[1][...] = 0
...     it.reset()
...     for x, y in it:
...         y[...] += x*x
...     return it.operands[1]
...
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_py(a)
array(55.0)
>>> sum_squares_py(a, axis=-1)
array([  5.,  50.])

इस फ़ंक्शन को साइथन-ize करने के लिए, हम साइथॉन कोड के साथ इनर लूप (y […] + = x * x) को बदलते हैं जो फ्लोट64 डायटाइप के लिए विशेष है। 'External_loop' ध्वज सक्षम होने के साथ, आंतरिक लूप को प्रदान की गई सरणियाँ हमेशा एक-आयामी होंगी, इसलिए बहुत कम जाँच की जानी चाहिए।

यहाँ sum_squares.pyx की सूची दी गई है:

import numpy as np
cimport numpy as np
cimport cython

def axis_to_axeslist(axis, ndim):
    if axis is None:
        return [-1] * ndim
    else:
        if type(axis) is not tuple:
            axis = (axis,)
        axeslist = [1] * ndim
        for i in axis:
            axeslist[i] = -1
        ax = 0
        for i in range(ndim):
            if axeslist[i] != -1:
                axeslist[i] = ax
                ax += 1
        return axeslist

@cython.boundscheck(False)
def sum_squares_cy(arr, axis=None, out=None):
    cdef np.ndarray[double] x
    cdef np.ndarray[double] y
    cdef int size
    cdef double value

    axeslist = axis_to_axeslist(axis, arr.ndim)
    it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop',
                                      'buffered', 'delay_bufalloc'],
                op_flags=[['readonly'], ['readwrite', 'allocate']],
                op_axes=[None, axeslist],
                op_dtypes=['float64', 'float64'])
    it.operands[1][...] = 0
    it.reset()
    for xarr, yarr in it:
        x = xarr
        y = yarr
        size = x.shape[0]
        for i in range(size):
           value = x[i]
           y[i] = y[i] + value * value
    return it.operands[1]

इस मशीन पर, .pyx फ़ाइल को एक मॉड्यूल में बनाना निम्न की तरह दिखता है, लेकिन आपको अपने सिस्टम कॉन्फ़िगरेशन के लिए बारीकियों को बताने के लिए कुछ साइथन ट्यूटोरियल खोजने पड़ सकते हैं।

$ cython sum_squares.pyx
$ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python2.7 -fno-strict-aliasing -o sum_squares.so sum_squares.c

पायथन इंटरप्रेटर से इसे चलाने से हमारे देशी पायथन / NumPy कोड के समान उत्तर मिलते हैं।

उदाहरण

>>> from sum_squares import sum_squares_cy
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_cy(a)
array(55.0)
>>> sum_squares_cy(a, axis=-1)
array([  5.,  50.])

आईपीथॉन में थोड़ा सा समय देना दर्शाता है कि साइथॉन इनर लूप का कम ओवरहेड और मेमोरी एलोकेशन, सीधा पाइथन कोड दोनों पर बहुत अच्छा स्पीडअप प्रदान कर रहा है और न्यूमिथ के बिल्ट-इन सम्‍पर्क का उपयोग करते हुए एक एक्‍सप्रेशन:

>>> a = np.random.rand(1000,1000)

>>> timeit sum_squares_py(a, axis=-1)
10 loops, best of 3: 37.1 ms per loop

>>> timeit np.sum(a*a, axis=-1)
10 loops, best of 3: 20.9 ms per loop

>>> timeit sum_squares_cy(a, axis=-1)
100 loops, best of 3: 11.8 ms per loop

>>> np.all(sum_squares_cy(a, axis=-1) == np.sum(a*a, axis=-1))
True

>>> np.all(sum_squares_py(a, axis=-1) == np.sum(a*a, axis=-1))
True