2017-08-01 199 views
0

鑑於兩個陣列一個(形狀= a, b, c, d, e)和b(形狀= a, x, b),我想的bx包括尺寸成一個 ,這樣一個新的陣列c導致形狀= a, x, b, c, d, e。的b值應均勻分佈:numpy的:從一個陣列整合尺寸到另一個

  • c.sum(1) == a
  • b.sum(1) == a.sum(axis=(2, 3, 4)) == c.sum(axis=(1, 3, 4, 5)

是否有這樣做,這是一個幾行numpy的或者是有必要在所有迭代的任何聰明的辦法手動值b[x]

我目前的解決方案:

for a, x, b in zip(*_b_.nonzero()): 
    tot = _a_[a, b].sum() 
    for c, d, e in zip(*_a_[a, b].nonzero()): 
     val = _b_[a, a, b] 
     frac = _a_[a, b, c, d, e]/tot 
     _c_[a, x, b, c, d, e] = val * frac 
+0

'c = a [:,None,...] + b [:,:,:None,None,None]'應該形成一個正確維度的新矩陣。縮放'a'或'b'可能會滿足您的總和條件。我還沒有研究他們足以說肯定。 – hpaulj

+0

我用一些示例代碼更新了描述。我不認爲添加_a_和_b_會起作用... – orange

+2

請大家幫忙,不要在維和變量中使用相同的變量名。它使你的代碼和描述非常難以閱讀。 –

回答

0

這是一種方式,三線要做到這一點,但首先一些關於我的做法的話:

  • 爲了更好的符號,我將使用大寫字母矩陣和索引的小寫字母。所以我們有A[a,b,c,d,e]B[a,x,b]作爲輸入。
  • 根據您的代碼,C對於所有x都是一樣的,所以我們並不真的需要該軸進行計算(如果需要,您可以將其添加爲新維度並在之後複製條目)。
  • B[a,a,b]可以沿着前兩個軸取對角線。
  • tot超過指數c,d,e的總和,我們可以在這個存儲在預先計算陣列Tot[a,b]
  • 能在最後一步使用numpy.einsum,我會先採取逆Tot = 1/Tot

這裏是完整的代碼:

import numpy 

# generate some example input 
a = 2 
b = 3 
c = 4 
d = 5 
e = 6 
x = 7 

A = numpy.arange(a*b*c*d*e).reshape((a,b,c,d,e)) 
B = numpy.arange(a*x*b).reshape((a,x,b)) 
C = numpy.zeros((a,x,b,c,d,e)) 

# solution by orange 
for a, x, b in zip(*B.nonzero()): 
    tot = A[a, b].sum() 
    for c, d, e in zip(*A[a, b].nonzero()): 
     val = B[a, a, b] 
     frac = A[a, b, c, d, e]/tot 
     C[a, x, b, c, d, e] = val * frac 

# new solution 
B2 = numpy.diagonal(B, axis1=0, axis2=1).transpose() # contract B_aab -> B2_ab 
Tot = 1/numpy.sum(A, (2,3,4)) # contract \sum_cde A_abcde -> 1/Tot_ab 
C2 = numpy.einsum('ab,abcde,ab->abcde',B2,A,Tot) 

# compare (should print x times True) 
for i in range(C.shape[1]): 
    C_ = C[:,i,:,:,:] 
    print(numpy.all(numpy.isclose(C_,C2))) 

編輯:如果numpy.einsum()是你太慢了,你可以實現在用Cython與for廁所的最後一步PS。