另一个矩阵的每一列进行Numpy矩阵减法。

4
我知道在numpy中,如果你有一个矩阵A,并且我通过执行A - v来减去一个向量v,则会进行广播,使v成为与A相同的维度,并执行逐元素的减法。
我想知道如果我需要对相同的A但不同的v执行上述操作多次,是否能够使用向量化进行操作。
一个朴素的实现方式如下:
def foo(A, V):
   """
   @params: A, V
   A - an n by m matrix
   V - a list of n by 1 column vectors
   @returns: result
   """

   result = []
   for v in V:
      result.append(A-v)
   return result

我知道可以将V作为一个n行p列的矩阵传入,其中每一列代表一个向量v。然而,我无法想到任何用线性代数描述上述操作的方式。

例如,如果

 A = [[1 2 3], 
      [1 2 3]]

 V = [[1 4], 
      [1 4]]  

输出应该是两个矩阵。
[
  [[0 1 2], 
  [0,1,2]], 

  [[-3 -2 -1], 
  [-3 -2, -1]]
]

我不确定如何完全向量化这个问题,但是根据您需要多少性能提升,列表推导式可能会让您达到所需的程度:result = [A - v for v in V] - BLimitless
所以你想要 [A-v[0], A-v[0]-v[1],...]A-np.cumsum(v, axis=1) 可能可以做到。 - hpaulj
如果V包含一堆水平堆叠的列向量,那么我想要的是[A-V[:0],A-V[:1],...]。请注意,广播应针对每个列向量进行。我的最终输出应该是一个矩阵列表或集合。 - coderhk
“A”形状是什么?“v”和“result”的形状需要明确吗?我们可能需要一个小的实例来说明。 - hpaulj
“V - 一个由n个1列向量组成的列表” - 你能给出一个关于“V”的最小示例吗?如果“A = np.zeros((4,3)) + [1,2,3]”,那么“V”可以是...“[np.ones(4),np.ones(4)*2,np.ones(4)*3]”吗? - wwii
显示剩余3条评论
2个回答

2
a = np.zeros((4,3)) + [1,2,3]
V = [np.ones(3),np.ones(3)*2,np.ones(3)*3]

你想要的是:
>>> a-V[0]
array([[0., 1., 2.],
       [0., 1., 2.],
       [0., 1., 2.],
       [0., 1., 2.]])
>>> a-V[1] 
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.],
       [-1.,  0.,  1.],
       [-1.,  0.,  1.]])
>>> a-V[2]
array([[-2., -1.,  0.],
       [-2., -1.,  0.],
       [-2., -1.,  0.],
       [-2., -1.,  0.]])

将向量堆叠在V中,然后添加一个维度以进行减法运算。

>>> W = np.vstack(V)
>>> a - W[:,None,:]
array([[[ 0.,  1.,  2.],
        [ 0.,  1.,  2.],
        [ 0.,  1.,  2.],
        [ 0.,  1.,  2.]],

       [[-1.,  0.,  1.],
        [-1.,  0.,  1.],
        [-1.,  0.,  1.],
        [-1.,  0.,  1.]],

       [[-2., -1.,  0.],
        [-2., -1.,  0.],
        [-2., -1.,  0.],
        [-2., -1.,  0.]]])

再试一次,调整 (n,m)(4,3)

a = np.zeros((4,3)) + [1,2,3]       # (n.m) = (4,3)
V = [np.ones(4),np.ones(4)*2,np.ones(4)*3]  # three (nx1) vectors
W = np.vstack(V)

>>> a.shape
(4, 3)
>>> W.shape
(3, 4)
>>> Z = a - W[...,None] 
>>> Z[0]
array([[0., 1., 2.],
       [0., 1., 2.],
       [0., 1., 2.],
       [0., 1., 2.]])
>>> Z[1]
array([[-1.,  0.,  1.],
       [-1.,  0.,  1.],
       [-1.,  0.,  1.],
       [-1.,  0.,  1.]])
>>> Z[2]
array([[-2., -1.,  0.],
       [-2., -1.,  0.],
       [-2., -1.,  0.],
       [-2., -1.,  0.]])
>>> Z.shape             
(3, 4, 3)

我可能把 mn 搞混了。 - wwii
你的代码 V = [np.ones(3),np.ones(3)*2,np.ones(3)*3] 是一个行向量列表,我需要一个列向量列表。例如,如果 A = [[1 2 3], [1 2 3]] v = [[1 4], [1 4]] 输出应该是两个矩阵 [ [[0 1 2], [-3 -2 -1]], [[0,1,2],[-3 -2, -1]]] - coderhk
@coderhk 请看一下修改后的内容是否符合您的要求。 - wwii
非常感谢。看起来如果我对我的 V 向量进行转置,然后应用您的算法,它将产生所需的结果。尽管如此,这得益于您的帮助! :) - coderhk

1
In [104]:  A = [[1, 2, 3],
     ...:       [1, 2, 3]]
     ...: 
     ...:  V = [[1, 4],
     ...:       [1, 4]]
In [105]: A=np.array(A);V=np.array(V)
In [106]: A              # (n,m)
Out[106]: 
array([[1, 2, 3],
       [1, 2, 3]])
In [107]: V               # (n,p)
Out[107]: 
array([[1, 4],
       [1, 4]])

迭代减法;[0]用于创建数组(2,1)(与(2,3)A进行广播)。
In [108]: [A-V[:,[0]], A-V[:,[1]]]
Out[108]: 
[array([[0, 1, 2],
        [0, 1, 2]]),
 array([[-3, -2, -1],
        [-3, -2, -1]])]

让我们尝试使用整个数组广播:
In [109]: A[None,:,:]-V[:,:,None]
Out[109]: 
array([[[ 0,  1,  2],
        [-3, -2, -1]],

       [[ 0,  1,  2],
        [-3, -2, -1]]])

糟糕,混合错误;请再试一次,将 V 更改为 (p,n)。如果 V 不是 (2,2),那会更清晰明了:

In [110]: A[None,:,:]-V.T[:,:,None]
Out[110]: 
array([[[ 0,  1,  2],
        [ 0,  1,  2]],

       [[-3, -2, -1],
        [-3, -2, -1]]])

使用 (2,4) V
In [112]: V1=np.hstack((V,V))
In [113]: V1.shape
Out[113]: (2, 4)
In [114]: (A[None,:,:]-V1.T[:,:,None]).shape
Out[114]: (4, 2, 3)
In [115]: (A-V1.T[:,:,None]).shape
Out[115]: (4, 2, 3)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接