blob: 8578c073d95b671e2139ff260e28fcf2bda77232 [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package stats
import "math"
// TODO: Implement histograms on top of scales.
type Histogram interface {
// Add adds a sample with value x to histogram h.
Add(x float64)
// Counts returns the number of samples less than the lowest
// bin, a slice of the number of samples in each bin,
// and the number of samples greater than the highest bin.
Counts() (under uint, counts []uint, over uint)
// BinToValue returns the value that would appear at the given
// bin index.
//
// For integral values of bin, BinToValue returns the lower
// bound of bin. That is, a sample value x will be in bin if
// bin is integral and
//
// BinToValue(bin) <= x < BinToValue(bin + 1)
//
// For non-integral values of bin, BinToValue interpolates
// between the lower and upper bounds of math.Floor(bin).
//
// BinToValue is undefined if bin > 1 + the number of bins.
BinToValue(bin float64) float64
}
// HistogramQuantile returns the x such that n*q samples in hist are
// <= x, assuming values are distibuted within each bin according to
// hist's distribution.
//
// If the q'th sample falls below the lowest bin or above the highest
// bin, returns NaN.
func HistogramQuantile(hist Histogram, q float64) float64 {
under, counts, over := hist.Counts()
total := under + over
for _, count := range counts {
total += count
}
goal := uint(float64(total) * q)
if goal <= under || goal > total-over {
return math.NaN()
}
for bin, count := range counts {
if count > goal {
return hist.BinToValue(float64(bin) + float64(goal)/float64(count))
}
goal -= count
}
panic("goal count not reached")
}
// HistogramIQR returns the interquartile range of the samples in
// hist.
func HistogramIQR(hist Histogram) float64 {
return HistogramQuantile(hist, 0.75) - HistogramQuantile(hist, 0.25)
}