2017-02-04 35 views
0

我有一個對象,我已經將大量的for-loop方法轉換爲一系列向量化的numpy數組(大約快50倍)。現在我試圖添加一個新方法,我需要處理一個numpy矩陣,然後根據矩陣內的數組索引「移動」子數組內容(即插入值)。我知道我可以用for循環來完成這個任務,但是我試圖通過使用矢量數學來實現加速增益來避免這種情況。Numpy:矩陣數組移位/索引插入

我想知道是否有實現以下一個快速和有效的方式:

[[ 0.2 0.4 0.6] 
[ 2. 4. 6. ] 
[ 20. 40. 60. ]] 

我想排在Z偏移添加:在

import numpy as np 

period = [1, 2, 3] 

x = [1, 10, 100] 
y = [.2, .4, .6] 

z = np.outer(x,y) 

print(z) 

結果基於週期的零的數量作爲z中的行索引,基本上如下:

[[ 0.0 0.2 0.4 0.6 ] 
[ 0.0 0.0 2.0 4.0 6.0 ] 
[ 0.0 0.0 0.0 20.0 40.0 60.0 ]] 

最終,我會是廁所國王在垂直/列軸(軸= 1)上求和。我需要一個最終的陣列類似如下:

[ 0.0 0.2 2.4 24.6 46.0 60.0] 
+0

小心時間測試。非迭代答案會創建一個數組,其大小是原始數據的兩倍。迭代地,您可以在沒有這些的情況下對偏移行進行求和。 – hpaulj

+0

「外部」是問題的重要組成部分,還是創建'z'的便捷方式?這真的是某種內在產品或加權移動總和? – hpaulj

回答

1

您也可以先計算指標,並分配一次:

a = np.array(
    [[0.2 , 0.4 , 0.6], 
    [2., 4., 6. ], 
    [20., 40., 60. ]]) 

s0, s1 = a.shape 
rows = np.repeat(np.arange(s0), s1).reshape(a.shape) 
cols = (np.add.outer(np.arange(0, s0), np.arange(s1)) + 1) 
res = np.zeros((s0, s0 + s1)) 
res[rows, cols] = a 
np.sum(res,axis=0) 

>>> np.sum(res,axis=0) 
array([ 0. , 0.2, 2.4, 24.6, 46. , 60. ]) 
+0

這對我來說很好,只需一次調整即可。對我來說真正的「a」不是方矩陣,所以我必須將'rows = np.repeat(np.arange(s0),s0).reshape(a.shape)'改爲'rows = np.repeat (np.arange(s0),s1).reshape(a.shape)'。 – m3m5rr

+0

太好了。感謝您的調整。相應地爲我們的下一個求助者更新我的回答。 –

1

循環在第一層面的工作:

a = np.array(
    [[0.2 , 0.4 , 0.6], 
    [2., 4., 6. ], 
    [20., 40., 60. ]]) 

​ 
s0, s1 = a.shape 
res = np.zeros((s0, s0 + s1)) 
for i in range(s1): 
    res[i, i + 1: i + s0 + 1] = a[i] 

>>> np.sum(res,axis=0) 
array([ 0. , 0.2, 2.4, 24.6, 46. , 60. ]) 
2
[[ 0.0 0.2 0.4 0.6 ] 
[ 0.0 0.0 2.0 4.0 6.0 ] 
[ 0.0 0.0 0.0 20.0 40.0 60.0 ]] 

是一個衣衫襤褸的名單。我們可以用viectorized陣列魔法來構建它,至少不是用普通的東西。

爲了解決這個問題,我們需要或者變平或墊這種結構

[[ 0.0 0.2 0.4 0.6 0.0 0.0] 
[ 0.0 0.0 2.0 4.0 6.0 0.0 ] 
[ 0.0 0.0 0.0 20.0 40.0 60.0 ]] 

[ 0.0 0.2 0.4 0.6 0.0 0.0 2.0 4.0 6.0 0.0 0.0 0.0 20.0 40.0 60.0 ] 

sum.reduceat讓我們平坦的陣列的總和塊,但你想跳過總和。我想我可以探索平移轉置。

