2011-11-23 17 views
8

की दक्षता में सुधार कैसे कर सकता हूं मुझे लेबल युक्त एक numpy सरणी मिली है। मैं अपने आकार और बाध्यकारी बॉक्स के आधार पर प्रत्येक लेबल के लिए एक संख्या की गणना करना चाहता हूं। मैं इसे और अधिक कुशलता से कैसे लिख सकता हूं ताकि बड़े सरणी (~ 15000 लेबल) पर उपयोग करना यथार्थवादी हो?मैं इस numpy लूप

A = array([[ 1, 1, 0, 3, 3], 
      [ 1, 1, 0, 0, 0], 
      [ 1, 0, 0, 2, 2], 
      [ 1, 0, 2, 2, 2]]) 

B = zeros(4) 

for label in range(1, 4): 
    # get the bounding box of the label 
    label_points = argwhere(A == label) 
    (y0, x0), (y1, x1) = label_points.min(0), label_points.max(0) + 1 

    # assume I've computed the size of each label in a numpy array size_A 
    B[ label ] = myfunc(y0, x0, y1, x1, size_A[label]) 
+0

वास्तविक उपयोग मामले में 'ए' कितना बड़ा है? –

+1

Ballpark 7000x9000 – ajwood

+0

क्या आपने कुछ प्रोफाइलिंग किया है यह देखने के लिए कि आपके कौन से बयान आपको धीमा कर रहे हैं? हो सकता है कि यह 'myfunc' फ़ंक्शन है जिसे लूप से बाहर निकलने वाले अलग-अलग सरणी में y0, x0, y1, x1 को सहेजकर और केवल एक बार फ़ंक्शन को कॉल करके सहेजा जा सकता है। अन्यथा, यदि गति वास्तव में मायने रखती है, तो आप यह देखना चाहेंगे कि कुछ सी कोड करने के लायक है या नहीं। मैंने नमस्ते सरणी के साथ काम करते समय साइथन को वास्तव में आरामदायक महसूस किया। –

उत्तर

7

मैं वास्तव में का उपयोग कर कुछ NumPy vectorised कार्यों को कुशलता से इस लागू करने में सक्षम नहीं था, इसलिए हो सकता है एक चतुर अजगर कार्यान्वयन तेजी से हो जाएगा।

def first_row(a, labels): 
    d = {} 
    d_setdefault = d.setdefault 
    len_ = len 
    num_labels = len_(labels) 
    for i, row in enumerate(a): 
     for label in row: 
      d_setdefault(label, i) 
     if len_(d) == num_labels: 
      break 
    return d 

यह फ़ंक्शन शब्दकोश पहली पंक्ति यह भी दिखाई देने वाली इंडेक्स पर लेबल मानचित्रण देता है। A को समारोह, A.T, A[::-1] और A.T[::-1] भी आप पहले कॉलम देता है और साथ ही अंतिम पंक्ति को लागू करने और स्तंभ।

यदि आप एक शब्दकोश के बजाय सूची पसंद करना चाहते हैं, तो आप map(d.get, labels) का उपयोग करके शब्दकोश को एक सूची में बदल सकते हैं। वैकल्पिक रूप से, आप शुरुआत से ही एक शब्दकोश के बजाय एक NumPy सरणी का उपयोग कर सकते हैं, लेकिन जैसे ही सभी लेबल पाए गए थे, आप लूप को छोड़ने की क्षमता खो देंगे।

मुझे दिलचस्पी होगी कि (और कितना) यह वास्तव में आपके कोड को गति देता है, लेकिन मुझे विश्वास है कि यह आपके मूल समाधान से तेज़ है।

+0

यह जाने का सबसे सुंदर तरीका नहीं है, लेकिन यह निश्चित रूप से काम करता है। मेरा मूल तरीका इतनी देर तक चला कि मैंने इसे कभी खत्म नहीं किया (20 मिनट के बाद छोड़ दिया)। मैंने बस अपनी विधि को चलाया और इसे 6m30s में मिला। – ajwood

+0

@ajwood: प्रतिक्रिया के लिए धन्यवाद। मुझे पता है कि यह सुंदर नहीं है, लेकिन सबसे अच्छा आसान समाधान मैं सोच सकता हूं। यदि आप इसे और भी तेज़ी से करना चाहते हैं, तो मैं इसे साइथन में लागू करने का सुझाव दूंगा। –

1

प्रदर्शन बाधा वास्तव में argmax पर कॉल होने लगती है। (केवल कंप्यूटिंग Y0, y1, लेकिन x0 को सामान्य करने के लिए आसान, x 1) यह लूप के रूप में इस प्रकार बदल कर बचा जा सकता है:

