rstats is a simple helper library to calculate running statistics. It's memory efficient using only 7 internal variables and update statistics values on the fly every time you push data to it (no arrays or slices involved).
This is a Go port of C++ code by John D. Cook. Please check his great blog post.
The implementation uses the Welford method presented in Donald Knuth’s Art of Computer Programming, Vol 2, page 232, 3rd edition. Please refer to John D. Cook's post for a detailed explanation and references.
Get a new instance and push float64 values to it with Add method:
stats := rstats.New()
// ...
stats.Add(/*some value*/)At any time you can call the methods to get statiscs:
CountMinMaxMeanStandardDeviationVarianceSkewnessKurtosis
Stats also implements Stringer and returns an string like this:
count 1893209 min 0.01 max 1.80 mean 0.51 (std dev 0.289 variance 0.08)
You can reset the state calling Reset method.
I use this package mostly to measure requests/connections on my projects. Here is a dummy implementation collecting execution time statistics of a HTTP handler, and periodically logging it.
package main
import (
"log"
"math/rand"
"net/http"
"time"
"github.com/lucindo/rstats"
)
func hello(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
w.Write([]byte("Hello stats!"))
}
func handleStats(fn http.HandlerFunc, stats *rstats.Stats) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer stats.Add(time.Since(start).Seconds())
fn(w, r)
}
}
func main() {
stats := rstats.New()
ticker := time.NewTicker(time.Millisecond * 1500)
go func(stats *rstats.Stats) {
for _ = range ticker.C {
log.Println("http stats:", stats)
}
}(stats)
http.HandleFunc("/", handleStats(hello, stats))
log.Fatal(http.ListenAndServe(":8080", nil))
}This package also provides an auxiliary struct to serialize the statistics:
stats := rstats.New()
// ...
stats.Add(/*some value*/)
// ...
statsStruct := new(rstats.StatsStruct)
rstats.GetStatsStruct(statsStruct, stats)
// serialize statsStructI use it to expose statistics in expvar this way:
import (
"expvar"
"github.com/lucindo/rstats"
)
func statsexpvar(stats *rstats.Stats) expvar.Func {
return func() interface{} {
statsStruct := new(rstats.StatsStruct)
rstats.GetStatsStruct(statsStruct, stats)
return *statsStruct
}
}
func main() {
stats := rstats.New()
expvar.Publish("MyStats", expvar.Func(statsexpvar(stats)))
//...
}