我的第一個想法是,填充陣列看起來像是一個對角化陣列,[.2,2,20]放置在對角線上,在下一個偏移處放置[.4,4,40],等等。 。我知道sparse可以從一個矩陣和一組偏移量構建矩陣,但我不認爲在numpy中有這樣的函數。他們一次只能使用一個膠印。

但它也看起來像stride_tricks可以產生的偏移類型。

讓我們探索:

In [458]: as_strided =np.lib.index_tricks.as_strided 

In [459]: Z=np.pad(z,[[0,0],[3,3]],mode='constant') 
In [460]: Z 
Out[460]: 
array([[ 0. , 0. , 0. , 0.2, 0.4, 0.6, 0. , 0. , 0. ], 
     [ 0. , 0. , 0. , 2. , 4. , 6. , 0. , 0. , 0. ], 
     [ 0. , 0. , 0. , 20. , 40. , 60. , 0. , 0. , 0. ]]) 

In [461]: Z.strides 
Out[461]: (72, 8)  # prod an offset with (72+8, 8) 
In [462]: as_strided(Z,shape=(3,6),strides=(80,8)) 
Out[462]: 
array([[ 0. , 0. , 0. , 0.2, 0.4, 0.6], 
     [ 0. , 0. , 2. , 4. , 6. , 0. ], 
     [ 0. , 20. , 40. , 60. , 0. , 0. ]]) 

這就是那種我們想要轉移的,但方向是錯誤的;所以讓翻蓋Z

In [463]: Z1=Z[::-1,:].copy() 
In [464]: as_strided(Z1,shape=(3,6),strides=(80,8)) 
Out[464]: 
array([[ 0. , 0. , 0. , 20. , 40. , 60. ], 
     [ 0. , 0. , 2. , 4. , 6. , 0. ], 
     [ 0. , 0.2, 0.4, 0.6, 0. , 0. ]]) 
In [465]: as_strided(Z1,shape=(3,6),strides=(80,8)).sum(0) 
Out[465]: array([ 0. , 0.2, 2.4, 24.6, 46. , 60. ]) 

泛化可以留給讀者。

是否有任何速度優勢未知。可能不是這個小案子,也許是一個非常大的案件。


此清理填充和跨越位

In [497]: Z=np.pad(z,[[0,0],[1,4]],mode='constant') 
In [498]: Z.strides 
Out[498]: (64, 8) 
In [499]: as_strided(Z,shape=(3,6),strides=(64-8,8)) 
Out[499]: 
array([[ 0. , 0.2, 0.4, 0.6, 0. , 0. ], 
     [ 0. , 0. , 2. , 4. , 6. , 0. ], 
     [ 0. , 0. , 0. , 20. , 40. , 60. ]]) 

這忽略z是如何構造的。如果外部產品是問題的核心,我可能會嘗試在1d y上大步前進,並使用x執行加權求和。

In [553]: x=np.array([1,10,100]); y=np.array([.2,.4,.6]) 
In [554]: z=np.concatenate(([0,0],y[::-1],[0,0,0])) 
In [555]: z 
Out[555]: array([ 0. , 0. , 0.6, 0.4, 0.2, 0. , 0. , 0. ]) 
In [556]: Z=as_strided(z,shape=(3,6), strides=(8,8)) 
In [557]: Z 
Out[557]: 
array([[ 0. , 0. , 0.6, 0.4, 0.2, 0. ], 
     [ 0. , 0.6, 0.4, 0.2, 0. , 0. ], 
     [ 0.6, 0.4, 0.2, 0. , 0. , 0. ]]) 
In [558]: np.dot(x,Z) 
Out[558]: array([ 60. , 46. , 24.6, 2.4, 0.2, 0. ]) 

在這種結構中Zz的圖,所以是比上的Z小。但我確信dot將它發送到編譯代碼時會生成副本。 np.einsum('i,ij',x,Z)可能會避免這種情況,在不擴展它的情況下對其進行編譯迭代。處理非常大的數組時,這可能會有所不同。

結果相反,但這很容易修復。我甚至可以在施工期間修復它。