【问题标题】:How to calculate the covariance matrix of 3d numpy arrays?如何计算 3d numpy 数组的协方差矩阵?
【发布时间】:2020-02-25 21:54:15
【问题描述】:

我有一个矩阵A,形状为(N, D, 4)。首先我计算A转置,A_t。我想计算A_t 乘以A 的乘积。我希望得到的矩阵形状为(D, D),并且矩阵的乘积就像最后一个由 4 个分量组成的向量是一个数字。 (两个向量的点积是一个数。)

import numpy as np

N = 15
D = 98
A = np.random.random((N, D, 4))
A_t = np.zeros((D, N, 4))
for i in range(N):
    A_t[:, i] = A[i]

S = np.zeros((D, D))

for i in range(D):
    row = A_t[i]
    for j in range(D):
        col = A[:, j, :]
        val = 0
        for n in range(N):
            val += np.matmul(row[n], col[n])
        S[i][j] = val

print(A.shape)
print(A_t.shape)
print(S.shape)

【问题讨论】:

  • 代码没有显示你想从矩阵中得到什么。您可以只使用A_t = A.T 进行转置。根本不需要循环。如果需要,显示带有小数组和for 循环的具体示例。预期结果在这里很重要。
  • 代码可能不会显示,但会说出来。我想得到 A.t * A 的乘积,考虑到 4 个分量的向量,就好像它们只是数字一样,所以我想要的结果矩阵是 DxD。这种矩阵的值将是由 4 个分量的两个向量的乘积得出的数字。
  • 请在代码中显示这意味着什么。您当前的代码不构成 MCVE 或真正对讨论有任何帮助。如果必须的话,写一个for 循环。可以通过多种方式定义两个向量的乘积。虽然我假设您的意思是点积,但目前尚不清楚您打算如何组合这些元素以达到这一点。
  • 好的,让我试着更清楚一些,如果我没有正确解释自己,我很抱歉。首先,我不能做 A.T 来找到 A 的转置,因为当我这样做时,尺寸不是我希望这个 3D 矩阵被转置的方式。我已经计算了我想要使用循环的矩阵,但我想知道是否有更快更好的方法来做到这一点。当尺寸变大时,这种方法太慢了。
  • 感谢您发布循环。现在,您要 100% 清楚地知道您要做什么。我会尽快发布解决方案

标签: python numpy matrix multiplication


【解决方案1】:

让我们来看看您正在尝试的操作,看看我们可以做些什么来简化它们。对于初学者,你可以写

A_t = np.swapaxes(A, 0, 1)

这相当于

A_t = np.transpose(A, [0, 1, 2])

A_t = A.transpose([0, 1, 2])

碰巧的是,您当前的应用程序都不需要。要了解原因,让我们使用一个简化的示例:

np.random.seed(42)
N = 4
D = 3
K = 2
A = np.random.randint(0, 10, (N, D, K))

在你的外循环中,你有row = A_t[i]。但根据您的转置定义,这与row = A[:, i, :] 相同,让您的生活更轻松,转置是多余的。

内部循环对一些点积求和:

val = 0
for n in range(N):
    val += np.matmul(row[n], col[n])

如果你记得点积的定义,你会发现你正在做的等价于

np.sum(np.sum(row * col, axis=1), axis=0)

内部和是循环中的和积,而外部和是val 的计算。分别对两个维度求和与一次对整个缓冲区求和是一样的,所以我们可以立即用 just 替换内部循环

for i in range(D):
    for j in range(D):
        S[i][j] = np.sum(A[:, i, :] * A[:, j, :])

您可以使用np.dotnp.tensordotnp.einsum 或简单的广播来简化此操作。前两个是不必要的复杂,因为您实际上是同时在两个维度上相乘。 np.einsum 总体上提供了最直接的解决方案,但它对您的代码的翻译不太直接。

解决方案 1:广播

让我们从双循环的直接广播版本开始,然后再转向更惯用的解决方案:

S = (A[:, None, ...] * A[:, :, None, ...]).sum(axis=(0, -1))

S = np.sum(A[:, None, ...] * A[:, :, None, ...], axis=(0, -1))

这会分别创建A 形状的(N, 1, D, K)(N, D, 1, K) 的视图。乘法在每种情况下都将复制的 D 轴广播到 for 循环所做的事情,因此 NK 轴上的最终总和与 S[i][j] = np.sum(A[:, i, :] * A[:, j, :]) 之前的行完全相同。

解决方案 2:np.einsum

此解决方案可让您将 sum-product 直接应用于您想要的任何轴:

S = np.einsum('ijk,ihk->jh', A, A)

请注意,您必须为第二个矩阵的第二个轴(jh)使用不同的字母,以表明您不会在该轴上求和。 S 是对称的,但如果不是,您可以通过在结果中转置为 ->hj 来转置它。

【讨论】:

  • 非常感谢,我不知道是否应该一起提出不同的问题,但知道矩阵 S 是对称、实数和半正数的,获得从大到小排序的特征值和特征向量的最佳方法是什么特征值的值??
  • @DanielCasasampera。这是微不足道的谷歌。我不建议在没有先做一些基础研究的情况下发布这样的问题。
  • 如果它适合你,别忘了选择答案。
  • 对不起,我没在家测试它,它符合我的要求,非常感谢您的宝贵时间!
  • 由于某种原因在我的矩阵中它给了我负值,有什么原因吗?
猜你喜欢
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 2020-04-13
  • 1970-01-01
  • 1970-01-01
  • 2017-08-27
  • 1970-01-01
相关资源
最近更新 更多