2012-06-29 14 views
7

मैं इस गणना में सभी अजगर छोरों को खत्म कर सकते हैं:मैं इस ट्रिपल-लूप को numpy में 2 डी सरणी से कैसे सदिश कर सकता हूं?

result[i,j,k] = (x[i] * y[j] * z[k]).sum() 

जहां x[i], y[j], z[k] लंबाई N और x, y की वैक्टर हैं, z लंबाई A, B के साथ पहली आयाम है, C s.t. आउटपुट आकार (A,B,C) है और प्रत्येक तत्व एक ट्रिपल-उत्पाद (तत्व-वार) का योग है।

मैं इसे 3 से 1 loops (नीचे कोड) से नीचे प्राप्त कर सकता हूं, लेकिन को अंतिम लूप को खत्म करने की कोशिश कर रहा हूं।

यदि आवश्यक हो तो मैं A=B=C (पैडिंग की थोड़ी मात्रा के माध्यम से) बना सकता हूं।

np.einsum('im,jm,km->ijk',x,y,z) 

जो आपके फंस संस्करणों के बराबर है:

# Example with 3 loops, 2 loops, 1 loop (testing omitted) 

N = 100 # more like 100k in real problem 
A = 2 # more like 20 in real problem 
B = 3 # more like 20 in real problem 
C = 4 # more like 20 in real problem 

import numpy 
x = numpy.random.rand(A, N) 
y = numpy.random.rand(B, N) 
z = numpy.random.rand(C, N) 

# outputs of each variant 
result_slow = numpy.empty((A,B,C)) 
result_vec_C = numpy.empty((A,B,C)) 
result_vec_CB = numpy.empty((A,B,C)) 

# 3 nested loops 
for i in range(A): 
    for j in range(B): 
     for k in range(C): 
      result_slow[i,j,k] = (x[i] * y[j] * z[k]).sum() 

# vectorize loop over C (2 nested loops) 
for i in range(A): 
    for j in range(B): 
     result_vec_C[i,j,:] = (x[i] * y[j] * z).sum(axis=1) 

# vectorize one C and B (one loop) 
for i in range(A): 
    result_vec_CB[i,:,:] = numpy.dot(x[i] * y, z.transpose()) 

numpy.testing.assert_almost_equal(result_slow, result_vec_C) 
numpy.testing.assert_almost_equal(result_slow, result_vec_CB) 
+0

क्या यह होमवर्क है? – Dhara

+6

अफसोस की बात है, यह होमवर्क समस्या नहीं है। असल में मैं रोमांचित होगा अगर "वेक्टरेट कैसे करें" के सामान्य विषय पर पाठ्यक्रम/पाठ्यपुस्तक थे! –

उत्तर

9

आप numpy> 1.6 उपयोग कर रहे हैं, वहाँ भयानक np.einsum कार्य है। मुझे यकीन नहीं है कि वास्तविक समस्या में आपके सरणी के आकार तक पहुंचने के बाद यह दक्षता पर कैसे निष्पक्ष होगा (जब मैं उन आकारों पर जाता हूं, तो मुझे वास्तव में मेरी मशीन पर एक सेगफॉल्ट मिल रहा है)। अन्य समाधान जो मैं अक्सर इस तरह की समस्याओं के लिए पसंद करता हूं, साइथन का उपयोग करके विधि को फिर से लिख रहा है।

+0

वाह, यह आश्चर्यजनक है, मुझे नहीं पता था कि यह अस्तित्व में था, धन्यवाद! –

8

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

>>> x = numpy.arange(2 * 4).reshape(2, 4) 
>>> y = numpy.arange(3 * 4).reshape(3, 4) 
>>> z = numpy.arange(4 * 4).reshape(4, 4) 
>>> (x.reshape(2, 1, 1, 4) * 
... y.reshape(1, 3, 1, 4) * 
... z.reshape(1, 1, 4, 4)).sum(axis=3) 
array([[[ 36, 92, 148, 204], 
     [ 92, 244, 396, 548], 
     [ 148, 396, 644, 892]], 

     [[ 92, 244, 396, 548], 
     [ 244, 748, 1252, 1756], 
     [ 396, 1252, 2108, 2964]]]) 

आप टुकड़ा संकेतन का उपयोग करके यह थोड़ा अधिक सामान्यीकृत कर सकते हैं, newaxis मान (जो None के बराबर है, तो नीचे दिए गए None साथ के रूप में अच्छी तरह से काम करेगा), और तथ्य यह है कि sum नकारात्मक अक्ष मान स्वीकार करता है (-1 के साथ अंतिम, -2 को इंगित करता है कि अगले-से-आखिरी, और इसी तरह)। इस तरह, आपको सरणी के मूल आकार को जानने की ज़रूरत नहीं है; जब तक कि उनके अंतिम अक्ष संगत हों, यह पहले तीनों को एक साथ प्रसारित करेगा:

>>> (x[:, numpy.newaxis, numpy.newaxis, :] * 
... y[numpy.newaxis, :, numpy.newaxis, :] * 
... z[numpy.newaxis, numpy.newaxis, :, :]).sum(axis=-1) 
array([[[ 36, 92, 148, 204], 
     [ 92, 244, 396, 548], 
     [ 148, 396, 644, 892]], 

     [[ 92, 244, 396, 548], 
     [ 244, 748, 1252, 1756], 
     [ 396, 1252, 2108, 2964]]]) 

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

  • कोई संबंधित समस्या नहीं^_^