【问题标题】:Building a covariance matrix in Python在 Python 中构建协方差矩阵
【发布时间】:2016-02-06 12:00:54
【问题描述】:

问题 我想从我的主管未发表的论文中实现一个算法,作为其中的一部分,我需要使用论文中给出的一些规则来构造一个协方差矩阵 C。我来自 Matlab,想借此机会最终学习 Python,因此我的问题是:如何以 Python(包括 numpy、scipy)中最有效(快速)的方式做到这一点?

子问题 1:

  • 选项 1:我使用 2 个 for 循环,遍历所有行和所有列。我认为这是最糟糕的事情。
  • 选项 2:使用列表推导,我构造了一个欧几里得对列表,然后遍历该列表。这就是我现在正在做的事情。

有没有更好的办法?

子问题 2

  • 选项 1:我遍历矩阵中的所有元素。
  • 选项 2:我只迭代下三角部分(没有对角线),然后添加转置(因为协方差矩阵是对称的),然后添加对角线。

我相当确信子问题 1 是显而易见的,但我不知道子问题 2。我可能还应该说我正在处理的矩阵可能是 2*10^4 x 2*10^ 4.

谢谢!

编辑 我不想给出实际的协方差矩阵,但由于人们想要一个例子,假设我们想要构建一个称为“布朗桥”的随机过程的协方差矩阵。它的结构由下式给出:

cov(Xs, Xt) = min{s,t} - st

假设 s,t ∈ {1,...,100}。您将如何构建它?

【问题讨论】:

  • @atomh33ls:我没有数据来估计协方差。我得到了一些关于如何构造协方差矩阵的规则。如果您熟悉高斯过程,例如布朗运动的协方差矩阵有条目 (i,j) 取值 min(i,j)。我有这样的规则是徒劳的。
  • 解释计算协方差矩阵的过程会非常有帮助(如果没有必要)。很可能根本不显式迭代,这将是最好/最快和最“pythonic”的方式。
  • @Leb - 这实际上是跳入科学 python 的完美问题类型,i.m.o.对于来自 Matlab 的人来说,直接跳入 numpy 等确实是您想要的。
  • 在许多方面,您应该将学习 numpy 和学习 Python 视为完全不同的事情,因为 numpy 有自己独特的一组最佳实践。获得良好性能的诀窍是尽可能少地使用 Python。这意味着通过使用巧妙的数组索引和向量化操作来避免低级 Python 构造,例如 for 循环和列表推导。通过这样做,您实际上是在 C 而不是 Python 中进行循环。

标签: python numpy scipy covariance gaussian


【解决方案1】:

首先,对于将​​来可能会遇到这个问题的其他人:如果您确实有数据并且想要估计协方差矩阵,正如一些人所指出的,请使用 np.cov 或类似的东西。

从模式构建数组

但是,您的问题是关于如何在给定一些预定义规则的情况下构建大型矩阵。为了消除 cmets 中的一些混淆:您的问题似乎不是关于估计协方差矩阵,而是关于指定一个。换句话说,你问的是如何在给定一些预定义规则的情况下构建一个大数组。

哪种方式最有效将取决于您具体做什么。在这种情况下,大多数性能技巧将涉及在您正在执行的计算中利用对称性。 (例如,一行是否相同?)

如果不确切知道自己在做什么,就很难说出具体的内容。因此,我将重点介绍如何做这类事情。 (注意:我刚刚注意到您的编辑。稍后我将包含一个布朗桥的示例......)

常量(或简单)行/列

最基本的情况是输出数组中的恒定行或列。使用切片语法创建数组并将值分配给列或行很容易:

import numpy as np

num_vars = 10**4
cov = np.zeros((num_vars, num_vars), dtype=float)

设置整个列/行:

# Third column will be all 9's
cov[:,2] = 9

# Second row will be all 1's (will overwrite the 9 in col3)
cov[1,:] = 1

您还可以将数组分配给列/行:

# 5th row will have random values
cov[4,:] = np.random.random(num_vars)

# 6th row will have a simple geometric sequence
cov[5,:] = np.arange(num_vars)**2

堆叠数组

在许多情况下,(但可能不是这个确切的情况)您会希望从现有数组构建您的输出。您可以为此使用vstack/hstack/column_stack/tile 和许多其他类似功能。

一个很好的例子是,如果我们为多项式的线性求逆设置一个矩阵:

import numpy as np

num = 10
x = np.random.random(num) # Observation locations

# "Green's functions" for a second-order polynomial
# at our observed locations
A = np.column_stack([x**i for i in range(3)])

但是,这将建立几个临时数组(在本例中为三个)。如果我们使用具有 10^6 个观测值的 10000 维多项式,上述方法将使用太多 RAM。因此,您可以改为遍历列:

ndim = 2
A = np.zeros((x.size, ndim + 1), dtype=float)
for j in range(ndim + 1):
    A[:,j] = x**j

在大多数情况下,不必担心临时数组。除非您使用相对较大的数组,否则基于 colum_stack 的示例是正确的方法。

最通用的方法

如果没有更多信息,我们就无法利用任何形式的对称性。最一般的方法是迭代。通常你会想避免这种方法,但有时它是不可避免的(特别是如果计算取决于以前的值)。

在速度方面这与嵌套 for 循环相同,但使用 np.ndindex 而不是多个 for 循环更容易(尤其是对于 >2D 数组):

import numpy as np

num_vars = 10**4
cov = np.zeros((num_vars, num_vars), dtype=float)
for i, j in np.ndindex(cov.shape):
    # Logic presumably in some function...
    cov[i, j] = calculate_value(i, j)

基于向量索引的计算

如果有很多情况,您可以向量化基于索引的计算。换句话说,直接对输出的索引数组进行操作。

假设我们有如下代码:

import numpy as np

cov = np.zeros((10, 10)), dtype=float)
for i, j in np.ndindex(cov.shape):
    cov[i,j] = i*j - i

我们可以将其替换为:

i, j = np.mgrid[:10, :10]
cov = i*j - i

作为另一个例子,让我们建立一个 100 x 100 的“倒锥形”值:

# The complex numbers in "mgrid" give the number of increments
# mgrid[min:max:num*1j, min:max:num*1j] is similar to
# meshgrid(linspace(min, max, num), linspace(min, max, num))
y, x = np.mgrid[-5:5:100j, -5:5:100j]

# Our "inverted cone" is just the distance from 0
r = np.hypot(x, y)

布朗桥

这是可以轻松矢量化的一个很好的例子。如果我正确阅读了您的示例,您会想要类似于:

import numpy as np

st = np.mgrid[1:101, 1:101]
s, t = st
cov = st.min(axis=0) - s * t

总的来说,我只涉及了一些一般模式。不过,希望这能让您找到正确的方向。

【讨论】:

  • 这就是我想要的。感谢您的努力!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-20
  • 1970-01-01
  • 2012-12-09
相关资源
最近更新 更多