2017-02-11 132 views
0

我正在嘗試在Python中編寫一個函數,它將參數a作爲輸入列表並返回依賴於這些參數的向量列表u。然後,我希望能夠使用標準矢量操作(例如標量產品)對u採取行動。另外,在以下(從this post第二應答啓發)的例子中,該函數將返回兩個初始矢量vw的組合:向量化函數返回Python中的向量列表

import numpy as np 
v = np.array([1,0,0]) 
w = np.array([0,1,0]) 
a = np.linspace(0,np.pi,4) 
def u(a): 
    u = np.cos(a)*v + np.sin(a)*w 
    return u 
uvec = np.vectorize(u, otypes=[np.ndarray])  
ufin = np.array(uvec(a).tolist()) 
print "ufin =", ufin 
print "ufin.v =", np.dot(ufin,v) 

這將返回:

ufin = [[ 1.00000000e+00 0.00000000e+00 0.00000000e+00] 
[ 5.00000000e-01 8.66025404e-01 0.00000000e+00] 
[ -5.00000000e-01 8.66025404e-01 0.00000000e+00] 
[ -1.00000000e+00 1.22464680e-16 0.00000000e+00]] 
ufin.v = [ 1. 0.5 -0.5 -1. ] 

這是我想獲得自ufin表現得像一個向量。你能告訴我是否有其他更簡單的方法來實現這一點?我需要編寫一個代碼,其中需要定義大量的向量和向量操作,並希望它儘可能緊湊。

預先感謝您!


編輯:

我發現基於上次的答案this post另一個(顯然更緊湊)的解決方案。這個想法是將參數列表重塑爲一個列數組,使得該函數的輸出自動(不需要向量化)將一個向量列表作爲二維數組返回。這是這樣完成的:

import numpy as np 
from numpy.core.umath_tests import inner1d 

v = np.array([1,0,0]) 
w = np.array([0,1,0]) 
a = np.linspace(0,np.pi,4).reshape((4,1)) 
b = np.linspace(0,np.pi/2,4).reshape((4,1)) 
def u(a): 
    u = np.cos(a)*v + np.sin(a)*w 
    return u 
print "u(a) =",u(a) 
print "u(b) =",u(b) 
print "u(a).v =",np.dot(u(a),v) 
print "u(a)^v =",np.cross(u(a),v) 
# print "u(a).u(b) =",np.dot(u(a),u(b)) # does not work 
print "u(a).u(b) =",inner1d(u(a),u(b)) # works 
print "u(a)^u(b) =",np.cross(u(a),u(b)) 

這將返回:

u(a) = [[ 1.00000000e+00 0.00000000e+00 0.00000000e+00] 
[ 5.00000000e-01 8.66025404e-01 0.00000000e+00] 
[ -5.00000000e-01 8.66025404e-01 0.00000000e+00] 
[ -1.00000000e+00 1.22464680e-16 0.00000000e+00]] 
u(b) = [[ 1.00000000e+00 0.00000000e+00 0.00000000e+00] 
[ 8.66025404e-01 5.00000000e-01 0.00000000e+00] 
[ 5.00000000e-01 8.66025404e-01 0.00000000e+00] 
[ 6.12323400e-17 1.00000000e+00 0.00000000e+00]] 
u(a).v = [ 1. 0.5 -0.5 -1. ] 
u(a)^v = [[ 0.00000000e+00 0.00000000e+00 0.00000000e+00] 
[ 0.00000000e+00 0.00000000e+00 -8.66025404e-01] 
[ 0.00000000e+00 0.00000000e+00 -8.66025404e-01] 
[ 0.00000000e+00 0.00000000e+00 -1.22464680e-16]] 
u(a).u(b) = [ 1.00000000e+00 8.66025404e-01 5.00000000e-01 6.12323400e-17] 
u(a)^u(b) = [[ 0.   0.   0.  ] 
[ 0.   0.  -0.5  ] 
[ 0.   0.  -0.8660254] 
[ 0.   0.  -1.  ]] 

它既是涉及初始(uv)和載體(u(a)u(b)的輸出列出操作正確的行爲)以及涉及兩個輸出列表的操作。唯一需要注意的是(輸出矢量列表之間的操作)是必須使用inner1d函數代替標準np.dot,因爲後者被解釋爲由於兩個矩陣的大小不一致而無法完成的矩陣乘積。

回答

0

