代码之家  ›  专栏  ›  技术社区  ›  iceblueorbitz

np.dot 3x3,带n个1x3阵列

  •  4
  • iceblueorbitz  · 技术社区  · 6 年前

    我有一个n个1x3数组的ndarray,我想用3x3矩阵执行点乘法。我似乎找不到一个有效的方法来做这个,因为所有的multi-do t和tensordot等方法都是递归地求和或乘每个操作的结果。我只想应用一个点乘,就像应用一个标量一样。我可以通过for循环或列表理解来完成这项工作,但对于我的应用程序来说,这太慢了。

    N = np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ...])
    m = np.asarray([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
    

    我想执行这样的操作,但不需要任何Python循环:

    np.asarray([np.dot(m, a) for a in N])
    

    所以它只是返回 [m * N[0], m * N[1], m * N[2], ...]

    最有效的方法是什么?如果n只是一个1x3矩阵,它会输出与np.dot(m,n)相同的结果吗?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Hao Li    6 年前

    试试这个:

    import numpy as np
    N = np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3], [4, 5, 6]])
    m = np.asarray([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
    re0 = np.asarray([np.dot(m, a) for a in N])  # original
    re1 = np.dot(m, N.T).T  # efficient
    print("result0:\n{}".format(re0))
    print("result1:\n{}".format(re1))
    print("Is result0 == result1? {}".format(np.array_equal(re0, re1)))
    

    输出:

    result0:
    [[ 140  320  500]
     [ 320  770 1220]
     [ 500 1220 1940]
     [ 140  320  500]
     [ 320  770 1220]]
    result1:
    [[ 140  320  500]
     [ 320  770 1220]
     [ 500 1220 1940]
     [ 140  320  500]
     [ 320  770 1220]]
    Is result0 == result1? True
    

    时间成本:

    import timeit
    setup = '''
    import numpy as np
    N = np.random.random((1, 3))
    m = np.asarray([[10, 20, 30], [40, 50, 60], [70, 80, 790]])
    '''
    
    >> timeit.timeit("np.asarray([np.dot(m, a) for a in N])", setup=setup, number=100000)
    0.295798063278
    >> timeit.timeit("np.dot(m, N.T).T", setup=setup, number=100000)
    0.10135102272
    # N = np.random.random((10, 3))
    >> timeit.timeit("np.asarray([np.dot(m, a) for a in N])", setup=setup, number=100000)
    1.7417007659969386
    >> timeit.timeit("np.dot(m, N.T).T", setup=setup, number=100000)
    0.1587108800013084
    # N = np.random.random((100, 3))
    >> timeit.timeit("np.asarray([np.dot(m, a) for a in N])", setup=setup, number=100000)
    11.6454949379
    >> timeit.timeit("np.dot(m, N.T).T", setup=setup, number=100000)
    0.180465936661
    
        2
  •  3
  •   hpaulj    6 年前

    首先,关于你最后一个问题。A(3,)之间有区别 N 和(1,3):

    In [171]: np.dot(m,[1,2,3])
    Out[171]: array([140, 320, 500])       # (3,) result
    
    In [172]: np.dot(m,[[1,2,3]])
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-172-e8006b318a32> in <module>()
    ----> 1 np.dot(m,[[1,2,3]])
    
    ValueError: shapes (3,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)
    

    迭代版本产生(1,3)结果:

    In [174]: np.array([np.dot(m,a) for a in [[1,2,3]]])
    Out[174]: array([[140, 320, 500]])
    

    制作 n (4,3)数组(这有助于保持n的第一个dim不同):

    In [176]: N = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10,11,12]])
    In [177]: N.shape
    Out[177]: (4, 3)
    In [178]: np.array([np.dot(m,a) for a in N])
    Out[178]: 
    array([[ 140,  320,  500],
           [ 320,  770, 1220],
           [ 500, 1220, 1940],
           [ 680, 1670, 2660]])
    

    结果是(4,3)。

    简单的 dot 不起作用(与(1,3)情况相同):

    In [179]: np.dot(m,N)
    ...
    ValueError: shapes (3,3) and (4,3) not aligned: 3 (dim 1) != 4 (dim 0)
    In [180]: np.dot(m,N.T)     # (3,3) dot with (3,4) -> (3,4)
    Out[180]: 
    array([[ 140,  320,  500,  680],
           [ 320,  770, 1220, 1670],
           [ 500, 1220, 1940, 2660]])
    

    所以这需要另一个转置来匹配迭代结果。

    明确的指数 einsum 还可以处理这些转置:

    In [181]: np.einsum('ij,kj->ki',m,N)
    Out[181]: 
    array([[ 140,  320,  500],
           [ 320,  770, 1220],
           [ 500, 1220, 1940],
           [ 680, 1670, 2660]])
    

    也适用于(1,3)案例(但不适用于(3,3)案例):

    In [182]: np.einsum('ij,kj->ki',m,[[1,2,3]])
    Out[182]: array([[140, 320, 500]])
    

    matmul , @ 也可用于计算重复点-如果输入为3D(或可向其广播):

    In [184]: (m@N[:,:,None]).shape
    Out[184]: (4, 3, 1)
    In [185]: (m@N[:,:,None])[:,:,0]     # to squeeze out that last dimension
    Out[185]: 
    array([[ 140,  320,  500],
           [ 320,  770, 1220],
           [ 500, 1220, 1940],
           [ 680, 1670, 2660]])
    

    马特穆尔 描述1、2和3D输入的情况。要想了解正在发生的事情,可能需要一些时间和实验。基本规则是A的最后一个,B的第二个到最后一个。

    你的 n 实际上是(n,3), n (3,) 数组。以下是4(1,3)个数组的外观:

    In [186]: N1 = N[:,None,:]
    In [187]: N1.shape
    Out[187]: (4, 1, 3)
    In [188]: N1
    Out[188]: 
    array([[[ 1,  2,  3]],
    
           [[ 4,  5,  6]],
    
           [[ 7,  8,  9]],
    
           [[10, 11, 12]]])
    

    和前面的点一样(4,1,3)点(3,3).t->(4,1,3)->(4,3)

    In [190]: N1.dot(m.T).squeeze()
    Out[190]: 
    array([[ 140,  320,  500],
           [ 320,  770, 1220],
           [ 500, 1220, 1940],
           [ 680, 1670, 2660]])
    

    其中n个:

    In [191]: np.array([np.dot(a,m.T).squeeze() for a in N1])
    Out[191]: 
    array([[ 140,  320,  500],
           [ 320,  770, 1220],
           [ 500, 1220, 1940],
           [ 680, 1670, 2660]])