एक NumPy 2d सरणी का टुकड़ा करना, या मैं एक nxn सरणी (n> m) से mxm सबमेट्रिक्स कैसे निकालूं?


174

मैं एक NumPy nxn सरणी का टुकड़ा करना चाहता हूं। मैं उस सरणी की m पंक्तियों और स्तंभों का एक मनमाना चयन करना चाहता हूं (अर्थात पंक्तियों / स्तंभों की संख्या में बिना किसी पैटर्न के), यह एक नया, mxm सरणी बनाता है। इस उदाहरण के लिए मान लीजिए कि सरणी 4x4 है और मैं इसमें से 2x2 सरणी निकालना चाहता हूं।

यहाँ हमारे सरणी है:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

निकालने के लिए लाइन और कॉलम समान हैं। सबसे आसान मामला तब है जब मैं एक 2x2 सबमेट्रिक्स निकालना चाहता हूं जो शुरुआत में या अंत में है, यानी:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

लेकिन क्या होगा अगर मुझे पंक्तियों / स्तंभों का एक और मिश्रण निकालने की आवश्यकता है? क्या होगा अगर मुझे पहली और तीसरी पंक्तियों / पंक्तियों को हटाने की आवश्यकता है, इस प्रकार सबमेट्रिक्स को निकालना [[5,7],[13,15]]? पंक्तियों / रेखाओं की कोई भी रचना हो सकती है। मैंने कहीं पढ़ा है कि मुझे केवल पंक्तियों और स्तंभों के लिए सरणियों / सूचियों की सूची का उपयोग करके अपने सरणी को अनुक्रमित करने की आवश्यकता है, लेकिन यह काम नहीं करता है:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

मुझे एक रास्ता मिला, जो है:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

इसके साथ पहला मुद्दा यह है कि यह शायद ही पठनीय है, हालांकि मैं इसके साथ रह सकता हूं। अगर किसी के पास बेहतर समाधान है, तो मैं निश्चित रूप से इसे सुनना चाहूंगा।

अन्य बात यह है कि मैंने एक मंच पर पढ़ा है कि सरणियों के साथ अनुक्रमण अनुक्रमणिका वांछित सरणी की एक प्रतिलिपि बनाने के लिए NumPy को बाध्य करता है, इस प्रकार जब बड़े सरणियों के साथ इलाज किया जाता है तो यह एक समस्या बन सकती है। ऐसा क्यों है / यह तंत्र कैसे काम करता है?

जवाबों:


62

जैसा कि स्वेन ने उल्लेख किया है, x[[[0],[2]],[1,3]]0 और 2 पंक्तियों को 1 और 3 कॉलम के साथ मेल खाते हुए वापस दे देंगे जबकि x[[0,2],[1,3]]एक सरणी में x [0,1] और x [2,3] मानों को वापस कर देंगे।

मेरे द्वारा दिए गए पहले उदाहरण को करने के लिए एक उपयोगी कार्य है numpy.ix_। आप मेरे पहले उदाहरण के साथ भी ऐसा ही कर सकते हैं x[numpy.ix_([0,2],[1,3])]। यह आपको उन सभी अतिरिक्त कोष्ठकों में प्रवेश करने से बचा सकता है।


111

इस सवाल का जवाब देने के लिए, हमें यह देखना होगा कि Numpy में एक बहुआयामी सरणी कैसे काम करता है। आइए सबसे पहले कहते हैं कि आपके पास xआपके प्रश्न का सरणी है। बफर को x0 से 15. तक 16 आरोही पूर्णांक शामिल होंगे। यदि आप एक तत्व का उपयोग करते हैं, तो कहते हैं x[i,j], NumPy को बफर की शुरुआत के सापेक्ष इस तत्व की मेमोरी लोकेशन का पता लगाना है। यह प्रभाव में गणना i*x.shape[1]+jकरके (और एक वास्तविक मेमोरी ऑफसेट प्राप्त करने के लिए एक इंट के आकार के साथ गुणा करके) किया जाता है।

यदि आप बुनियादी स्लाइसिंग जैसे कि एक सबर्रे निकालते हैं y = x[0:2,0:2], तो परिणामस्वरूप ऑब्जेक्ट अंतर्निहित बफर को साझा करेगा x। लेकिन अगर आप आरोप लगाते हैं तो क्या होता है y[i,j]? NumPy i*y.shape[1]+jसरणी में ऑफ़सेट की गणना करने के लिए उपयोग नहीं कर सकता है , क्योंकि संबंधित डेटा yस्मृति में लगातार नहीं है।

