From 2f4d0b0f9a265457bc508e3d03e64821ffd236fd Mon Sep 17 00:00:00 2001 From: Justin Li Date: Tue, 22 Jul 2014 02:41:39 -0400 Subject: [PATCH] Add base Percentile interface and tests --- stats/percentile.go | 45 ++++++++++++++++++++++++++++++++++++++++ stats/percentile_test.go | 30 +++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 stats/percentile.go create mode 100644 stats/percentile_test.go diff --git a/stats/percentile.go b/stats/percentile.go new file mode 100644 index 0000000..2332af8 --- /dev/null +++ b/stats/percentile.go @@ -0,0 +1,45 @@ +package stats + +import ( + "sort" +) + +type Percentile struct { + percentile float64 + values sort.Float64Slice + offset int +} + +func NewPercentile(percentile float64, sampleWindow int) *Percentile { + return &Percentile{ + percentile: percentile, + values: make([]float64, 0, sampleWindow), + } +} + +func (p *Percentile) AddSample(sample float64) { + p.values = append(p.values, sample) + sort.Sort(p.values) +} + +func (p *Percentile) Value() float64 { + if len(p.values) == 0 { + return 0 + } + + return p.values[round(p.index())] +} + +func (p *Percentile) index() float64 { + return float64(len(p.values)) * p.percentile - float64(p.offset) +} + +func round(value float64) int64 { + if value < 0.0 { + value -= 0.5 + } else { + value += 0.5 + } + + return int64(value) +} diff --git a/stats/percentile_test.go b/stats/percentile_test.go new file mode 100644 index 0000000..59addbe --- /dev/null +++ b/stats/percentile_test.go @@ -0,0 +1,30 @@ +package stats + +import ( + "testing" + "math/rand" +) + +func TestPercentiles(t *testing.T) { + testInRange(t, 1, 0.5) + testInRange(t, 1, 0.9) + testInRange(t, 1, 0.95) + testInRange(t, 10000, 0.5) + testInRange(t, 10000, 0.9) + testInRange(t, 10000, 0.95) +} + +func testInRange(t *testing.T, max, percentile float64) { + p := NewPercentile(percentile, 10) + + for i := 0; i < 1000; i++ { + p.AddSample(rand.Float64() * max) + } + + got := p.Value() + expected := percentile * max + + if got < expected * (1 - 0.02) || got > expected * (1 + 0.02) { + t.Errorf("Percentile out of range\n actual: %f\nexpected: %f", got, expected) + } +}