2010-01-21 21 views
28

मैं लूप में एक आयामी numpy arrays की एक सूची उत्पन्न करता हूं और बाद में इस सूची को 2 डी numpy सरणी में परिवर्तित करता हूं। अगर मैं समय से पहले वस्तुओं की संख्या जानता था, तो मैं एक 2 डी numpy सरणी preallocated होगा, लेकिन मैं नहीं, इसलिए मैं सब कुछ एक सूची में डाल दिया।numpy arrays की सूची से एक numpy सरणी बनाने के लिए पाइथोनिक तरीका

नकली अप के नीचे है:

>>> list_of_arrays = map(lambda x: x*ones(2), range(5)) 
>>> list_of_arrays 
[array([ 0., 0.]), array([ 1., 1.]), array([ 2., 2.]), array([ 3., 3.]), array([ 4., 4.])] 
>>> arr = array(list_of_arrays) 
>>> arr 
array([[ 0., 0.], 
     [ 1., 1.], 
     [ 2., 2.], 
     [ 3., 3.], 
     [ 4., 4.]]) 

मेरा प्रश्न यह है: (

वहाँ एक बेहतर तरीका (performancewise) अनुक्रमिक संख्यात्मक डेटा इकट्ठा करने का कार्य के बारे में जाना है में मेरी केस numpy arrays) उन्हें एक सूची में डालने और फिर एक numpy.array बनाने से बाहर (मैं एक नया obj बना रहा हूँ और डेटा की प्रतिलिपि बना रहा हूँ)? क्या एक अच्छी तरह से परीक्षण मॉड्यूल में उपलब्ध एक "विस्तार योग्य" मैट्रिक्स डेटा संरचना है?

मेरी 2d मैट्रिक्स का एक विशिष्ट आकार 100x10 के बीच हो सकता है और 5000x10 तैरता

संपादित करें: इस उदाहरण में मैं नक्शा उपयोग कर रहा हूँ, लेकिन मेरी वास्तविक आवेदन में मैं एक पाश

के लिए

उत्तर

14

मान लीजिए कि आप जानते हैं कि अंतिम सरणी arr 5000x10 से अधिक कभी नहीं होगी। फिर आप अधिकतम आकार की सरणी आवंटित कर सकते हैं, इसे के रूप में डेटा के साथ पॉप्युलेट करें, आप लूप से गुजरते हैं, और फिर arr.resize का उपयोग लूप से बाहर निकलने के बाद खोजे गए आकार में कटौती करने के लिए करें।

नीचे दिए गए परीक्षणों का सुझाव है कि इंटरमीडिएट पायथन सूचियों के निर्माण से थोड़ा तेज़ होगा, इससे कोई फर्क नहीं पड़ता कि सरणी का अंतिम आकार क्या है।

इसके अलावा, arr.resize अप्रयुक्त स्मृति को डी-आवंटित करता है, इसलिए अंतिम (हालांकि शायद मध्यवर्ती नहीं) स्मृति पदचिह्न python_lists_to_array द्वारा उपयोग किए जाने वाले छोटे से छोटा है।

इससे पता चलता है numpy_all_the_way तेजी से होता है:

% python -mtimeit -s"import test" "test.numpy_all_the_way(100)" 
100 loops, best of 3: 1.78 msec per loop 
% python -mtimeit -s"import test" "test.numpy_all_the_way(1000)" 
100 loops, best of 3: 18.1 msec per loop 
% python -mtimeit -s"import test" "test.numpy_all_the_way(5000)" 
10 loops, best of 3: 90.4 msec per loop 

% python -mtimeit -s"import test" "test.python_lists_to_array(100)" 
1000 loops, best of 3: 1.97 msec per loop 
% python -mtimeit -s"import test" "test.python_lists_to_array(1000)" 
10 loops, best of 3: 20.3 msec per loop 
% python -mtimeit -s"import test" "test.python_lists_to_array(5000)" 
10 loops, best of 3: 101 msec per loop 

इससे पता चलता है numpy_all_the_way कम स्मृति का उपयोग करता है:

% test.py 
Initial memory usage: 19788 
After python_lists_to_array: 20976 
After numpy_all_the_way: 20348 

test.py:

#!/usr/bin/env python 
import numpy as np 
import os 

def memory_usage(): 
    pid=os.getpid() 
    return next(line for line in open('/proc/%s/status'%pid).read().splitlines() 
      if line.startswith('VmSize')).split()[-2] 

N,M=5000,10 

def python_lists_to_array(k): 
    list_of_arrays = map(lambda x: x*np.ones(M), range(k)) 
    arr = np.array(list_of_arrays) 
    return arr 

def numpy_all_the_way(k): 
    arr=np.empty((N,M)) 
    for x in range(k): 
     arr[x]=x*np.ones(M) 
    arr.resize((k,M)) 
    return arr 

