commit e7e174530e2d3dfca989f156d4e006ccc84b95de Author: Stephen McQuay Date: Sat Dec 28 23:56:48 2013 -0800 init diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..3aa41b2 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +tags +.*.swp diff --git a/ostat.go b/ostat.go new file mode 100644 index 0000000..e1473d6 --- /dev/null +++ b/ostat.go @@ -0,0 +1,56 @@ +package ostat + +import ( + "errors" + "math" +) + +// from http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm + +type OnlineStat struct { + n int64 + mean float64 + m2 float64 + Max float64 + Min float64 +} + +func NewOnlineStat() *OnlineStat { + return &OnlineStat{ + Min: math.Inf(1), + Max: math.Inf(-1), + } +} + +func (os *OnlineStat) Push(v float64) { + os.n += 1 + if v < os.Min { + os.Min = v + } + if v > os.Max { + os.Max = v + } + + delta := v - os.mean + os.mean = os.mean + delta/float64(os.n) + os.m2 = os.m2 + delta*(v-os.mean) +} + +func (os *OnlineStat) Mean() (float64, error) { + if os.n == 0 { + return 0.0, errors.New("no data") + } + return os.mean, nil +} + +func (os *OnlineStat) Variance() (float64, error) { + if os.n == 0 { + return 0.0, errors.New("no data") + } + return os.m2 / float64(os.n-1), nil +} + +func (os *OnlineStat) StdDev() (float64, error) { + variance, err := os.Variance() + return math.Sqrt(variance), err +} diff --git a/ostat_test.go b/ostat_test.go new file mode 100644 index 0000000..93c923a --- /dev/null +++ b/ostat_test.go @@ -0,0 +1,45 @@ +package ostat + +import ( + "math" + "testing" +) + +func TestInsert(t *testing.T) { + os := NewOnlineStat() + v := []float64{4, 7, 13, 16} + for _, i := range v { + os.Push(i) + } + if os.Min != 4 { + t.Errorf("incorrectly calculated min") + } + if os.Max != 16 { + t.Errorf("incorrectly calculated max") + } + if m, _ := os.Mean(); m != 10.0 { + t.Errorf("incorrect mean") + } + if variance, _ := os.Variance(); variance != 30.0 { + t.Errorf("incorrect variance: %f", variance) + } + if stdev, _ := os.StdDev(); stdev != math.Sqrt(30.0) { + t.Errorf("incorrect stdev: %f", stdev) + } +} + +func TestEmpty(t *testing.T) { + os := NewOnlineStat() + _, err := os.Mean() + if err == nil { + t.Errorf("failure to notify the running stat was empty") + } + _, err = os.Variance() + if err == nil { + t.Errorf("failure to notify the running stat was empty") + } + _, err = os.StdDev() + if err == nil { + t.Errorf("failure to notify the running stat was empty") + } +}