diff --git a/promql/functions.go b/promql/functions.go index 6505cab470..23b0bbed45 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -478,6 +478,51 @@ func funcSumOverTime(ev *evaluator, args Expressions) model.Value { }) } +// === quantile_over_time(matrix model.ValMatrix) Vector === +func funcQuantileOverTime(ev *evaluator, args Expressions) model.Value { + q := ev.evalFloat(args[0]) + mat := ev.evalMatrix(args[1]) + resultVector := vector{} + + for _, el := range mat { + if len(el.Values) == 0 { + continue + } + + el.Metric.Del(model.MetricNameLabel) + var result float64 + if q < 0 { + result = math.Inf(-1) + } else if q > 1 { + result = math.Inf(+1) + } else { + values := make([]float64, 0, len(el.Values)) + for _, v := range el.Values { + values = append(values, float64(v.Value)) + } + sort.Float64s(values) + + n := float64(len(el.Values)) + // When the quantile lies between two samples, + // we use a weighted average of the two samples. + rank := q * (n-1) + + lowerIndex := math.Max(0, math.Floor(rank)) + upperIndex := math.Min(n-1, lowerIndex+1) + + weight := rank - math.Floor(rank) + + result = values[int(lowerIndex)] * (1 - weight) + values[int(upperIndex)] * weight + } + resultVector = append(resultVector, &sample{ + Metric: el.Metric, + Value: model.SampleValue(result), + Timestamp: ev.Timestamp, + }) + } + return resultVector +} + // === stddev_over_time(matrix model.ValMatrix) Vector === func funcStddevOverTime(ev *evaluator, args Expressions) model.Value { return aggrOverTime(ev, args, func(values []model.SamplePair) model.SampleValue { @@ -504,7 +549,7 @@ func funcStdvarOverTime(ev *evaluator, args Expressions) model.Value { avg := sum / count return squaredSum/count - avg*avg }) -} + // === abs(vector model.ValVector) Vector === func funcAbs(ev *evaluator, args Expressions) model.Value { @@ -973,6 +1018,12 @@ var functions = map[string]*Function{ ReturnType: model.ValVector, Call: funcPredictLinear, }, + "quantile_over_time": { + Name: "quantile_over_time", + ArgTypes: []model.ValueType{model.ValScalar, model.ValMatrix}, + ReturnType: model.ValVector, + Call: funcQuantileOverTime, + }, "rate": { Name: "rate", ArgTypes: []model.ValueType{model.ValMatrix}, diff --git a/promql/testdata/functions.test b/promql/testdata/functions.test index 4b73e2b8c6..9a6a360a76 100644 --- a/promql/testdata/functions.test +++ b/promql/testdata/functions.test @@ -276,7 +276,6 @@ eval instant at 8000s holt_winters(http_requests[1m], 0.01, 0.1) {job="api-server", instance="0", group="canary"} 24000 {job="api-server", instance="1", group="canary"} -32000 - # Tests for stddev_over_time and stdvar_over_time. clear load 10s @@ -287,3 +286,47 @@ eval instant at 1m stdvar_over_time(metric[1m]) eval instant at 1m stddev_over_time(metric[1m]) {} 3.249615 + +# Tests for quantile_over_time +clear + +load 10s + data{test="two samples"} 0 1 + data{test="three samples"} 0 1 2 + data{test="uneven samples"} 0 1 4 + +eval instant at 1m quantile_over_time(0, data[1m]) + {test="two samples"} 0 + {test="three samples"} 0 + {test="uneven samples"} 0 + +eval instant at 1m quantile_over_time(0.5, data[1m]) + {test="two samples"} 0.5 + {test="three samples"} 1 + {test="uneven samples"} 1 + +eval instant at 1m quantile_over_time(0.75, data[1m]) + {test="two samples"} 0.75 + {test="three samples"} 1.5 + {test="uneven samples"} 2.5 + +eval instant at 1m quantile_over_time(0.8, data[1m]) + {test="two samples"} 0.8 + {test="three samples"} 1.6 + {test="uneven samples"} 2.8 + +eval instant at 1m quantile_over_time(1, data[1m]) + {test="two samples"} 1 + {test="three samples"} 2 + {test="uneven samples"} 4 + +eval instant at 1m quantile_over_time(-1, data[1m]) + {test="two samples"} -Inf + {test="three samples"} -Inf + {test="uneven samples"} -Inf + +eval instant at 1m quantile_over_time(2, data[1m]) + {test="two samples"} +Inf + {test="three samples"} +Inf + {test="uneven samples"} +Inf +>>>>>>> Add quantile_over_time function