mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 21:54:10 -08:00
Export quantile functions (#15190)
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (0) (push) Waiting to run
CI / Build Prometheus for common architectures (1) (push) Waiting to run
CI / Build Prometheus for common architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (0) (push) Waiting to run
CI / Build Prometheus for all architectures (1) (push) Waiting to run
CI / Build Prometheus for all architectures (10) (push) Waiting to run
CI / Build Prometheus for all architectures (11) (push) Waiting to run
CI / Build Prometheus for all architectures (2) (push) Waiting to run
CI / Build Prometheus for all architectures (3) (push) Waiting to run
CI / Build Prometheus for all architectures (4) (push) Waiting to run
CI / Build Prometheus for all architectures (5) (push) Waiting to run
CI / Build Prometheus for all architectures (6) (push) Waiting to run
CI / Build Prometheus for all architectures (7) (push) Waiting to run
CI / Build Prometheus for all architectures (8) (push) Waiting to run
CI / Build Prometheus for all architectures (9) (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
Export quantile functions For use in Mimir's query engine, it would be helpful if these functions were exported. Co-authored-by: Björn Rabenstein <github@rabenste.in> Signed-off-by: Joshua Hesketh <josh@hesketh.net.au> --------- Signed-off-by: Joshua Hesketh <josh@nitrotech.org> Signed-off-by: Joshua Hesketh <josh@hesketh.net.au> Co-authored-by: Björn Rabenstein <github@rabenste.in>
This commit is contained in:
parent
9ad93ba8df
commit
8e3301eb44
|
@ -1300,7 +1300,7 @@ func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *Ev
|
||||||
}
|
}
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: sample.Metric,
|
Metric: sample.Metric,
|
||||||
F: histogramFraction(lower, upper, sample.H),
|
F: HistogramFraction(lower, upper, sample.H),
|
||||||
DropName: true,
|
DropName: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1352,7 +1352,7 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
|
||||||
mb = &metricWithBuckets{sample.Metric, nil}
|
mb = &metricWithBuckets{sample.Metric, nil}
|
||||||
enh.signatureToMetricWithBuckets[string(enh.lblBuf)] = mb
|
enh.signatureToMetricWithBuckets[string(enh.lblBuf)] = mb
|
||||||
}
|
}
|
||||||
mb.buckets = append(mb.buckets, bucket{upperBound, sample.F})
|
mb.buckets = append(mb.buckets, Bucket{upperBound, sample.F})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now deal with the histograms.
|
// Now deal with the histograms.
|
||||||
|
@ -1374,14 +1374,14 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
|
||||||
}
|
}
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: sample.Metric,
|
Metric: sample.Metric,
|
||||||
F: histogramQuantile(q, sample.H),
|
F: HistogramQuantile(q, sample.H),
|
||||||
DropName: true,
|
DropName: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mb := range enh.signatureToMetricWithBuckets {
|
for _, mb := range enh.signatureToMetricWithBuckets {
|
||||||
if len(mb.buckets) > 0 {
|
if len(mb.buckets) > 0 {
|
||||||
res, forcedMonotonicity, _ := bucketQuantile(q, mb.buckets)
|
res, forcedMonotonicity, _ := BucketQuantile(q, mb.buckets)
|
||||||
enh.Out = append(enh.Out, Sample{
|
enh.Out = append(enh.Out, Sample{
|
||||||
Metric: mb.metric,
|
Metric: mb.metric,
|
||||||
F: res,
|
F: res,
|
||||||
|
|
|
@ -51,20 +51,22 @@ var excludedLabels = []string{
|
||||||
labels.BucketLabel,
|
labels.BucketLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
type bucket struct {
|
// Bucket represents a bucket of a classic histogram. It is used internally by the promql
|
||||||
upperBound float64
|
// package, but it is nevertheless exported for potential use in other PromQL engines.
|
||||||
count float64
|
type Bucket struct {
|
||||||
|
UpperBound float64
|
||||||
|
Count float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// buckets implements sort.Interface.
|
// Buckets implements sort.Interface.
|
||||||
type buckets []bucket
|
type Buckets []Bucket
|
||||||
|
|
||||||
type metricWithBuckets struct {
|
type metricWithBuckets struct {
|
||||||
metric labels.Labels
|
metric labels.Labels
|
||||||
buckets buckets
|
buckets Buckets
|
||||||
}
|
}
|
||||||
|
|
||||||
// bucketQuantile calculates the quantile 'q' based on the given buckets. The
|
// BucketQuantile calculates the quantile 'q' based on the given buckets. The
|
||||||
// buckets will be sorted by upperBound by this function (i.e. no sorting
|
// buckets will be sorted by upperBound by this function (i.e. no sorting
|
||||||
// needed before calling this function). The quantile value is interpolated
|
// needed before calling this function). The quantile value is interpolated
|
||||||
// assuming a linear distribution within a bucket. However, if the quantile
|
// assuming a linear distribution within a bucket. However, if the quantile
|
||||||
|
@ -95,7 +97,14 @@ type metricWithBuckets struct {
|
||||||
// and another bool to indicate if small differences between buckets (that
|
// and another bool to indicate if small differences between buckets (that
|
||||||
// are likely artifacts of floating point precision issues) have been
|
// are likely artifacts of floating point precision issues) have been
|
||||||
// ignored.
|
// ignored.
|
||||||
func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
//
|
||||||
|
// Generically speaking, BucketQuantile is for calculating the
|
||||||
|
// histogram_quantile() of classic histograms. See also: HistogramQuantile
|
||||||
|
// for native histograms.
|
||||||
|
//
|
||||||
|
// BucketQuantile is exported as a useful quantile function over a set of
|
||||||
|
// given buckets. It may be used by other PromQL engine implementations.
|
||||||
|
func BucketQuantile(q float64, buckets Buckets) (float64, bool, bool) {
|
||||||
if math.IsNaN(q) {
|
if math.IsNaN(q) {
|
||||||
return math.NaN(), false, false
|
return math.NaN(), false, false
|
||||||
}
|
}
|
||||||
|
@ -105,17 +114,17 @@ func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
||||||
if q > 1 {
|
if q > 1 {
|
||||||
return math.Inf(+1), false, false
|
return math.Inf(+1), false, false
|
||||||
}
|
}
|
||||||
slices.SortFunc(buckets, func(a, b bucket) int {
|
slices.SortFunc(buckets, func(a, b Bucket) int {
|
||||||
// We don't expect the bucket boundary to be a NaN.
|
// We don't expect the bucket boundary to be a NaN.
|
||||||
if a.upperBound < b.upperBound {
|
if a.UpperBound < b.UpperBound {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
if a.upperBound > b.upperBound {
|
if a.UpperBound > b.UpperBound {
|
||||||
return +1
|
return +1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
|
if !math.IsInf(buckets[len(buckets)-1].UpperBound, +1) {
|
||||||
return math.NaN(), false, false
|
return math.NaN(), false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,33 +134,33 @@ func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
||||||
if len(buckets) < 2 {
|
if len(buckets) < 2 {
|
||||||
return math.NaN(), false, false
|
return math.NaN(), false, false
|
||||||
}
|
}
|
||||||
observations := buckets[len(buckets)-1].count
|
observations := buckets[len(buckets)-1].Count
|
||||||
if observations == 0 {
|
if observations == 0 {
|
||||||
return math.NaN(), false, false
|
return math.NaN(), false, false
|
||||||
}
|
}
|
||||||
rank := q * observations
|
rank := q * observations
|
||||||
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
|
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].Count >= rank })
|
||||||
|
|
||||||
if b == len(buckets)-1 {
|
if b == len(buckets)-1 {
|
||||||
return buckets[len(buckets)-2].upperBound, forcedMonotonic, fixedPrecision
|
return buckets[len(buckets)-2].UpperBound, forcedMonotonic, fixedPrecision
|
||||||
}
|
}
|
||||||
if b == 0 && buckets[0].upperBound <= 0 {
|
if b == 0 && buckets[0].UpperBound <= 0 {
|
||||||
return buckets[0].upperBound, forcedMonotonic, fixedPrecision
|
return buckets[0].UpperBound, forcedMonotonic, fixedPrecision
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
bucketStart float64
|
bucketStart float64
|
||||||
bucketEnd = buckets[b].upperBound
|
bucketEnd = buckets[b].UpperBound
|
||||||
count = buckets[b].count
|
count = buckets[b].Count
|
||||||
)
|
)
|
||||||
if b > 0 {
|
if b > 0 {
|
||||||
bucketStart = buckets[b-1].upperBound
|
bucketStart = buckets[b-1].UpperBound
|
||||||
count -= buckets[b-1].count
|
count -= buckets[b-1].Count
|
||||||
rank -= buckets[b-1].count
|
rank -= buckets[b-1].Count
|
||||||
}
|
}
|
||||||
return bucketStart + (bucketEnd-bucketStart)*(rank/count), forcedMonotonic, fixedPrecision
|
return bucketStart + (bucketEnd-bucketStart)*(rank/count), forcedMonotonic, fixedPrecision
|
||||||
}
|
}
|
||||||
|
|
||||||
// histogramQuantile calculates the quantile 'q' based on the given histogram.
|
// HistogramQuantile calculates the quantile 'q' based on the given histogram.
|
||||||
//
|
//
|
||||||
// For custom buckets, the result is interpolated linearly, i.e. it is assumed
|
// For custom buckets, the result is interpolated linearly, i.e. it is assumed
|
||||||
// the observations are uniformly distributed within each bucket. (This is a
|
// the observations are uniformly distributed within each bucket. (This is a
|
||||||
|
@ -186,7 +195,13 @@ func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
||||||
// If q>1, +Inf is returned.
|
// If q>1, +Inf is returned.
|
||||||
//
|
//
|
||||||
// If q is NaN, NaN is returned.
|
// If q is NaN, NaN is returned.
|
||||||
func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
//
|
||||||
|
// HistogramQuantile is for calculating the histogram_quantile() of native
|
||||||
|
// histograms. See also: BucketQuantile for classic histograms.
|
||||||
|
//
|
||||||
|
// HistogramQuantile is exported as it may be used by other PromQL engine
|
||||||
|
// implementations.
|
||||||
|
func HistogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
||||||
if q < 0 {
|
if q < 0 {
|
||||||
return math.Inf(-1)
|
return math.Inf(-1)
|
||||||
}
|
}
|
||||||
|
@ -297,11 +312,11 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
||||||
return -math.Exp2(logUpper + (logLower-logUpper)*(1-fraction))
|
return -math.Exp2(logUpper + (logLower-logUpper)*(1-fraction))
|
||||||
}
|
}
|
||||||
|
|
||||||
// histogramFraction calculates the fraction of observations between the
|
// HistogramFraction calculates the fraction of observations between the
|
||||||
// provided lower and upper bounds, based on the provided histogram.
|
// provided lower and upper bounds, based on the provided histogram.
|
||||||
//
|
//
|
||||||
// histogramFraction is in a certain way the inverse of histogramQuantile. If
|
// HistogramFraction is in a certain way the inverse of histogramQuantile. If
|
||||||
// histogramQuantile(0.9, h) returns 123.4, then histogramFraction(-Inf, 123.4, h)
|
// HistogramQuantile(0.9, h) returns 123.4, then HistogramFraction(-Inf, 123.4, h)
|
||||||
// returns 0.9.
|
// returns 0.9.
|
||||||
//
|
//
|
||||||
// The same notes with regard to interpolation and assumptions about the zero
|
// The same notes with regard to interpolation and assumptions about the zero
|
||||||
|
@ -328,7 +343,10 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
||||||
// If lower or upper is NaN, NaN is returned.
|
// If lower or upper is NaN, NaN is returned.
|
||||||
//
|
//
|
||||||
// If lower >= upper and the histogram has at least 1 observation, zero is returned.
|
// If lower >= upper and the histogram has at least 1 observation, zero is returned.
|
||||||
func histogramFraction(lower, upper float64, h *histogram.FloatHistogram) float64 {
|
//
|
||||||
|
// HistogramFraction is exported as it may be used by other PromQL engine
|
||||||
|
// implementations.
|
||||||
|
func HistogramFraction(lower, upper float64, h *histogram.FloatHistogram) float64 {
|
||||||
if h.Count == 0 || math.IsNaN(lower) || math.IsNaN(upper) {
|
if h.Count == 0 || math.IsNaN(lower) || math.IsNaN(upper) {
|
||||||
return math.NaN()
|
return math.NaN()
|
||||||
}
|
}
|
||||||
|
@ -434,12 +452,12 @@ func histogramFraction(lower, upper float64, h *histogram.FloatHistogram) float6
|
||||||
// coalesceBuckets merges buckets with the same upper bound.
|
// coalesceBuckets merges buckets with the same upper bound.
|
||||||
//
|
//
|
||||||
// The input buckets must be sorted.
|
// The input buckets must be sorted.
|
||||||
func coalesceBuckets(buckets buckets) buckets {
|
func coalesceBuckets(buckets Buckets) Buckets {
|
||||||
last := buckets[0]
|
last := buckets[0]
|
||||||
i := 0
|
i := 0
|
||||||
for _, b := range buckets[1:] {
|
for _, b := range buckets[1:] {
|
||||||
if b.upperBound == last.upperBound {
|
if b.UpperBound == last.UpperBound {
|
||||||
last.count += b.count
|
last.Count += b.Count
|
||||||
} else {
|
} else {
|
||||||
buckets[i] = last
|
buckets[i] = last
|
||||||
last = b
|
last = b
|
||||||
|
@ -476,11 +494,11 @@ func coalesceBuckets(buckets buckets) buckets {
|
||||||
//
|
//
|
||||||
// We return a bool to indicate if this monotonicity was forced or not, and
|
// We return a bool to indicate if this monotonicity was forced or not, and
|
||||||
// another bool to indicate if small deltas were ignored or not.
|
// another bool to indicate if small deltas were ignored or not.
|
||||||
func ensureMonotonicAndIgnoreSmallDeltas(buckets buckets, tolerance float64) (bool, bool) {
|
func ensureMonotonicAndIgnoreSmallDeltas(buckets Buckets, tolerance float64) (bool, bool) {
|
||||||
var forcedMonotonic, fixedPrecision bool
|
var forcedMonotonic, fixedPrecision bool
|
||||||
prev := buckets[0].count
|
prev := buckets[0].Count
|
||||||
for i := 1; i < len(buckets); i++ {
|
for i := 1; i < len(buckets); i++ {
|
||||||
curr := buckets[i].count // Assumed always positive.
|
curr := buckets[i].Count // Assumed always positive.
|
||||||
if curr == prev {
|
if curr == prev {
|
||||||
// No correction needed if the counts are identical between buckets.
|
// No correction needed if the counts are identical between buckets.
|
||||||
continue
|
continue
|
||||||
|
@ -489,14 +507,14 @@ func ensureMonotonicAndIgnoreSmallDeltas(buckets buckets, tolerance float64) (bo
|
||||||
// Silently correct numerically insignificant differences from floating
|
// Silently correct numerically insignificant differences from floating
|
||||||
// point precision errors, regardless of direction.
|
// point precision errors, regardless of direction.
|
||||||
// Do not update the 'prev' value as we are ignoring the difference.
|
// Do not update the 'prev' value as we are ignoring the difference.
|
||||||
buckets[i].count = prev
|
buckets[i].Count = prev
|
||||||
fixedPrecision = true
|
fixedPrecision = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if curr < prev {
|
if curr < prev {
|
||||||
// Force monotonicity by removing any decreases regardless of magnitude.
|
// Force monotonicity by removing any decreases regardless of magnitude.
|
||||||
// Do not update the 'prev' value as we are ignoring the decrease.
|
// Do not update the 'prev' value as we are ignoring the decrease.
|
||||||
buckets[i].count = prev
|
buckets[i].Count = prev
|
||||||
forcedMonotonic = true
|
forcedMonotonic = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,29 +24,29 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
eps := 1e-12
|
eps := 1e-12
|
||||||
|
|
||||||
for name, tc := range map[string]struct {
|
for name, tc := range map[string]struct {
|
||||||
getInput func() buckets // The buckets can be modified in-place so return a new one each time.
|
getInput func() Buckets // The buckets can be modified in-place so return a new one each time.
|
||||||
expectedForced bool
|
expectedForced bool
|
||||||
expectedFixed bool
|
expectedFixed bool
|
||||||
expectedValues map[float64]float64
|
expectedValues map[float64]float64
|
||||||
}{
|
}{
|
||||||
"simple - monotonic": {
|
"simple - monotonic": {
|
||||||
getInput: func() buckets {
|
getInput: func() Buckets {
|
||||||
return buckets{
|
return Buckets{
|
||||||
{
|
{
|
||||||
upperBound: 10,
|
UpperBound: 10,
|
||||||
count: 10,
|
Count: 10,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 15,
|
UpperBound: 15,
|
||||||
count: 15,
|
Count: 15,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 20,
|
UpperBound: 20,
|
||||||
count: 15,
|
Count: 15,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 30,
|
UpperBound: 30,
|
||||||
count: 15,
|
Count: 15,
|
||||||
}, {
|
}, {
|
||||||
upperBound: math.Inf(1),
|
UpperBound: math.Inf(1),
|
||||||
count: 15,
|
Count: 15,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -60,23 +60,23 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"simple - non-monotonic middle": {
|
"simple - non-monotonic middle": {
|
||||||
getInput: func() buckets {
|
getInput: func() Buckets {
|
||||||
return buckets{
|
return Buckets{
|
||||||
{
|
{
|
||||||
upperBound: 10,
|
UpperBound: 10,
|
||||||
count: 10,
|
Count: 10,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 15,
|
UpperBound: 15,
|
||||||
count: 15,
|
Count: 15,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 20,
|
UpperBound: 20,
|
||||||
count: 15.00000000001, // Simulate the case there's a small imprecision in float64.
|
Count: 15.00000000001, // Simulate the case there's a small imprecision in float64.
|
||||||
}, {
|
}, {
|
||||||
upperBound: 30,
|
UpperBound: 30,
|
||||||
count: 15,
|
Count: 15,
|
||||||
}, {
|
}, {
|
||||||
upperBound: math.Inf(1),
|
UpperBound: math.Inf(1),
|
||||||
count: 15,
|
Count: 15,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -90,41 +90,41 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"real example - monotonic": {
|
"real example - monotonic": {
|
||||||
getInput: func() buckets {
|
getInput: func() Buckets {
|
||||||
return buckets{
|
return Buckets{
|
||||||
{
|
{
|
||||||
upperBound: 1,
|
UpperBound: 1,
|
||||||
count: 6454661.3014166197,
|
Count: 6454661.3014166197,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 5,
|
UpperBound: 5,
|
||||||
count: 8339611.2001912938,
|
Count: 8339611.2001912938,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 10,
|
UpperBound: 10,
|
||||||
count: 14118319.2444762159,
|
Count: 14118319.2444762159,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 25,
|
UpperBound: 25,
|
||||||
count: 14130031.5272856522,
|
Count: 14130031.5272856522,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 50,
|
UpperBound: 50,
|
||||||
count: 46001270.3030008152,
|
Count: 46001270.3030008152,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 64,
|
UpperBound: 64,
|
||||||
count: 46008473.8585563600,
|
Count: 46008473.8585563600,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 80,
|
UpperBound: 80,
|
||||||
count: 46008473.8585563600,
|
Count: 46008473.8585563600,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 100,
|
UpperBound: 100,
|
||||||
count: 46008473.8585563600,
|
Count: 46008473.8585563600,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 250,
|
UpperBound: 250,
|
||||||
count: 46008473.8585563600,
|
Count: 46008473.8585563600,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 1000,
|
UpperBound: 1000,
|
||||||
count: 46008473.8585563600,
|
Count: 46008473.8585563600,
|
||||||
}, {
|
}, {
|
||||||
upperBound: math.Inf(1),
|
UpperBound: math.Inf(1),
|
||||||
count: 46008473.8585563600,
|
Count: 46008473.8585563600,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -138,41 +138,41 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"real example - non-monotonic": {
|
"real example - non-monotonic": {
|
||||||
getInput: func() buckets {
|
getInput: func() Buckets {
|
||||||
return buckets{
|
return Buckets{
|
||||||
{
|
{
|
||||||
upperBound: 1,
|
UpperBound: 1,
|
||||||
count: 6454661.3014166225,
|
Count: 6454661.3014166225,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 5,
|
UpperBound: 5,
|
||||||
count: 8339611.2001912957,
|
Count: 8339611.2001912957,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 10,
|
UpperBound: 10,
|
||||||
count: 14118319.2444762159,
|
Count: 14118319.2444762159,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 25,
|
UpperBound: 25,
|
||||||
count: 14130031.5272856504,
|
Count: 14130031.5272856504,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 50,
|
UpperBound: 50,
|
||||||
count: 46001270.3030008227,
|
Count: 46001270.3030008227,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 64,
|
UpperBound: 64,
|
||||||
count: 46008473.8585563824,
|
Count: 46008473.8585563824,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 80,
|
UpperBound: 80,
|
||||||
count: 46008473.8585563898,
|
Count: 46008473.8585563898,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 100,
|
UpperBound: 100,
|
||||||
count: 46008473.8585563824,
|
Count: 46008473.8585563824,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 250,
|
UpperBound: 250,
|
||||||
count: 46008473.8585563824,
|
Count: 46008473.8585563824,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 1000,
|
UpperBound: 1000,
|
||||||
count: 46008473.8585563898,
|
Count: 46008473.8585563898,
|
||||||
}, {
|
}, {
|
||||||
upperBound: math.Inf(1),
|
UpperBound: math.Inf(1),
|
||||||
count: 46008473.8585563824,
|
Count: 46008473.8585563824,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -186,53 +186,53 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"real example 2 - monotonic": {
|
"real example 2 - monotonic": {
|
||||||
getInput: func() buckets {
|
getInput: func() Buckets {
|
||||||
return buckets{
|
return Buckets{
|
||||||
{
|
{
|
||||||
upperBound: 0.005,
|
UpperBound: 0.005,
|
||||||
count: 9.6,
|
Count: 9.6,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.01,
|
UpperBound: 0.01,
|
||||||
count: 9.688888889,
|
Count: 9.688888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.025,
|
UpperBound: 0.025,
|
||||||
count: 9.755555556,
|
Count: 9.755555556,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.05,
|
UpperBound: 0.05,
|
||||||
count: 9.844444444,
|
Count: 9.844444444,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.1,
|
UpperBound: 0.1,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.25,
|
UpperBound: 0.25,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.5,
|
UpperBound: 0.5,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 1,
|
UpperBound: 1,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 2.5,
|
UpperBound: 2.5,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 5,
|
UpperBound: 5,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 10,
|
UpperBound: 10,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 25,
|
UpperBound: 25,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 50,
|
UpperBound: 50,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 100,
|
UpperBound: 100,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: math.Inf(1),
|
UpperBound: math.Inf(1),
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -246,53 +246,53 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"real example 2 - non-monotonic": {
|
"real example 2 - non-monotonic": {
|
||||||
getInput: func() buckets {
|
getInput: func() Buckets {
|
||||||
return buckets{
|
return Buckets{
|
||||||
{
|
{
|
||||||
upperBound: 0.005,
|
UpperBound: 0.005,
|
||||||
count: 9.6,
|
Count: 9.6,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.01,
|
UpperBound: 0.01,
|
||||||
count: 9.688888889,
|
Count: 9.688888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.025,
|
UpperBound: 0.025,
|
||||||
count: 9.755555556,
|
Count: 9.755555556,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.05,
|
UpperBound: 0.05,
|
||||||
count: 9.844444444,
|
Count: 9.844444444,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.1,
|
UpperBound: 0.1,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.25,
|
UpperBound: 0.25,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 0.5,
|
UpperBound: 0.5,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 1,
|
UpperBound: 1,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 2.5,
|
UpperBound: 2.5,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 5,
|
UpperBound: 5,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 10,
|
UpperBound: 10,
|
||||||
count: 9.888888889001, // Simulate the case there's a small imprecision in float64.
|
Count: 9.888888889001, // Simulate the case there's a small imprecision in float64.
|
||||||
}, {
|
}, {
|
||||||
upperBound: 25,
|
UpperBound: 25,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: 50,
|
UpperBound: 50,
|
||||||
count: 9.888888888999, // Simulate the case there's a small imprecision in float64.
|
Count: 9.888888888999, // Simulate the case there's a small imprecision in float64.
|
||||||
}, {
|
}, {
|
||||||
upperBound: 100,
|
UpperBound: 100,
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
}, {
|
}, {
|
||||||
upperBound: math.Inf(1),
|
UpperBound: math.Inf(1),
|
||||||
count: 9.888888889,
|
Count: 9.888888889,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -308,7 +308,7 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
for q, v := range tc.expectedValues {
|
for q, v := range tc.expectedValues {
|
||||||
res, forced, fixed := bucketQuantile(q, tc.getInput())
|
res, forced, fixed := BucketQuantile(q, tc.getInput())
|
||||||
require.Equal(t, tc.expectedForced, forced)
|
require.Equal(t, tc.expectedForced, forced)
|
||||||
require.Equal(t, tc.expectedFixed, fixed)
|
require.Equal(t, tc.expectedFixed, fixed)
|
||||||
require.InEpsilon(t, v, res, eps)
|
require.InEpsilon(t, v, res, eps)
|
||||||
|
|
Loading…
Reference in a new issue