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)
问题是对我来说速度不够快。是否有通过增量更新 mean
和 cov
而不根据所有 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]
广播以找到每对元素的产物
我有一个 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)
问题是对我来说速度不够快。是否有通过增量更新 mean
和 cov
而不根据所有 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]
广播以找到每对元素的产物