Python 一个涉及到填充物放置位置的广播问题 介绍

Python 一个涉及到填充物放置位置的广播问题 介绍,python,python-3.x,numpy,vectorization,array-broadcasting,Python,Python 3.x,Numpy,Vectorization,Array Broadcasting,我有一个可向量化的函数func,我使用np.frompyfunc对其进行向量化。我不想对循环使用嵌套的,我只想调用它一次,因此我需要用np.newaxis填充输入 我的目标是摆脱两个嵌套的for循环,改用numpy.array广播功能 这里是MWEfor循环(我想去掉for循环,而是在调用myfunc时填充变量c_0,c_1,rn_1,rn_2,以及因子) 利益问题的解决 上述显式for循环是正确的,并包含在质量保证的块代码中 我目前的努力 单片代码块 问题 通过上面的代码,我得到了结果数组的正

我有一个可向量化的函数
func
,我使用
np.frompyfunc
对其进行向量化。我不想对循环使用嵌套的
,我只想调用它一次,因此我需要用
np.newaxis
填充输入

我的目标是摆脱两个嵌套的
for
循环,改用
numpy.array
广播功能

这里是MWE
for
循环(我想去掉for循环,而是在调用
myfunc
时填充变量
c_0
c_1
rn_1
rn_2
,以及
因子

利益问题的解决 上述显式for循环是正确的,并包含在质量保证的块代码中

我目前的努力 单片代码块 问题
  • 通过上面的代码,我得到了结果数组的正确形状(
    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循环更快的解决方案

    #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
    
    出于理解目的,因为在旧的循环解决方案中,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),(1,3,2

    最后,我再次转置,将广播的频道放在前面,这样我们就有了一个(3,2,5,3,3)形状的张量,其中前两个频道是3,2嵌套循环的广播版本,5,3,3是现在需要在轴=4而不是轴=2上np.prod的矩阵

    在此之后,只需使用np.add.reduce对0,1轴求和即可将结果转换为5,3矩阵。最终结果应与循环结果完全相同

    我有点冒昧地修改了第一部分,因为这对我的眼睛来说更舒服,但是可以忽略这一部分

    PS.检查您提到的示例是否可以无缝工作

    b_00 = np.array([2.2, 1.1, 4.4, 5.1, 6.2])
    

    我喜欢在可能的情况下使用不同的尺寸。这样可以更容易地检测缺陷。例如,arr=np.arange(24)。重塑(2,3,4)
    @hpaulj这是一个很好的决定。我整晚都在看这个问题。当我把b_00长度定为4时,它出现了一个错误。我会跟进这个线索。谢谢。对不起,我读了你的帖子,我看不到这个问题。错误是什么,你想做什么?func是什么?你能过那个吗?@Dorian都在那里,func是在机器人上定义的汤姆
        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
    
    #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)
    
    b_00 = np.array([2.2, 1.1, 4.4, 5.1, 6.2])