NumPy शुरू करने से इस समस्या का हल प्रगति । एक्सेस करने के लिए मेमोरी ऑफ़सेट की गणना करते समय x[i,j], वास्तव में क्या गणना की जाती है i*x.strides[0]+j*x.strides[1](और इसमें पहले से ही एक इंट के आकार के लिए कारक शामिल हैं):

x.strides
(16, 4)

जब yऊपर की तरह निकाला जाता है, NumPy एक नया बफर पैदा नहीं करता है, लेकिन यह करता है एक ही बफर (अन्यथा संदर्भित एक नई सरणी वस्तु बनाने के yबस के बराबर होगा xतो।) नई सरणी वस्तु एक अलग आकार होगा xऔर हो सकता है एक अलग शुरू बफर में ऑफसेट, लेकिन साथ साझा करेंगे x(इस मामले में कम से कम):

y.shape
(2,2)
y.strides
(16, 4)

इस तरह, मेमोरी ऑफ़सेट की गणना करने y[i,j]से सही परिणाम मिलेगा।

लेकिन क्या कुछ के लिए NumPy करना चाहिए z=x[[1,3]]? यदि मूल बफ़र के लिए उपयोग किया जाता है तो स्ट्राइड तंत्र सही अनुक्रमण की अनुमति नहीं देगा z। NumPy सैद्धांतिक रूप से स्ट्राइड्स की तुलना में कुछ अधिक परिष्कृत तंत्र जोड़ सकता है , लेकिन यह किसी ऐरे के संपूर्ण विचार को धता बताते हुए किसी तरह से तत्व को महंगा बना देगा। इसके अलावा, एक दृश्य अब वास्तव में हल्के वस्तु नहीं होगा।

यह अनुक्रमण पर NumPy प्रलेखन में गहराई से कवर किया गया है ।

ओह, और लगभग आपके वास्तविक प्रश्न के बारे में भूल गया: यहां बताया गया है कि कई सूचियों के साथ अनुक्रमण को कैसे अपेक्षित रूप से काम करें:

x[[[1],[3]],[1,3]]

इसका कारण यह है कि सूचकांक सरणियों को एक सामान्य आकार में प्रसारित किया जाता है। बेशक, इस विशेष उदाहरण के लिए, आप मूल स्लाइसिंग के साथ भी कर सकते हैं:

x[1::2, 1::2]

यह उप-सरणियों को कम करने के लिए संभव हो सकता है ताकि एक "स्लेसी-व्यू" ऑब्जेक्ट हो सके जो मूल सरणी में अनुक्रमों को फिर से दबा देगा। संभवतः यह ओपी की जरूरतों को पूरा कर सकता है
jsbueno

@jsbueno: जो पायथन कोड के लिए काम करेगा, लेकिन सी / फोरट्रान रूटीन के लिए नहीं जो कि स्कैपी / नेम्पी लपेटता है। वे लिपटे हुए रूटीन हैं, जहां Numpy की शक्ति निहित है।
डाट चू

सू .. x [[[१], [३]], [१,३]] और x [[१,३],:] [:, [१,३]] में क्या अंतर है मेरा मतलब है कि एक ऐसा संस्करण है जो दूसरे की तुलना में उपयोग करना बेहतर है?
लेव्सकेव

1
@ जेसी: x[[[1],[3]],[1,3]]केवल एक नई सरणी बनाता है, जबकि x[[1,3],:][:,[1,3]]दो बार प्रतियां, इसलिए पहले एक का उपयोग करें।
स्वेन मार्नाच

@ जेसी: या जस्टिन के जवाब से विधि का उपयोग करें।
स्वेन मार्नाच

13

मुझे नहीं लगता कि x[[1,3]][:,[1,3]]यह शायद ही पठनीय है। यदि आप अपने इरादे पर अधिक स्पष्ट होना चाहते हैं, तो आप कर सकते हैं:

a[[1,3],:][:,[1,3]]

मैं स्लाइसिंग में विशेषज्ञ नहीं हूं, लेकिन आम तौर पर, यदि आप किसी ऐरे में स्लाइस करने की कोशिश करते हैं और मान निरंतर हैं, तो आपको एक दृश्य वापस मिलता है जहां स्ट्राइड वैल्यू बदल जाती है।

उदा। आपके इनपुट 33 और 34 में, हालाँकि आपको 2x2 सरणी मिलती है, स्ट्राइड 4. है। जब आप अगली पंक्ति को इंडेक्स करते हैं, तो पॉइंटर मेमोरी में सही स्थिति में चला जाता है।

