blob: 0deb90442afaabcc637aac0491e5f40c237eeee9 [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package stats
import (
"fmt"
"math"
)
// TODO(austin) Unify more with Sample interface
// StreamStats tracks basic statistics for a stream of data in O(1)
// space.
//
// StreamStats should be initialized to its zero value.
type StreamStats struct {
Count uint
Total, Min, Max float64
// Numerically stable online mean
mean float64
meanOfSquares float64
// Online variance
vM2 float64
}
// Add updates s's statistics with sample value x.
func (s *StreamStats) Add(x float64) {
s.Total += x
if s.Count == 0 {
s.Min, s.Max = x, x
} else {
if x < s.Min {
s.Min = x
}
if x > s.Max {
s.Max = x
}
}
s.Count++
// Update online mean, mean of squares, and variance. Online
// variance based on Wikipedia's presentation ("Algorithms for
// calculating variance") of Knuth's formulation of Welford
// 1962.
delta := x - s.mean
s.mean += delta / float64(s.Count)
s.meanOfSquares += (x*x - s.meanOfSquares) / float64(s.Count)
s.vM2 += delta * (x - s.mean)
}
func (s *StreamStats) Weight() float64 {
return float64(s.Count)
}
func (s *StreamStats) Mean() float64 {
return s.mean
}
func (s *StreamStats) Variance() float64 {
return s.vM2 / float64(s.Count-1)
}
func (s *StreamStats) StdDev() float64 {
return math.Sqrt(s.Variance())
}
func (s *StreamStats) RMS() float64 {
return math.Sqrt(s.meanOfSquares)
}
// Combine updates s's statistics as if all samples added to o were
// added to s.
func (s *StreamStats) Combine(o *StreamStats) {
count := s.Count + o.Count
// Compute combined online variance statistics
delta := o.mean - s.mean
mean := s.mean + delta*float64(o.Count)/float64(count)
vM2 := s.vM2 + o.vM2 + delta*delta*float64(s.Count)*float64(o.Count)/float64(count)
s.Count = count
s.Total += o.Total
if o.Min < s.Min {
s.Min = o.Min
}
if o.Max > s.Max {
s.Max = o.Max
}
s.mean = mean
s.meanOfSquares += (o.meanOfSquares - s.meanOfSquares) * float64(o.Count) / float64(count)
s.vM2 = vM2
}
func (s *StreamStats) String() string {
return fmt.Sprintf("Count=%d Total=%g Min=%g Mean=%g RMS=%g Max=%g StdDev=%g", s.Count, s.Total, s.Min, s.Mean(), s.RMS(), s.Max, s.StdDev())
}