Skip to content

Commit 726bf06

Browse files
author
Christopher M. Wolff
authoredFeb 23, 2023
feat: add parameter to histogramQuantile (#5386)
* feat: add parameter to histogramQuantile * chore: make fmt * feat: implement new parameter for histogramQuantile * chore: update interpreter_test.go
1 parent 0cbec3e commit 726bf06

File tree

7 files changed

+285
-25
lines changed

7 files changed

+285
-25
lines changed
 

‎interpreter/interpreter_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -883,8 +883,8 @@ func TestStack(t *testing.T) {
883883
FunctionName: "window",
884884
Location: ast.SourceLocation{
885885
File: "universe/universe.flux",
886-
Start: ast.Position{Line: 3895, Column: 12},
887-
End: ast.Position{Line: 3895, Column: 51},
886+
Start: ast.Position{Line: 3904, Column: 12},
887+
End: ast.Position{Line: 3904, Column: 51},
888888
Source: `window(every: inf, timeColumn: timeDst)`,
889889
},
890890
},

‎libflux/go/libflux/buildinfo.gen.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ var sourceHashes = map[string]string{
214214
"stdlib/experimental/polyline/polyline.flux": "09f8d405349236de713fef7639d5523b107401bbe349789924f2283296205b9b",
215215
"stdlib/experimental/polyline/polyline_test.flux": "16473dce4f71dcdbe1e3f90b350ab1e56a513679cb5c3f872e0f2d7678d42d1f",
216216
"stdlib/experimental/preview_test.flux": "cca570d25b17ed201a0ecc7ebf9e547ccff2aa0814a3ac49f12faa938cbdaf73",
217-
"stdlib/experimental/prometheus/prometheus.flux": "e0b3df509c8f522edee1081e5e3907ce9628f783d33b47c2e2d8ffb766f3f948",
218-
"stdlib/experimental/prometheus/prometheus_histogramQuantile_test.flux": "37bda52e9440820c3bb278b4e9d331c0330a68f405a7623b95e15a50ee176753",
217+
"stdlib/experimental/prometheus/prometheus.flux": "dc01322d3c0655661a8c2279c69acf50d02791a670fb0d681947af6726bc2f4d",
218+
"stdlib/experimental/prometheus/prometheus_histogramQuantile_test.flux": "15da11b9c9d43cbc1c579de7064d7f3870b1c77b610ce3c567e8eb14f40ee090",
219219
"stdlib/experimental/quantile_test.flux": "e3cf2ee9716c1179139d0a388c24b24f1c25ca329b27bebaeb53b7e1b608ead2",
220220
"stdlib/experimental/query/from.flux": "713b7feb6904d64cf4cbd235396ff6ff20e1eb96578190c0ac3be4f37df7c362",
221221
"stdlib/experimental/record/record.flux": "273eebb2ee5cb9b153940ca0f42ed5b97b3d86de07d91c96a75508682d84feae",
@@ -492,7 +492,7 @@ var sourceHashes = map[string]string{
492492
"stdlib/universe/highestAverage_test.flux": "65744d16b7c5d8ac2f4e3c47621195de1840ad72b12bfbb692d4f645e71a00a8",
493493
"stdlib/universe/highestCurrent_test.flux": "c285ff40a7d8789d2c20bcf90d8063b710499ddc24621d627f6693c30351ebe5",
494494
"stdlib/universe/highestMax_test.flux": "fff9f21422d2607afb9ff2786d67d173c7cd797418eb7b3521f8cf7e49443b88",
495-
"stdlib/universe/histogram_quantile_test.flux": "7dcd4549a44f6a2889a445ae8c1adbb07a99747358b1d400d39b68d5c4233551",
495+
"stdlib/universe/histogram_quantile_test.flux": "3ce3d5e6ce27fd8bb2e84da031faca2dafef2566737842e534fbd667ffa8a4f6",
496496
"stdlib/universe/histogram_test.flux": "e9e9775f80ac7c2a76044e6e0e8a89045d736c6ab618e8de6d7d1ebe9848938e",
497497
"stdlib/universe/holt_winters_panic_test.flux": "204eb8044d634e5350a364eac466eb51e7f549e4ac7f454de7b244ba272b248f",
498498
"stdlib/universe/holt_winters_test.flux": "9bc8441527867b6c075d003034a3def1748766df478ba8b434e2a2297ead0ec0",
@@ -609,7 +609,7 @@ var sourceHashes = map[string]string{
609609
"stdlib/universe/union_heterogeneous_test.flux": "7d8b47b3e96b859a5fed5985c051e2a3fdc947d3d6ff9cc104e40821581fb0cb",
610610
"stdlib/universe/union_test.flux": "f008260d48db70212ce64d3f51f4cf031532a9a67c1ba43242dbc4d43ef31293",
611611
"stdlib/universe/unique_test.flux": "c108ab7b0e4b0b77f0a320c8c4dacb8cfbccae8b389425754e9583e69cd64ee3",
612-
"stdlib/universe/universe.flux": "060426aa8c8caf89187489f3bbd077cdba9efa389d76a09d3346636b4ba35e61",
612+
"stdlib/universe/universe.flux": "8f3b92accba41d660339bcd6e2f83e7a0d650cab637630e320ba87b5cc2f2335",
613613
"stdlib/universe/universe_truncateTimeColumn_test.flux": "8acb700c612e9eba87c0525b33fd1f0528e6139cc912ed844932caef25d37b56",
614614
"stdlib/universe/window_aggregate_test.flux": "c8f66f7ee188bb2e979e5a8b526057b653922197ae441658f7c7f11251c96576",
615615
"stdlib/universe/window_default_start_align_test.flux": "0aaf612796fbb5ac421579151ad32a8861f4494a314ea615d0ccedd18067b980",

‎stdlib/experimental/prometheus/prometheus.flux

+15-7
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ builtin scrape : (url: string) => stream[A] where A: Record
4646
// Available versions are `1` and `2`.
4747
// Default is `2`.
4848
// - tables: Input data. Default is piped-forward data (`<-`).
49+
// - onNonmonotonic: Describes behavior when counts are not monotonically increasing
50+
// when sorted by upper bound. Default is `error`.
51+
//
52+
// **Supported values**:
53+
// - **error**: Produce an error.
54+
// - **force**: Force bin counts to be monotonic by adding to each bin such that it
55+
// is equal to the next smaller bin.
56+
// - **drop**: When a nonmonotonic table is encountered, produce no output.
4957
//
5058
// ## Examples
5159
//
@@ -72,31 +80,31 @@ builtin scrape : (url: string) => stream[A] where A: Record
7280
// ## Metadata
7381
// tags: transformations,aggregates,prometheus
7482
//
75-
histogramQuantile = (tables=<-, quantile, metricVersion=2) => {
76-
_version2 = () =>
83+
histogramQuantile = (tables=<-, quantile, metricVersion=2, onNonmonotonic="error") => {
84+
_version2 = (onNonmonotonic) =>
7785
tables
7886
|> group(mode: "except", columns: ["le", "_value"])
7987
|> map(fn: (r) => ({r with le: float(v: r.le)}))
80-
|> universe.histogramQuantile(quantile: quantile)
88+
|> universe.histogramQuantile(quantile: quantile, onNonmonotonic: onNonmonotonic)
8189
|> group(mode: "except", columns: ["le", "_value", "_time"])
8290
|> set(key: "quantile", value: string(v: quantile))
8391
|> experimental.group(columns: ["quantile"], mode: "extend")
8492

85-
_version1 = () =>
93+
_version1 = (onNonmonotonic) =>
8694
tables
8795
|> filter(fn: (r) => r._field != "sum" and r._field != "count")
8896
|> map(fn: (r) => ({r with le: float(v: r._field)}))
8997
|> group(mode: "except", columns: ["_field", "le", "_value"])
90-
|> universe.histogramQuantile(quantile: quantile)
98+
|> universe.histogramQuantile(quantile: quantile, onNonmonotonic: onNonmonotonic)
9199
|> group(mode: "except", columns: ["le", "_value", "_time"])
92100
|> set(key: "quantile", value: string(v: quantile))
93101
|> experimental.group(columns: ["quantile"], mode: "extend")
94102

95103
output =
96104
if metricVersion == 2 then
97-
_version2()
105+
_version2(onNonmonotonic)
98106
else if metricVersion == 1 then
99-
_version1()
107+
_version1(onNonmonotonic)
100108
else
101109
universe.die(msg: "Invalid metricVersion. Available versions are 1 and 2.")
102110

‎stdlib/experimental/prometheus/prometheus_histogramQuantile_test.flux

+83
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,86 @@ testcase prometheus_histogramQuantile_v1 {
292292

293293
testing.diff(got: got, want: want)
294294
}
295+
296+
testcase prometheus_histogramQuantile_onNonmonotonicForce {
297+
inData =
298+
"#group,false,false,true,true,false,false,true,true
299+
#datatype,string,long,string,string,dateTime:RFC3339,double,string,string
300+
#default,_result,,,,,,,
301+
,result,table,_field,_measurement,_time,_value,le,org
302+
,,0,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,0,0.001,0001
303+
,,2,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,1,0.005,0001
304+
,,3,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,1,0.025,0001
305+
,,4,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,3,0.125,0001
306+
,,4,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,2,0.625,0001
307+
,,5,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,7,3.125,0001
308+
,,6,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,10,15.625,0001
309+
,,7,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00.412729Z,10,+Inf,0001
310+
"
311+
outData =
312+
"#group,false,false,true,true,true,true,false,true,false,true
313+
#datatype,string,long,string,string,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,string,double,string
314+
#default,_result,,,,,,,,,
315+
,result,table,_field,_measurement,_start,_stop,_time,org,_value,quantile
316+
,,0,qc_all_duration_seconds,prometheus,2021-10-08T00:00:00Z,2021-10-08T00:01:00Z,2021-10-08T00:00:00.412729Z,0001,15.208333333333336,0.99
317+
"
318+
want = csv.from(csv: outData)
319+
got =
320+
csv.from(csv: inData)
321+
|> range(start: 2021-10-08T00:00:00Z, stop: 2021-10-08T00:01:00Z)
322+
|> prometheus.histogramQuantile(
323+
quantile: 0.99,
324+
metricVersion: 2,
325+
onNonmonotonic: "force",
326+
)
327+
328+
testing.diff(got: got, want: want)
329+
}
330+
331+
testcase prometheus_histogramQuantile_onNonmonotonicDrop {
332+
// Data for org 0002 is not monotonic
333+
inData =
334+
"#group,false,false,true,true,false,false,true
335+
#datatype,string,long,string,string,dateTime:RFC3339,double,string
336+
#default,_result,,,,,,
337+
,result,table,_field,_measurement,_time,_value,org
338+
,,0,+Inf,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,10,0001
339+
,,1,+Inf,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1405,0002
340+
,,2,0.001,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,0,0001
341+
,,3,0.001,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1,0002
342+
,,4,0.005,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,0,0001
343+
,,5,0.005,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1,0002
344+
,,6,0.025,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,0,0001
345+
,,7,0.025,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,84,0002
346+
,,8,0.125,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,0,0001
347+
,,9,0.125,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,83,0002
348+
,,10,0.625,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,0,0001
349+
,,11,0.625,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1373,0002
350+
,,12,15.625,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,10,0001
351+
,,13,15.625,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1405,0002
352+
,,14,3.125,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,0,0001
353+
,,15,3.125,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1401,0002
354+
,,16,count,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,10,0001
355+
,,17,count,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,1405,0002
356+
,,18,sum,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,45.746700925,0001
357+
,,19,sum,qc_all_duration_seconds,2021-10-08T00:00:01.866064Z,178.4667627259998,0002
358+
"
359+
outData =
360+
"#group,false,false,true,true,true,false,true,false,true
361+
#datatype,string,long,string,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,string,double,string
362+
#default,_result,,,,,,,,
363+
,result,table,_measurement,_start,_stop,_time,org,_value,quantile
364+
,,0,qc_all_duration_seconds,2021-10-08T00:00:00Z,2021-10-08T00:01:00Z,2021-10-08T00:00:01.866064Z,0001,15.5,0.99
365+
"
366+
want = csv.from(csv: outData)
367+
got =
368+
csv.from(csv: inData)
369+
|> range(start: 2021-10-08T00:00:00Z, stop: 2021-10-08T00:01:00Z)
370+
|> prometheus.histogramQuantile(
371+
quantile: 0.99,
372+
metricVersion: 1,
373+
onNonmonotonic: "drop",
374+
)
375+
376+
testing.diff(got: got, want: want)
377+
}

‎stdlib/universe/histogram_quantile.go

+46-12
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,23 @@ import (
1212
"github.com/influxdata/flux/runtime"
1313
)
1414

15-
const HistogramQuantileKind = "histogramQuantile"
15+
const (
16+
HistogramQuantileKind = "histogramQuantile"
1617

17-
const DefaultUpperBoundColumnLabel = "le"
18+
DefaultUpperBoundColumnLabel = "le"
19+
20+
onNonmonotonicError = "error"
21+
onNonmonotonicDrop = "drop"
22+
onNonmonotonicForce = "force"
23+
)
1824

1925
type HistogramQuantileOpSpec struct {
2026
Quantile float64 `json:"quantile"`
2127
CountColumn string `json:"countColumn"`
2228
UpperBoundColumn string `json:"upperBoundColumn"`
2329
ValueColumn string `json:"valueColumn"`
2430
MinValue float64 `json:"minValue"`
31+
OnNonmonotonic string `json:"onNonmonotonic"`
2532
}
2633

2734
func init() {
@@ -73,6 +80,18 @@ func CreateHistogramQuantileOpSpec(args flux.Arguments, a *flux.Administration)
7380
s.MinValue = min
7481
}
7582

83+
if onNonmonotonic, ok, err := args.GetString("onNonmonotonic"); err != nil {
84+
return nil, err
85+
} else if ok {
86+
s.OnNonmonotonic = onNonmonotonic
87+
} else {
88+
s.OnNonmonotonic = onNonmonotonicError
89+
}
90+
91+
if s.OnNonmonotonic != onNonmonotonicError && s.OnNonmonotonic != onNonmonotonicForce && s.OnNonmonotonic != onNonmonotonicDrop {
92+
return nil, errors.Newf(codes.Invalid, "value provided to histogramQuantile parameter onNonmonotonic is invalid; must be one of %q, %q or %q", onNonmonotonicError, onNonmonotonicForce, onNonmonotonicDrop)
93+
}
94+
7695
return s, nil
7796
}
7897

@@ -87,6 +106,7 @@ type HistogramQuantileProcedureSpec struct {
87106
UpperBoundColumn string `json:"upperBoundColumn"`
88107
ValueColumn string `json:"valueColumn"`
89108
MinValue float64 `json:"minValue"`
109+
OnNonmonotonic string `json:"onNonmonotonic"`
90110
}
91111

92112
func newHistogramQuantileProcedure(qs flux.OperationSpec, a plan.Administration) (plan.ProcedureSpec, error) {
@@ -100,6 +120,7 @@ func newHistogramQuantileProcedure(qs flux.OperationSpec, a plan.Administration)
100120
UpperBoundColumn: spec.UpperBoundColumn,
101121
ValueColumn: spec.ValueColumn,
102122
MinValue: spec.MinValue,
123+
OnNonmonotonic: spec.OnNonmonotonic,
103124
}, nil
104125
}
105126

@@ -230,10 +251,13 @@ func (t histogramQuantileTransformation) Process(id execute.DatasetID, tbl flux.
230251
})
231252
}
232253

233-
q, err := t.computeQuantile(cdf)
254+
q, ok, err := t.computeQuantile(cdf)
234255
if err != nil {
235256
return err
236257
}
258+
if !ok {
259+
return nil
260+
}
237261
if err := execute.AppendKeyValues(tbl.Key(), builder); err != nil {
238262
return err
239263
}
@@ -243,9 +267,9 @@ func (t histogramQuantileTransformation) Process(id execute.DatasetID, tbl flux.
243267
return nil
244268
}
245269

246-
func (t *histogramQuantileTransformation) computeQuantile(cdf []bucket) (float64, error) {
270+
func (t *histogramQuantileTransformation) computeQuantile(cdf []bucket) (float64, bool, error) {
247271
if len(cdf) == 0 {
248-
return 0, errors.New(codes.FailedPrecondition, "histogram is empty")
272+
return 0, false, errors.New(codes.FailedPrecondition, "histogram is empty")
249273
}
250274
// Find rank index and check counts are monotonic
251275
prevCount := 0.0
@@ -254,9 +278,19 @@ func (t *histogramQuantileTransformation) computeQuantile(cdf []bucket) (float64
254278
rankIdx := -1
255279
for i, b := range cdf {
256280
if b.count < prevCount {
257-
return 0, errors.New(codes.FailedPrecondition, "histogram records counts are not monotonic")
281+
switch t.spec.OnNonmonotonic {
282+
case onNonmonotonicError:
283+
return 0, false, errors.New(codes.FailedPrecondition, "histogram records counts are not monotonic")
284+
case onNonmonotonicForce:
285+
b.count = prevCount
286+
case onNonmonotonicDrop:
287+
return 0, false, nil
288+
default:
289+
return 0, false, errors.Newf(codes.Internal, "unknown value for onNonmonotonic: %q", t.spec.OnNonmonotonic)
290+
}
291+
} else {
292+
prevCount = b.count
258293
}
259-
prevCount = b.count
260294

261295
if rank >= b.count {
262296
rankIdx = i
@@ -277,7 +311,7 @@ func (t *histogramQuantileTransformation) computeQuantile(cdf []bucket) (float64
277311
upperBound = cdf[0].upperBound
278312
case len(cdf) - 1:
279313
// Quantile is above the highest upper bound, simply return it as it must be finite
280-
return cdf[len(cdf)-1].upperBound, nil
314+
return cdf[len(cdf)-1].upperBound, true, nil
281315
default:
282316
lowerCount = cdf[rankIdx].count
283317
lowerBound = cdf[rankIdx].upperBound
@@ -286,19 +320,19 @@ func (t *histogramQuantileTransformation) computeQuantile(cdf []bucket) (float64
286320
}
287321
if rank == lowerCount {
288322
// No need to interpolate
289-
return lowerBound, nil
323+
return lowerBound, true, nil
290324
}
291325
if math.IsInf(lowerBound, -1) {
292326
// We cannot interpolate with infinity
293-
return upperBound, nil
327+
return upperBound, true, nil
294328
}
295329
if math.IsInf(upperBound, 1) {
296330
// We cannot interpolate with infinity
297-
return lowerBound, nil
331+
return lowerBound, true, nil
298332
}
299333
// Compute quantile using linear interpolation
300334
scale := (rank - lowerCount) / (upperCount - lowerCount)
301-
return lowerBound + (upperBound-lowerBound)*scale, nil
335+
return lowerBound + (upperBound-lowerBound)*scale, true, nil
302336
}
303337

304338
func (t histogramQuantileTransformation) UpdateWatermark(id execute.DatasetID, mark execute.Time) error {

‎stdlib/universe/histogram_quantile_test.flux

+126
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,129 @@ testcase histogram_quantile_minvalue {
8888

8989
testing.diff(got, want)
9090
}
91+
92+
testcase histogramQuantileInvalidOnNonmonotonic {
93+
inData =
94+
"
95+
#datatype,string,long,dateTime:RFC3339,string,double,double,string
96+
#group,false,false,true,true,false,false,true
97+
#default,_result,,,,,,
98+
,result,table,_time,_field,_value,le,_measurement
99+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,-80,mm
100+
"
101+
fn = () =>
102+
csv.from(csv: inData)
103+
|> range(start: 2018-05-22T19:53:00Z)
104+
|> histogramQuantile(quantile: 0.25, minValue: -100.0, onNonmonotonic: "asdf")
105+
106+
testing.shouldError(
107+
fn: fn,
108+
want: /value provided to histogramQuantile parameter onNonmonotonic is invalid/,
109+
)
110+
}
111+
112+
testcase histogramQuantileOnNonmonotonicError {
113+
inData =
114+
"
115+
#datatype,string,long,dateTime:RFC3339,string,double,double,string
116+
#group,false,false,true,true,false,false,true
117+
#default,_result,,,,,,
118+
,result,table,_time,_field,_value,le,_measurement
119+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.1,l
120+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.2,l
121+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.3,l
122+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,3,0.4,l
123+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.5,l
124+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.6,l
125+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.7,l
126+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,8,0.8,l
127+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,0.9,l
128+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,+Inf,l
129+
"
130+
fn = () =>
131+
csv.from(csv: inData)
132+
|> histogramQuantile(quantile: 0.9)
133+
|> tableFind(fn: (key) => true)
134+
|> findRecord(fn: (key) => true, idx: 0)
135+
136+
testing.shouldError(fn: fn, want: /histogram records counts are not monotonic/)
137+
}
138+
139+
testcase histogramQuantileOnNonmonotonicForce {
140+
inData =
141+
"
142+
#datatype,string,long,dateTime:RFC3339,string,double,double,string
143+
#group,false,false,true,true,false,false,true
144+
#default,_result,,,,,,
145+
,result,table,_time,_field,_value,le,_measurement
146+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.1,l
147+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.2,l
148+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.3,l
149+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,0,0.4,l
150+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.5,l
151+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.6,l
152+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.7,l
153+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,8,0.8,l
154+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,0.9,l
155+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,+Inf,l
156+
"
157+
outData =
158+
"
159+
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,string,double,string
160+
#group,false,false,true,true,true,true,false,true
161+
#default,_result,,,,,,,
162+
,result,table,_start,_stop,_time,_field,_value,_measurement
163+
,,0,2018-05-22T19:53:00Z,2030-01-01T00:00:00Z,2018-05-22T19:53:00Z,x_duration_seconds,0.8500000000000001,l
164+
"
165+
166+
got =
167+
csv.from(csv: inData)
168+
|> range(start: 2018-05-22T19:53:00Z)
169+
|> histogramQuantile(quantile: 0.9, onNonmonotonic: "force")
170+
want = csv.from(csv: outData)
171+
172+
testing.diff(got, want)
173+
}
174+
175+
testcase histogramQuantileOnNonmonotonicDrop {
176+
inData =
177+
"
178+
#datatype,string,long,dateTime:RFC3339,string,double,double,string
179+
#group,false,false,true,true,false,false,true
180+
#default,_result,,,,,,
181+
,result,table,_time,_field,_value,le,_measurement
182+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.1,l
183+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.2,l
184+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.3,l
185+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,0,0.4,l
186+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.5,l
187+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,1,0.6,l
188+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,2,0.7,l
189+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,8,0.8,l
190+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,0.9,l
191+
,,0,2018-05-22T19:53:00Z,x_duration_seconds,10,+Inf,l
192+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,0,-Inf,l
193+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,10,0.2,l
194+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,15,0.4,l
195+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,25,0.6,l
196+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,35,0.8,l
197+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,45,1,l
198+
,,1,2018-05-22T19:53:00Z,y_duration_seconds,45,+Inf,l
199+
"
200+
outData =
201+
"
202+
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,string,double,string
203+
#group,false,false,true,true,true,true,false,true
204+
#default,_result,,,,,,,
205+
,result,table,_start,_stop,_time,_field,_value,_measurement
206+
,,1,2018-05-22T19:53:00Z,2030-01-01T00:00:00Z,2018-05-22T19:53:00Z,y_duration_seconds,0.91,l
207+
"
208+
209+
got =
210+
csv.from(csv: inData)
211+
|> range(start: 2018-05-22T19:53:00Z)
212+
|> histogramQuantile(quantile: 0.9, onNonmonotonic: "drop")
213+
want = csv.from(csv: outData)
214+
215+
testing.diff(got, want)
216+
}

‎stdlib/universe/universe.flux

+9
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,14 @@ builtin histogram : (
850850
// Default is `le`.
851851
// - valueColumn: Column to store the computed quantile in. Default is `_value.
852852
// - minValue: Assumed minimum value of the dataset. Default is `0.0`.
853+
// - onNonmonotonic: Describes behavior when counts are not monotonically increasing
854+
// when sorted by upper bound. Default is `error`.
855+
//
856+
// **Supported values**:
857+
// - **error**: Produce an error.
858+
// - **force**: Force bin counts to be monotonic by adding to each bin such that it
859+
// is equal to the next smaller bin.
860+
// - **drop**: When a nonmonotonic table is encountered, produce no output.
853861
//
854862
// If the quantile falls below the lowest upper bound, interpolation is
855863
// performed between `minValue` and the lowest upper bound.
@@ -880,6 +888,7 @@ builtin histogramQuantile : (
880888
?upperBoundColumn: string,
881889
?valueColumn: string,
882890
?minValue: float,
891+
?onNonmonotonic: string,
883892
) => stream[B]
884893
where
885894
A: Record,

0 commit comments

Comments
 (0)
Please sign in to comment.