Add log normal distributions to the percentile calculation tests
This commit is contained in:
parent
60fd62cc82
commit
b6f0fc79cb
2 changed files with 56 additions and 27 deletions
|
@ -40,7 +40,7 @@ func (p *Percentile) Value() float64 {
|
||||||
func (p *Percentile) AddSample(sample float64) {
|
func (p *Percentile) AddSample(sample float64) {
|
||||||
p.samples++
|
p.samples++
|
||||||
|
|
||||||
if p.samples > int64(cap(p.values)) {
|
if len(p.values) == cap(p.values) {
|
||||||
target := float64(p.samples)*p.percentile - float64(cap(p.values))/2
|
target := float64(p.samples)*p.percentile - float64(cap(p.values))/2
|
||||||
offset := round(math.Max(target, 0))
|
offset := round(math.Max(target, 0))
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -9,55 +11,82 @@ import (
|
||||||
func TestPercentiles(t *testing.T) {
|
func TestPercentiles(t *testing.T) {
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
|
|
||||||
testUniformRandom(t, 1, 0.5)
|
testSlice(t, uniform(10000, 1), 0.5)
|
||||||
testUniformRandom(t, 1, 0.9)
|
testSlice(t, uniform(10000, 1), 0.9)
|
||||||
testUniformRandom(t, 1, 0.95)
|
testSlice(t, uniform(10000, 10000), 0.5)
|
||||||
testUniformRandom(t, 10000, 0.5)
|
testSlice(t, uniform(10000, 10000), 0.9)
|
||||||
testUniformRandom(t, 10000, 0.9)
|
|
||||||
testUniformRandom(t, 10000, 0.95)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUniformRandom(t *testing.T, max, percentile float64) {
|
func TestLogNormPercentiles(t *testing.T) {
|
||||||
p := NewPercentile(percentile, 256)
|
rand.Seed(time.Now().Unix())
|
||||||
|
|
||||||
for i := 0; i < 100000; i++ {
|
testSlice(t, logNorm(10000, 1), 0.5)
|
||||||
p.AddSample(rand.Float64() * max)
|
testSlice(t, logNorm(10000, 1), 0.9)
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniform(n int, scale float64) sort.Float64Slice {
|
||||||
|
numbers := make(sort.Float64Slice, n)
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
numbers[i] = rand.Float64() * scale
|
||||||
}
|
}
|
||||||
|
|
||||||
got := p.Value()
|
return numbers
|
||||||
expected := percentile * max
|
}
|
||||||
maxError := 0.01
|
|
||||||
|
|
||||||
if got < expected*(1-maxError) || got > expected*(1+maxError) {
|
func logNorm(n int, scale float64) sort.Float64Slice {
|
||||||
t.Errorf("Percentile out of range\n actual: %f\nexpected: %f\n error: %f%%\n", got, expected, (got-expected)/expected*100)
|
numbers := make(sort.Float64Slice, n)
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
numbers[i] = math.Exp(rand.NormFloat64()) * scale
|
||||||
|
}
|
||||||
|
|
||||||
|
return numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSlice(t *testing.T, numbers sort.Float64Slice, percentile float64) {
|
||||||
|
p := NewPercentile(percentile, 256)
|
||||||
|
|
||||||
|
for i := 0; i < len(numbers); i++ {
|
||||||
|
p.AddSample(numbers[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(numbers)
|
||||||
|
got := p.Value()
|
||||||
|
expected := numbers[round(float64(len(numbers))*percentile)]
|
||||||
|
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("Percentile incorrect\n actual: %f\nexpected: %f\n error: %f%%\n", got, expected, (got-expected)/expected*100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPercentiles64(b *testing.B) {
|
func BenchmarkPercentiles64(b *testing.B) {
|
||||||
benchmarkUniformRandom(b, 64, 0.5)
|
benchmarkSlice(b, uniform(b.N, 1), 64, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPercentiles128(b *testing.B) {
|
func BenchmarkPercentiles128(b *testing.B) {
|
||||||
benchmarkUniformRandom(b, 128, 0.5)
|
benchmarkSlice(b, uniform(b.N, 1), 128, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPercentiles256(b *testing.B) {
|
func BenchmarkPercentiles256(b *testing.B) {
|
||||||
benchmarkUniformRandom(b, 256, 0.5)
|
benchmarkSlice(b, uniform(b.N, 1), 256, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPercentiles512(b *testing.B) {
|
func BenchmarkPercentiles512(b *testing.B) {
|
||||||
benchmarkUniformRandom(b, 512, 0.5)
|
benchmarkSlice(b, uniform(b.N, 1), 512, 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkUniformRandom(b *testing.B, window int, percentile float64) {
|
func BenchmarkLNPercentiles128(b *testing.B) {
|
||||||
|
benchmarkSlice(b, logNorm(b.N, 1), 128, 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLNPercentiles256(b *testing.B) {
|
||||||
|
benchmarkSlice(b, logNorm(b.N, 1), 258, 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkSlice(b *testing.B, numbers sort.Float64Slice, window int, percentile float64) {
|
||||||
p := NewPercentile(percentile, window)
|
p := NewPercentile(percentile, window)
|
||||||
|
|
||||||
numbers := make([]float64, b.N)
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
numbers[i] = rand.Float64()
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
p.AddSample(numbers[i])
|
p.AddSample(numbers[i])
|
||||||
|
|
Loading…
Add table
Reference in a new issue