Geo*_*e K 5 python numpy vectorization python-3.x array-broadcasting
我有一个func可矢量化的函数,我使用np.frompyfunc. for我不想使用嵌套循环,而是只想调用它一次,因此我需要用np.newaxis's填充输入。
我的目标是摆脱两个嵌套for循环并改用numpy.array广播功能。
这是 MWEfor循环(我想摆脱 for 循环,而是填充变量c_0, c_1, rn_1, rn_2,并factor在调用myfunc.
for i, b_0 in enumerate(b_00):
for j, b_1 in enumerate(b_11):
factor = b_0 + b_1
rn = (b_0 * coord + b_1 * coord2) / factor
rn_1 = coord - rn
rn_2 = coord2 - rn
results = np.add( results,np.prod(myfunc(c_0[:,None,:], c_1[None,:,:], rn_1, rn_2, factor), axis=2) )
Run Code Online (Sandbox Code Playgroud)
上面的显式 for 循环是正确的,并且包含在块代码中以保证质量。
factor = b_00[:, None] + b_11[None, :]
rn = np.add( (b_00[:,None] * coord[None,:])[:, None, :], (b_11[:,None] * coord2[None,:])[None, :, :] ) / factor[:,:,None]
rn_1 = coord - rn
rn_2 = coord2 - rn
results2 = np.prod(myfunc(c_0[:,None,:, None, None], c_1[None,:,:, None, None], rn_1[:,:,:],rn_2[:,:, :], factor[None,:, :, None]), axis=2)
results2 = np.squeeze(results2)
results2 = np.sum(results2, axis=(2,3))
Run Code Online (Sandbox Code Playgroud)
import numpy as np
myfunc = np.frompyfunc(func,5,1)
################################################################
# Prep arrays needed for MWE
################################################################
results = np.zeros((5,3))
coord = np.array([1,1,2])
coord2 = np.array([3,3,3])
c_0 = np.array([[0,0,2],[0,2,0],[2,0,0],[1,1,0], [1,0,1]])
c_1 = np.array([[0,0,2],[0,2,0],[2,0,0]])
b_00 = np.array([2.2, 1.1, 4.4]) # np.array([2.2, 3.3, 40.4])
b_11 = np.array([1.2, 3.3]) # np.array([1.2, 5.3])
################################################################
# This is only for comparison. `results` is the correct answer
################################################################
for i, b_0 in enumerate(b_00):
for j, b_1 in enumerate(b_11):
factor = b_0 + b_1
rn = (b_0 * coord + b_1 * coord2) / factor
rn_1 = coord - rn
rn_2 = coord2 - rn
results = np.add( results,np.prod(myfunc(c_0[:,None,:], c_1[None,:,:], rn_1, rn_2, factor), axis=2) )
################################################################
# Prep for broadcasting (My attempt)
################################################################
factor = b_00[:, None] + b_11[None, :]
rn = np.add( (b_00[:,None] * coord[None,:])[:, None, :], (b_11[:,None] * coord2[None,:])[None, :, :] ) / factor[:,:,None]
rn_1 = coord - rn
rn_2 = coord2 - rn
# The following all get the same *wrong* answer
# results2 = np.prod(myfunc(c_0[:,None,:,None, None], c_1[None,:,:, None, None], rn_1[:, None, None],rn_2[:,None, None], factor[None,:, :]), axis=2)
# results2 = np.prod(myfunc(c_0[:,None,:, None, None, None], c_1[None,:,:, None, None, None], rn_1[None, None,:,:,:, None],rn_2[None, None,:,:, :, None], factor[None,:, :, None, None]), axis=2)
# results2 = np.prod(myfunc(c_0[:,None,:, None, None], c_1[None,:,:, None, None], rn_1[None, None,:,:,:],rn_2[None, None,:,:, :], factor[None,:, :, None]), axis=2)
# this should be the only line needing work!
results2 = np.prod(myfunc(c_0[:,None,:, None, None], c_1[None,:,:, None, None], rn_1[:,:,:],rn_2[:,:, :], factor[None,:, :, None]), axis=2)
results2 = np.squeeze(results2)
results2 = np.sum(results2, axis=(2,3))
assert np.allclose(results, results2)
# Vectorized function to be sent broadcasted arrays
def func(r0, r1, x, y, at):
val = 0.0
for i in range(r0+1):
for j in range(r1+1):
val += x + i*j + at * y
return val
Run Code Online (Sandbox Code Playgroud)
results2是我尝试广播,并且results是给出正确答案的缓慢 for 循环),但它有错误的值。@hpaulj指出的,如果我将 的尺寸更改b_00为长度 4(或任何更大的尺寸),我的解决方案甚至无法获得正确的形状。请确保它适用于当前b_00 = np.array([2.2, 1.1, 4.4])以及更一般的b_00 = np.array([2.2, 1.1, 4.4, 5.1, 6.2]). 我想要一个广播解决方案,但会接受一个比for loops.
这应该可以解决你的问题!
#Definitions
coord = np.array([1,1,2])
coord2 = np.array([3,3,3])
c_0 = np.array([[0,0,2],[0,2,0],[2,0,0],[1,1,0], [1,0,1]])
c_1 = np.array([[0,0,2],[0,2,0],[2,0,0]])
b_00 = np.array([2.2, 1.1, 4.4]) # np.array([2.2, 3.3, 40.4])
b_11 = np.array([1.2, 3.3]) # np.array([1.2, 5.3])
#Vectorized code for prep
b_0 = np.outer(b_00, coord)
b_1 = np.outer(b_11, coord2)
factor = (b_00+b_11.reshape(-1,1)).T[:,:,None]
rn = np.divide((b_0[:,None]+b_1), factor)
rn_1 = coord-rn
rn_2 = coord2-rn
#THIS IS YOUR INTERESTING PART
results = np.prod(myfunc(c_0[:,None,:,None,None], c_1[None,:,:,None,None], rn_1.transpose(2,0,1), rn_2.transpose(2,0,1), factor.transpose(2,0,1)).transpose(3,4,0,1,2), axis=4)
results = np.add.reduce(results, axis=(0,1))
results
Run Code Online (Sandbox Code Playgroud)
array([[6707.793061061082, 5277.838468285241, 5277.838468285241],
[5277.838468285241, 5992.8157646731615, 5277.838468285241],
[5277.838468285241, 5277.838468285241, 5992.8157646731615],
[7037.117957713655, 7513.7694886389345, 7513.7694886389345],
[7990.421019564216, 7037.117957713655, 7513.7694886389345]],
dtype=object)
Run Code Online (Sandbox Code Playgroud)
只是出于理解目的,由于在旧的循环解决方案中, myfunc 在 rn_1 和 rn_2 的第一个轴上运行,因此广播通道需要是最后 2 个而不是第一个。因此,我在 c_0 和 c_1 的末尾添加 2 个通道,然后将最后一个轴放在前面,使 (3,2,3) rn_1 变为 (3,3,2)。对于因子也是如此。因此,现在 myfunc 可以对突出显示广播频道的张量进行操作 - (5,1,3, 1,1 ), (1,3,3, 1,1 ), (3, 3,2 ), (3, 3, 2 ), (1, 3,2 )
最后,我再次转置,将广播频道放在前面,这样我们就有一个 ( 3,2 ,5,3,3) 形状的张量,其中前 2 个频道是 3,2 嵌套循环和 5 的广播版本, 3,3 是您现在需要在 axis = 4 而不是 axis = 2 上 np.prod 的矩阵。
之后,使用 np.add.reduce 在 0,1 轴上求和,将结果带到 5,3 矩阵,这是一个简单的问题。最终的结果应该和循环的结果完全相同。
我也冒昧地修改了第一部分,因为这对我的眼睛来说更舒服,但请随意忽略该部分。
附言。检查它是否可以无缝地用于您提到的示例
b_00 = np.array([2.2, 1.1, 4.4, 5.1, 6.2])
Run Code Online (Sandbox Code Playgroud)