if __name__=='__main__': 
    print('Initial memory usage: %s'%memory_usage()) 
    arr=python_lists_to_array(5000) 
    print('After python_lists_to_array: %s'%memory_usage())  
    arr=numpy_all_the_way(5000) 
    print('After numpy_all_the_way: %s'%memory_usage())  
2

है क्या आप कर रहे हैं मानक तरीका है। Numpy arrays की एक संपत्ति यह है कि उन्हें संगत स्मृति की आवश्यकता है। stridesPyArrayObject के सदस्य के साथ "छेद" की एकमात्र संभावना संभव है, लेकिन इससे चर्चा पर कोई प्रभाव नहीं पड़ता है। चूंकि numpy arrays में संगत स्मृति होती है और "प्रीलोकेटेड" होती है, एक नई पंक्ति/कॉलम जोड़ने का अर्थ है नई मेमोरी आवंटित करना, डेटा कॉपी करना, और फिर पुरानी मेमोरी को मुक्त करना। यदि आप ऐसा करते हैं, तो यह बहुत ही कुशल नहीं है।

एक मामला जहां कोई सूची बनाना नहीं चाहता है और फिर अंत में एक numpy सरणी में परिवर्तित करना है, जब सूची में बहुत संख्याएं होती हैं: संख्याओं की एक संख्यात्मक सरणी देशी पायथन सूची से बहुत कम जगह लेती है संख्याओं (क्योंकि देशी पायथन सूची पाइथन वस्तुओं को स्टोर करती है)। आपके विशिष्ट सरणी आकारों के लिए, मुझे नहीं लगता कि यह एक मुद्दा है।

जब आप सरणी की सूची से अपना अंतिम सरणी बनाते हैं, तो आप सभी डेटा को नए (2-डी में आपके उदाहरण में) के लिए एक नए स्थान पर कॉपी करते हैं। हर बार जब आप नया डेटा प्राप्त करते हैं तो यह एक numpy सरणी होने और next = numpy.vstack((next, new_row)) करने से कहीं अधिक कुशल है। vstack() प्रत्येक "पंक्ति" के लिए सभी डेटा कॉपी करेगा।

कुछ समय पहले thread on numpy-discussion mailing list था जिसने एक नया numpy सरणी प्रकार जोड़ने की संभावना पर चर्चा की जो कुशल विस्तार/संलग्न करने की अनुमति देता है। ऐसा लगता है कि उस समय इसमें महत्वपूर्ण रुचि थी, हालांकि मुझे नहीं पता कि इससे कुछ निकला है या नहीं। आप उस धागे को देखना चाह सकते हैं।

मैं कहूंगा कि आप जो कर रहे हैं वह बहुत पाइथनिक और कुशल है, इसलिए जब तक आपको वास्तव में कुछ और चाहिए (अधिक स्थान दक्षता, शायद?), तो आपको ठीक होना चाहिए। इस तरह मैं अपने numpy arrays बनाते हैं जब मैं शुरुआत में सरणी में तत्वों की संख्या नहीं जानता।

+0

@Alok --- विचारशील जवाब के लिए धन्यवाद । ~ Unubuntu से उत्तर में समय 5% दक्षता की चिंता दिखाते हैं। यह लगभग निश्चित रूप से एक गलती है जब तक आप उस बिंदु तक नहीं पहुंच जाते कि आपके पास बिल्कुल 5% होना चाहिए। – telliott99

2

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

initial_guess = 1000 

def my_numpy_all_the_way(k): 
    arr=np.empty((initial_guess,M)) 
    for x,row in enumerate(make_test_data(k)): 
     try: 
      arr[x]=row 
     except IndexError: 
      arr.resize((arr.shape[0]*2, arr.shape[1])) 
      arr[x]=row 
    arr.resize((k,M)) 
    return arr 
11

numpy.concatenate का उपयोग करके सुविधाजनक तरीका। मेरा मानना ​​है कि यह भी तेज है, @ unutbu के जवाब से:

In [32]: import numpy as np 

In [33]: list_of_arrays = list(map(lambda x: x * np.ones(2), range(5))) 

In [34]: list_of_arrays 
Out[34]: 
[array([ 0., 0.]), 
array([ 1., 1.]), 
array([ 2., 2.]), 
array([ 3., 3.]), 
array([ 4., 4.])] 

In [37]: shape = list(list_of_arrays[0].shape) 

In [38]: shape 
Out[38]: [2] 

In [39]: shape[:0] = [len(list_of_arrays)] 

In [40]: shape 
Out[40]: [5, 2] 

In [41]: arr = np.concatenate(list_of_arrays).reshape(shape) 

In [42]: arr 
Out[42]: 
array([[ 0., 0.], 
     [ 1., 1.], 
     [ 2., 2.], 
     [ 3., 3.], 
     [ 4., 4.]]) 
0

@Gill बेट्स 'जवाब से भी सरल, यहाँ एक एक लाइन कोड है:

np.stack(list_of_arrays, axis=0) 
संबंधित मुद्दे