76 | 76 |
// is guaranteed to be within (Quantile±Epsilon).
|
77 | 77 |
//
|
78 | 78 |
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
79 | |
func NewTargeted(targets map[float64]float64) *Stream {
|
|
79 |
func NewTargeted(targetMap map[float64]float64) *Stream {
|
|
80 |
// Convert map to slice to avoid slow iterations on a map.
|
|
81 |
// ƒ is called on the hot path, so converting the map to a slice
|
|
82 |
// beforehand results in significant CPU savings.
|
|
83 |
targets := targetMapToSlice(targetMap)
|
|
84 |
|
80 | 85 |
ƒ := func(s *stream, r float64) float64 {
|
81 | 86 |
var m = math.MaxFloat64
|
82 | 87 |
var f float64
|
83 | |
for quantile, epsilon := range targets {
|
84 | |
if quantile*s.n <= r {
|
85 | |
f = (2 * epsilon * r) / quantile
|
|
88 |
for _, t := range targets {
|
|
89 |
if t.quantile*s.n <= r {
|
|
90 |
f = (2 * t.epsilon * r) / t.quantile
|
86 | 91 |
} else {
|
87 | |
f = (2 * epsilon * (s.n - r)) / (1 - quantile)
|
|
92 |
f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
|
88 | 93 |
}
|
89 | 94 |
if f < m {
|
90 | 95 |
m = f
|
|
93 | 98 |
return m
|
94 | 99 |
}
|
95 | 100 |
return newStream(ƒ)
|
|
101 |
}
|
|
102 |
|
|
103 |
type target struct {
|
|
104 |
quantile float64
|
|
105 |
epsilon float64
|
|
106 |
}
|
|
107 |
|
|
108 |
func targetMapToSlice(targetMap map[float64]float64) []target {
|
|
109 |
targets := make([]target, 0, len(targetMap))
|
|
110 |
|
|
111 |
for quantile, epsilon := range targetMap {
|
|
112 |
t := target{
|
|
113 |
quantile: quantile,
|
|
114 |
epsilon: epsilon,
|
|
115 |
}
|
|
116 |
targets = append(targets, t)
|
|
117 |
}
|
|
118 |
|
|
119 |
return targets
|
96 | 120 |
}
|
97 | 121 |
|
98 | 122 |
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|