स्पष्ट रूप से, यह तंत्र सूचकांकों की एक सरणी के मामले में अच्छी तरह से नहीं चलता है। इसलिए, कॉपी करने के लिए सुन्न करना होगा। आखिरकार, कई अन्य मैट्रिक्स गणित फ़ंक्शन आकार, स्ट्राइड और निरंतर मेमोरी आवंटन पर निर्भर करते हैं।


10

यदि आप हर दूसरी पंक्ति और हर दूसरे कॉलम को छोड़ना चाहते हैं, तो आप इसे बुनियादी स्लाइसिंग के साथ कर सकते हैं:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

यह आपके व्यूज़ की कॉपी नहीं, बल्कि एक व्यू देता है।

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

z=x[(1,3),:][:,(1,3)]उन्नत अनुक्रमण का उपयोग करते समय और इस प्रकार एक प्रति लौटाता है:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

ध्यान दें कि xअपरिवर्तित है:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

यदि आप मनमानी पंक्तियों और स्तंभों का चयन करना चाहते हैं, तो आप मूल स्लाइसिंग का उपयोग नहीं कर सकते। आपको उन्नत अनुक्रमण का उपयोग करना होगा x[rows,:][:,columns], जैसे कुछ , जहां rowsऔर columnsअनुक्रम हैं। यह निश्चित रूप से आपको एक कॉपी देने वाला है, न कि आपके मूल व्यू का। यह उतना ही होना चाहिए जितना कि एक उम्मीद की जानी चाहिए, क्योंकि एक अफीम सरणी सन्निहित मेमोरी (निरंतर स्ट्रैड के साथ) का उपयोग करती है, और मनमाने ढंग से पंक्तियों और स्तंभों के साथ एक दृश्य उत्पन्न करने का कोई तरीका नहीं होगा (क्योंकि इसके लिए गैर-स्थिर स्ट्राइड की आवश्यकता होगी)।


5

सुन्न के साथ, आप सूचकांक के प्रत्येक घटक के लिए एक टुकड़ा पास कर सकते हैं - इसलिए, x[0:2,0:2]ऊपर दिए गए आपके उदाहरण काम करते हैं।

यदि आप स्तंभों या पंक्तियों को समान रूप से छोड़ना चाहते हैं, तो आप स्लाइस को तीन घटकों (जैसे प्रारंभ, रोक, चरण) के साथ पास कर सकते हैं।

फिर, आपके उदाहरण के लिए ऊपर:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

जो मूल रूप से है: पहले आयाम में स्लाइस, इंडेक्स 1 पर शुरू होने के साथ, इंडेक्स के 4 या उससे अधिक होने पर रोकें, और प्रत्येक पास में इंडेक्स में 2 जोड़ें। दूसरे आयाम के लिए समान। दोबारा: यह केवल निरंतर चरणों के लिए काम करता है।

सिंटैक्स आपको आंतरिक रूप से कुछ अलग करने के लिए मिला - जो x[[1,3]][:,[1,3]]वास्तव में करता है वह एक नई सरणी बनाता है जिसमें मूल सरणी से केवल पंक्तियों 1 और 3 शामिल हैं ( x[[1,3]]भाग के साथ किया गया), और उसके बाद फिर से स्लाइस करें - एक तीसरा सरणी - केवल सहित पिछले सरणी के कॉलम 1 और 3।


1
यह समाधान काम नहीं करता है क्योंकि यह उन पंक्तियों / स्तंभों के लिए विशिष्ट है जिन्हें मैं निकालने की कोशिश कर रहा था। एक 50x50 मैट्रिक्स में उसी की कल्पना करें, जब मैं पंक्तियों / स्तंभों को 5,11,12,32,39,45 पर निकालना चाहता हूं, तो साधारण स्लाइस के साथ ऐसा करने का कोई तरीका नहीं है। क्षमा करें यदि मैं अपने प्रश्न में स्पष्ट नहीं था।
लेवेस्क नोव

3

मेरा यहाँ भी ऐसा ही प्रश्न है: सबसे पाइथोनियन तरीके से एक ndarray के उप-नादरे में लिखना। अजगर २

आपके मामले के लिए पिछली पोस्ट के समाधान के बाद समाधान जैसा दिखता है:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

Ix_ का उपयोग कर:

x[np.ix_(rows_to_keep, columns_to_keep)] 

जो है:

array([[ 5,  7],
       [13, 15]])

0

मुझे यकीन नहीं है कि यह कितना कुशल है, लेकिन आप दोनों अक्ष में श्रेणी () का उपयोग कर सकते हैं

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.