package onlinestats import ( "math" "math/rand" ) type Reservoir struct { data []float64 n int sum float64 } func NewReservoir(capacity int) *Reservoir { return &Reservoir{ data: make([]float64, 0, capacity), } } func (r *Reservoir) Push(n float64) { index := r.n r.n++ // not enough samples yet -- add it if index < cap(r.data) { r.data = append(r.data, n) return } ridx := rand.Intn(r.n) // == index+1, so we're 0..index inclusive if ridx >= len(r.data) { // ignore this one return } // add to our sample old := r.data[ridx] r.data[ridx] = n r.sum -= old r.sum += n } func (r *Reservoir) Len() int { return len(r.data) } func (r *Reservoir) Mean() float64 { return r.sum / float64(r.Len()) } func (r *Reservoir) Var() float64 { n := float64(r.Len()) mean := r.Mean() l := r.Len() sum1 := 0.0 sum2 := 0.0 for i := 0; i < l; i++ { xm := r.data[i] - mean sum1 += xm * xm sum2 += xm } return (sum1 - (sum2*sum2)/n) / (n - 1) } func (r *Reservoir) Stddev() float64 { return math.Sqrt(r.Var()) }