涉及将填充放在哪里的广播问题
A broadcasting issue involving where to put the padding
简介
我有一个可向量化的函数 func
,我使用 np.frompyfunc
对其进行向量化。我不想使用嵌套的 for
循环,而是只想调用它一次,因此我需要用 np.newaxis
填充输入。
我的目标是摆脱两个嵌套的 for
循环并改用 numpy.array
广播功能。
这是 MWE for
循环(我想摆脱 for 循环,而是填充变量 c_0
、c_1
、rn_1
、rn_2
, 和 factor
当调用 myfunc
.
感兴趣问题的MWE
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) )
上述显式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))
整体代码块
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
问题
- 通过上面的代码,我得到了结果数组的正确形状(
results2
是我尝试广播,而 results
是给出正确答案的慢循环),但是它有错误的值。
- 正如
@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
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)
仅供理解,因为在旧的循环解决方案中,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 是您现在需要 np.prod over axis = 4 而不是 axis = 2.
的矩阵
Post,使用 np.add.reduce 对 0,1 轴求和将结果带到 5,3 矩阵是一件简单的事情。最后的结果应该和循环的结果完全一样。
我也有点冒昧地修改了第一部分,因为这让我看起来更舒服,但请忽略那部分。
PS。检查它是否适用于您提到的示例
b_00 = np.array([2.2, 1.1, 4.4, 5.1, 6.2])
简介
我有一个可向量化的函数 func
,我使用 np.frompyfunc
对其进行向量化。我不想使用嵌套的 for
循环,而是只想调用它一次,因此我需要用 np.newaxis
填充输入。
我的目标是摆脱两个嵌套的 for
循环并改用 numpy.array
广播功能。
这是 MWE for
循环(我想摆脱 for 循环,而是填充变量 c_0
、c_1
、rn_1
、rn_2
, 和 factor
当调用 myfunc
.
感兴趣问题的MWE
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) )
上述显式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))
整体代码块
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
问题
- 通过上面的代码,我得到了结果数组的正确形状(
results2
是我尝试广播,而results
是给出正确答案的慢循环),但是它有错误的值。 - 正如
@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
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)
仅供理解,因为在旧的循环解决方案中,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 是您现在需要 np.prod over axis = 4 而不是 axis = 2.
的矩阵Post,使用 np.add.reduce 对 0,1 轴求和将结果带到 5,3 矩阵是一件简单的事情。最后的结果应该和循环的结果完全一样。
我也有点冒昧地修改了第一部分,因为这让我看起来更舒服,但请忽略那部分。
PS。检查它是否适用于您提到的示例
b_00 = np.array([2.2, 1.1, 4.4, 5.1, 6.2])