流计算中的简单统计指标计算
一般我们对数字序列的统计包括:个数,求和,平均值(期望),方差()。
其中,个数( \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,