for label in range(1, 4): 
    comp = (A == label) 
    yminind = comp.argmax(0) 
    ymin = comp.max(0) 
    ymaxind = comp.shape[0] - comp[::-1].argmax(0) 
    y0 = yminind[ymin].min() 
    y1 = ymaxind[ymin].max() 

मैं प्रदर्शन अंतर की वजह के बारे में यकीन नहीं है, लेकिन एक कारण हो सकता है कि ==, argmax, और max जैसे सभी ऑपरेशन इनपुट आउटर के आकार से सीधे अपने आउटपुट सरणी को प्रीलोकेट कर सकें, जो argwhere के लिए संभव नहीं है।

5

एल्गोरिथ्म:

  1. परिवर्तन एक आयाम
  2. को सरणी argsort()
  3. क्रमबद्ध sorted_A
  4. उपयोग के रूप में आयाम सरणी पर की संस्करण प्राप्त जहां() और के आधार पर चुनना सूचकांक मिल sorted_A
  5. में लेबल परिवर्तन स्थिति खोजने के लिए diff() एक आयाम में लेबल की मूल स्थिति प्राप्त करने के लिए परिवर्तन स्थिति और सॉर्ट इंडेक्स का उपयोग करें।
  6. आयाम स्थिति से दो आयाम स्थान की गणना करें।

बड़ी सरणी (7000, 9 000) के लिए, 30 के दशक में गणना समाप्त हो सकती है।

यहाँ कोड है:

import numpy as np 

A = np.array([[ 1, 1, 0, 3, 3], 
      [ 1, 1, 0, 0, 0], 
      [ 1, 0, 0, 2, 2], 
      [ 1, 0, 2, 2, 2]]) 

def label_range(A): 
    from itertools import izip_longest 
    h, w = A.shape 
    tmp = A.reshape(-1) 

    index = np.argsort(tmp) 
    sorted_A = tmp[index] 
    pos = np.where(np.diff(sorted_A))[0]+1 
    for p1,p2 in izip_longest(pos,pos[1:]): 
     label_index = index[p1:p2] 
     y = label_index // w 
     x = label_index % w 

     x0 = np.min(x) 
     x1 = np.max(x)+1 
     y0 = np.min(y) 
     y1 = np.max(y)+1 
     label = tmp[label_index[0]] 

     yield label,x0,y0,x1,y1 

for label,x0,y0,x1,y1 in label_range(A): 
    print "%d:(%d,%d)-(%d,%d)" % (label, x0,y0,x1,y1) 

#B = np.random.randint(0, 100, (7000, 9000)) 
#list(label_range(B)) 
+0

मैंने गलती से आपको पोस्ट छोड़ दिया क्योंकि मैंने सोचा था कि एल्गोरिदम गलत था। वोट को अनलॉक करने के लिए मुझे एक डमी संपादन करना पड़ा - इसके बजाए ऊपर उठाया गया। :) –

5

एक अन्य विधि:

उपयोग bincount() लेबल हर पंक्ति और स्तंभ में गिनती मिलता है, और में पंक्तियों और कॉलम सरणी जानकारी को बचाने के लिए।

प्रत्येक लेबल के लिए आपको केवल पंक्तियों और स्तंभों में सीमा को खोजने की आवश्यकता है।यह मेरे पीसी पर, सॉर्ट से तेज़ है, यह कुछ सेकंड में गणना कर सकता है।

def label_range2(A): 
    maxlabel = np.max(A)+1 
    h, w = A.shape 
    rows = np.zeros((h, maxlabel), np.bool) 
    for row in xrange(h): 
     rows[row,:] = np.bincount(A[row,:], minlength=maxlabel) > 0 

    cols = np.zeros((w, maxlabel), np.bool) 
    for col in xrange(w): 
     cols[col,:] =np.bincount(A[:,col], minlength=maxlabel) > 0 

    for label in xrange(1, maxlabel): 
     row = rows[:, label] 
     col = cols[:, label] 
     y = np.where(row)[0] 
     x = np.where(col)[0] 
     x0 = np.min(x) 
     x1 = np.max(x)+1 
     y0 = np.min(y) 
     y1 = np.max(y)+1   
     yield label, x0,y0,x1,y1 
+0

यह बहुत ही आशाजनक लग रहा है, और मैं इसे आज़माकर आज़मा दूंगा। – ajwood

1

पीपीपी का उपयोग करके आप केवल लूप चला सकते हैं और वेक्टरेशन के बारे में चिंता नहीं कर सकते हैं। यह तेज़ होना चाहिए।