流计算中的简单统计指标计算

4 年前 · 来自专栏 清雨的 Data Science 笔记

一般我们对数字序列的统计包括:个数,求和,平均值(期望),方差()。

其中,个数( \sum^{n}_{i=1}{1}=n )、求和( s=\sum^{n}_{i=1}{x_i} )是最简单的,按下不表。平均值是 \mu =\frac{\sum^{n}_{i=1}{x_i}}{n} 也很简单,这也是期望的求法。不过需要注意的是,这里的数据都是不带权值的,如果带权值的,需要将个数换成 \sum^{n}_{i=1}{w_i}

这里比较麻烦的是方差的求法,一般是 v=\frac{1}{n-1} \sum^{n}_{i=1}{(x_i-\mu)^2} ,这么求的话需要保留所有样本,如果需要在有限的内存下求取方差就需要展开:

v=\frac{1}{n-1} \sum^{n}_{i=1}{ x_i^2+\mu^2-2\mu x_i }

这样就裂解成了好几个部分:

v=\frac{1}{n-1} \sum^{n}_{i=1}{ x_i^2 }+ \frac{n\mu^2}{n-1}+ \frac{2\mu}{n-1} \sum^{n}_{i=1}{ x_i }

再化简一下得到:

v=\frac{1}{n-1} \sum^{n}_{i=1}{ x_i^2 }+ \frac{n\mu^2}{n-1}

这样只需要求值的和与值的平方和就可以以流的方式获取方差了。

下面放Scala代码:


/**
  * 数字简单统计
  * @param minValue        最小值
  * @param maxValue        最大值
  * @param sumValue        求和
  * @param sumSquaredValue 平方后数值的和
  * @param count           计数
case class SimpleNumberStatistics(minValue: Double, maxValue: Double, sumValue: Double, sumSquaredValue: Double, count: Long) {
      * 和另一个统计结果合并
      * @param v 另一个统计结果
      * @return
    def +(v: SimpleNumberStatistics): SimpleNumberStatistics = new SimpleNumberStatistics(
        math.min(v.minValue, minValue),
        math.max(v.maxValue, maxValue),
        v.sumValue + sumValue,
        v.sumSquaredValue + sumSquaredValue,
        v.count + count
      * 流式状态更新方式获取方差
      * @return
    def variance: Double = if (count > 1) {
        (sumSquaredValue - average * average * count) / (count - 1)
    } else {
        Double.NaN
      * 获取标准差
      * @return
    def std: Double = math.sqrt(variance)
      * 和另一个数字合并
      * @param v 数字
      * @return
    def +(v: Double): SimpleNumberStatistics = SimpleNumberStatistics.apply(v) + this
      * 求平均值
      * @return
    def average: Double = sumValue * 1.0 / count
object SimpleNumberStatistics {
      * 通过数字创建
      * @param number 数字
      * @return
    def apply(number: Double): SimpleNumberStatistics = new SimpleNumberStatistics(
        number,
        number,
        number,
        number * number,
      * 通过数组创建
      * @param numbers 数组
      * @return
    def apply(numbers: Iterable[Double]): SimpleNumberStatistics = new SimpleNumberStatistics(
        numbers.min,