Python 中均值和协方差的快速增量更新

Fast incremental update of the mean and covariance in Python

我有一个 Python 脚本,我需要在其中频繁更新均值和协方差矩阵。我目前正在做的是,每次我得到一个新的数据点 $x$(一个向量),我都会重新计算均值和协方差,如下所示:

data.append(x) # My `data` is just a list of lists of floats (i.e., x is a list of floats)
self.mean = np.mean( data, axis=0) # self.mean is a list representing the center of data
self.cov = np.cov( data, rowvar=0)

问题是对我来说速度不够快。是否有通过增量更新 meancov 而不根据所有 data 重新计算它们来提高效率的方法?

增量计算均值应该很容易,我可以算出来。我的主要问题是如何更新协方差矩阵 self.cov.

对于均值的计算,可以把之前N个数据的均值存起来,假设叫"before_mean",当新的数据x来的时候,这N+1个数据的新均值会像以前一样简单计算:

new_mean = float(before_mean * N + x) / (N + 1)

所以你不需要重新计算之前的数据。

对于 cov,我不认为有简单的方法来解决它,我不确定你的数据输入,因为 cov 总是与数字以外的列表一起使用。

出于好奇,如果数据集不是那么大,我认为你不需要关心这个,因为它是 O(N)

希望对你有帮助~

============更新===========

import numpy as np
import random

data = []
means = []
for m in range(3):
    sample_data = random.sample(range(10), 5)
    means.append(np.mean(sample_data))
    data.append(sample_data)

# calculate origin cov
origin_cov = np.cov(data)
print origin_cov

# new data
x = random.sample(range(10), 5)
mean_x = np.mean(x)
var_x = np.var(x)
new_line_cov = []
new_cov = np.empty([len(data)+1, len(data)+1])
for idx, sample_data in enumerate(data):
    mul_x_sample = 0
    for (elem_x, elem_sample) in zip(x, sample_data):
            mul_x_sample += (elem_x * elem_sample)
    mul_x_sample = mul_x_sample / len(x)
    cov_x_sample = mul_x_sample - mean_x * means[idx]
    new_cov[idx] = np.append(origin_cov[idx],cov_x_sample)
    new_line_cov.append(cov_x_sample)
new_line_cov.append(var_x)
new_cov[len(data)] = np.array(new_line_cov)

print new_cov

输出结果如下:

起源地

[[ 9.7   2.7  -4.05]
 [ 2.7   3.7  -3.05]
 [-4.05 -3.05  5.7 ]]

[[ 9.7   2.7  -4.05  0.56]
 [ 2.7   3.7  -3.05  1.56]
 [-4.05 -3.05  5.7   0.36]
 [ 0.56  1.56  0.36  8.56]]

对于一个方差(只有协方差矩阵的对角线)很简单。您还需要保留数据的平方和。回想一下方差的公式是:Var(x)=E[x^2]-(E[x])^2)。所以每一步你都在计算你的常规平均值,以及平方和的平均值。

这可以推广到完整协方差矩阵的多元变量。看看here.

我刚刚发现我们可以使用 mdp 库轻松地做到这一点 http://mdp-toolkit.sourceforge.net/api/mdp.utils.CovarianceMatrix-class.html

我会通过跟踪总和和平方和来做到这一点。

__init__中:

self.sumx = 0
self.sumx2 = 0

然后在追加中:

data.append(x)
self.sumx += x
self.sumx2 += x * x[:,np,newaxis]

self.mean = sumx / len(data)
self.cov = (self.sumx2 - self.mean * self.mean[:,np,newaxis])  / len(data)

注意 [:,np.newaxis] 广播以找到每对元素的产物