如果不反對獲得的2D陣列,而不是物體的陣列(前者比後者對於大多數用途更方便,反正)則外積針對此問題有用:

>>> np.outer(np.cos(a), v) + np.outer(np.sin(a), w) 
array([[ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00], 
     [ 5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00]]) 
>>> _ @ v 
array([ 1. , 0.5, -0.5, -1. ]) 

@ infix操作符只適用於python 3.5或更高版本。在舊版本中,您可以使用np.dot。無論在這方面的含義是matrix multiplication

+0

謝謝你的有用答案。我使用更簡潔的版本編輯了我的初始文章,該版本基於將參數列表重塑爲列數組。你能否告訴我你認爲最適合的方法? – user3450569

+0

有沒有標準的方法(如函數庫)用Python執行這些類型的操作?這種操作在處理物理問題時非常常見(例如,繪製軌道角動量作爲軌道參數的函數),所以如果出現這種情況,我會感到驚訝。 – user3450569

+0

@ user3450569是的,在這種情況下(您的功能恰好是「向量就緒」的),您的解決方案會更加優雅。你也可以寫'a = np.linspace(0,np.pi,4)[:,None]'來獲得我個人覺得在眼睛上更容易的列向量;但這是一個品味問題。你可能想看看'np.ogrid'和'np.ix_'這些在類似的情況下很有用。 –

0

所以你告訴vectorze函數u返回一個數組,實際上是object,因爲沒有dtype=array

In [475]: uvec=np.vectorize(u, otypes=[object]) 
In [476]: uvec(a) 
Out[476]: 
array([array([ 1., 0., 0.]), array([ 0.5  , 0.8660254, 0.  ]), 
     array([-0.5  , 0.8660254, 0.  ]), 
     array([ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00])], dtype=object) 

所以,如果你想「解開」這一點,創造你必須將其轉換爲陣列的列表,然後用np.array或(np.stack)加入他們沿着一個新的層面的二維數組:

In [477]: uvec(a).tolist() 
Out[477]: 
[array([ 1., 0., 0.]), 
array([ 0.5  , 0.8660254, 0.  ]), 
array([-0.5  , 0.8660254, 0.  ]), 
array([ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00])] 

但是對於像avectorize這樣的單個1d陣列的迭代已被過度殺死。一個簡單的列表理解作品一樣好

In [483]: np.array([u(i) for i in a]) 
Out[483]: 
array([[ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00], 
     [ 5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00]] 

vectorize是比較有用時,有幾個輸入數組,他們可以有幾個方面。它負責廣播和迭代多個層面。你也可以使用np.frompyfunc(u,1,1),它總是返回一個對象數組,並且速度稍快。

但你也可以計算這個不用循環:

def ufunc(a,v,w): 
    a = a[:,None] 
    return np.cos(a)*v + np.sin(a)*w 

In [498]: ufunc(a,v,w) 
Out[498]: 
array([[ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00], 
     [ 5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00]]) 

這原來a爲(4,1)陣列,廣播對(3,)v產生(4,3)陣列。


另一種方法是定義一個函數,標量(各地)

import math 
def u1(a,v,w): 
    return math.cos(a)*v+math.sin(a)*w 
u1v = np.vectorize(u1, otypes=[float]) 

In [505]: u1v(a[:,None], v, w) 
Out[505]: 
array([[ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00], 
     [ 5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -5.00000000e-01, 8.66025404e-01, 0.00000000e+00], 
     [ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00]]) 

這需要的vectorize充分廣播能力的優勢。我雖然它會比你的代碼慢,因爲它必須在兩個維度上循環,但它實際上測試更快(儘管不如我的ufunc快)。嘗試一半矢量化u函數可能無法爲您提供任何速度。


你可以有也定義了原有的功能採取vw作爲參數,並使用vectorizeexcluded參數來傳遞它們原樣。我不認爲它具有性能優勢。

In [521]: def u2(a,v,w): 
    ...:  return np.cos(a)*v + np.sin(a)*w 
    ...: 
In [522]: u2vec=np.vectorize(u2, otypes=[object],excluded=[1,2]) 
In [523]: u2vec(a,v,w) 
Out[523]: 
array([array([ 1., 0., 0.]), array([ 0.5  , 0.8660254, 0.  ]), 
     array([-0.5  , 0.8660254, 0.  ]), 
     array([ -1.00000000e+00, 1.22464680e-16, 0.00000000e+00])], dtype=object)