【问题标题】:Fast incremental update of the mean and covariance in PythonPython中均值和协方差的快速增量更新
【发布时间】:2016-05-28 11:49:06
【问题描述】:

我有一个 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

【问题讨论】:

  • 阅读this wikipedia article 并使用在线算法,用于方差和协方差。远离以下建议来跟踪平方和,因为它们可能在数值上不稳定。
  • 本文介绍了一种快速更新均值等矩的方法arxiv.org/abs/1510.04923
  • @Jaime Nah,它只有在数百次迭代后才会收敛!

标签: python numpy normal-distribution


【解决方案1】:

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

__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] 广播以查找每对元素的产物

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我很想看看你是如何应用这个的。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      对于平均值计算,你可以存储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]]
      

      【讨论】:

      • 平均值很容易增量计算。我的问题主要是如何更新协方差矩阵。我的data 只是浮动列表的列表。
      • @eLearner ,我认为 numpy 不直接支持,但可以手动检查,请检查我更新的答案代码,谢谢
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多