直观的使用3D numpy数组的方法

时间:2016-09-26 18:11:13

标签: python numpy matrix-multiplication

让我们举一个简单的例子,我有数据数组

A = np.asarray([[1,3], [2,4]])

此数据将在简单转换后转换为另一种形式:

Q = np.asarray([[-0.5,1], [1,0.5]])
B = np.dot(Q,np.dot(A,Q.T))
print B

现在假设我有一组数据,它采用2d数组的形式,用于几个时间步。为简单起见,再次假设此数据仅复制A 3个时间步。我们可以将此数据表示为维度为(2,2,N)的3d数组,在这种情况下为N =3。然后,第三个维度表示数据的时间索引。现在要求有一种简单的方法来转换数据如上所述,但是对于每个时间步骤,通过3d数组的直观乘法,我只能做出以下不直观的工作:< / p>

# Create the 3d data array
AA = np.tile(A,(3,1,1)) # shape (3,2,2)
BB = np.dot(Q,np.dot(AA,Q.T))

print np.all( BB[:,0,:] == B ) # Returns true 

因此,使用这种方法,我不必重新设定Q数组以使其正常工作,但现在第二个维度将作为&#34; time&#34;索引有点反直觉,因为在AA它是表示时间的第一个维度...理想情况下我想要一个AABB都有时间索引的解决方案在第三维度!

编辑:

从文档dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])开始,我想知道我想要实现的是不可能的?这似乎很奇怪,因为这应该是一个人们可能想要的相对普遍的事情......

2 个答案:

答案 0 :(得分:1)

In [91]: A=np.array([[1,3],[2,4]])
In [92]: Q=np.array([[-.5,1],[1,.5]])
In [93]: B=np.dot(Q,np.dot(A,Q.T))
In [94]: B
Out[94]: 
array([[ 1.75,  2.75],
       [ 4.  ,  4.5 ]])

einsum相同的计算:

In [95]: np.einsum('ij,jk,kl',Q,A,Q)
Out[95]: 
array([[ 1.75,  2.75],
       [ 4.  ,  4.5 ]])

如果我在新的第一维上制作了A的多个副本:

In [96]: AA = np.array([A,A,A])
In [97]: AA.shape
Out[97]: (3, 2, 2)
...
In [99]: BB=np.einsum('ij,pjk,kl->pil',Q,AA,Q)
In [100]: BB
Out[100]: 
array([[[ 1.75,  2.75],
        [ 4.  ,  4.5 ]],

       [[ 1.75,  2.75],
        [ 4.  ,  4.5 ]],

       [[ 1.75,  2.75],
        [ 4.  ,  4.5 ]]])

BB有一个(3,2,2)形状。

新的matmul(@运算符)让我做同样的事情

In [102]: Q@A@Q.T
Out[102]: 
array([[ 1.75,  2.75],
       [ 4.  ,  4.5 ]])
In [103]: Q@AA@Q.T
Out[103]: 
array([[[ 1.75,  2.75],
        [ 4.  ,  4.5 ]],

       [[ 1.75,  2.75],
        [ 4.  ,  4.5 ]],

       [[ 1.75,  2.75],
        [ 4.  ,  4.5 ]]])

使用einsum,使用最后一个维度同样容易:

In [104]: AA3=np.stack([A,A,A],-1)    # newish np.stack
In [105]: AA3.shape
Out[105]: (2, 2, 3)
In [106]: np.einsum('ij,jkp,kl->ilp',Q,AA3,Q)
Out[106]: 
array([[[ 1.75,  1.75,  1.75],
        [ 2.75,  2.75,  2.75]],

       [[ 4.  ,  4.  ,  4.  ],
        [ 4.5 ,  4.5 ,  4.5 ]]])
In [107]: _.shape
Out[107]: (2, 2, 3)

答案 1 :(得分:0)

我不确定我同意你对#34;直觉&#34;的定义 - 对我而言,通过数组的第一维表示时间索引似乎更自然。由于numpy数组默认为row-major,因此对于每个2x2子矩阵,维度的这种排序将为您提供更好的locality of reference,因为所有元素都位于相邻的内存地址中。

尽管如此,通过使用np.matmul并转置每个中间数组,可以使您的示例适用于(2, 2, N)数组:

CC = np.repeat(A[..., None], 3, -1)   # shape (2, 2, 3)
DD = np.matmul(Q.T, np.matmul(CC.T, Q)).T

print(DD.shape)
# (2, 2, 3)

print(repr(DD))
# array([[[ 1.75,  1.75,  1.75],
#         [ 2.75,  2.75,  2.75]],

#        [[ 4.  ,  4.  ,  4.  ],
#         [ 4.5 ,  4.5 ,  4.5 ]]])

在Python 3.5+中,您可以使用@ operator作为np.matmul的简写,使其更加紧凑:

DD = (Q.T @ (CC.T @ Q)).T