具有N个1x3阵列的np.dot 3x3

时间:2018-07-26 05:12:57

标签: python numpy matrix linear-algebra numpy-broadcasting

我有一个N个1x3数组的ndarray,我想用3x3矩阵执行点乘法。我似乎无法找出一种有效的方法来执行此操作,因为所有的multi_dot和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 个答案:

答案 0 :(得分:4)

尝试一下:

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

答案 1 :(得分:3)

首先,关于您的最后一个问题。 (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的第一个暗度保持明显):

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,)情况):

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]])

dotmatmul描述了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]])