84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
// 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"
|
|
|
|
// LogHist is a Histogram with logarithmically-spaced bins.
|
|
type LogHist struct {
|
|
b int
|
|
m float64
|
|
mOverLogb float64
|
|
low, high uint
|
|
bins []uint
|
|
}
|
|
|
|
// NewLogHist returns an empty logarithmic histogram with bins for
|
|
// integral values of m * log_b(x) up to x = max.
|
|
func NewLogHist(b int, m float64, max float64) *LogHist {
|
|
// TODO(austin) Minimum value as well? If the samples are
|
|
// actually integral, having fractional bin boundaries can
|
|
// mess up smoothing.
|
|
mOverLogb := m / math.Log(float64(b))
|
|
nbins := int(math.Ceil(mOverLogb * math.Log(max)))
|
|
return &LogHist{b: b, m: m, mOverLogb: mOverLogb, low: 0, high: 0, bins: make([]uint, nbins)}
|
|
}
|
|
|
|
func (h *LogHist) bin(x float64) int {
|
|
return int(h.mOverLogb * math.Log(x))
|
|
}
|
|
|
|
func (h *LogHist) Add(x float64) {
|
|
bin := h.bin(x)
|
|
if bin < 0 {
|
|
h.low++
|
|
} else if bin >= len(h.bins) {
|
|
h.high++
|
|
} else {
|
|
h.bins[bin]++
|
|
}
|
|
}
|
|
|
|
func (h *LogHist) Counts() (uint, []uint, uint) {
|
|
return h.low, h.bins, h.high
|
|
}
|
|
|
|
func (h *LogHist) BinToValue(bin float64) float64 {
|
|
return math.Pow(float64(h.b), bin/h.m)
|
|
}
|
|
|
|
func (h *LogHist) At(x float64) float64 {
|
|
bin := h.bin(x)
|
|
if bin < 0 || bin >= len(h.bins) {
|
|
return 0
|
|
}
|
|
return float64(h.bins[bin])
|
|
}
|
|
|
|
func (h *LogHist) Bounds() (float64, float64) {
|
|
// XXX Plot will plot this on a linear axis. Maybe this
|
|
// should be able to return the natural axis?
|
|
// Maybe then we could also give it the bins for the tics.
|
|
lowbin := 0
|
|
if h.low == 0 {
|
|
for bin, count := range h.bins {
|
|
if count > 0 {
|
|
lowbin = bin
|
|
break
|
|
}
|
|
}
|
|
}
|
|
highbin := len(h.bins)
|
|
if h.high == 0 {
|
|
for bin := range h.bins {
|
|
if h.bins[len(h.bins)-bin-1] > 0 {
|
|
highbin = len(h.bins) - bin
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return h.BinToValue(float64(lowbin)), h.BinToValue(float64(highbin))
|
|
}
|