Feature/mathandreduce (#41608)

* Added new math functions: round, ceil, floor 
* Added a new reduce function: last.
This commit is contained in:
Marius Bezuidenhout 2022-01-21 20:15:50 +02:00 committed by GitHub
parent 5d0bc9e933
commit 9fc0aee02b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 1 deletions

View File

@ -128,6 +128,18 @@ Log returns the natural logarithm of of its argument which can be a number or a
The inf, infn, nan, and null functions all return a single value of the name. They primarily exist for testing. Example: `null()`.
##### round
Round returns a rounded integer value. For example, `round(3.123)` or `round($A)`. (This function should probably take an argument so it can add precision to the rounded value).
##### ceil
Ceil rounds the number up to the nearest integer value. For example, `ceil(3.123)` returns 4.
##### floor
Floor rounds the number down to the nearest integer value. For example, `floor(3.123)` returns 3.
### Reduce
Reduce takes one or more time series returned from a query or an expression and turns each series into a single number. The labels of the time series are kept as labels on each outputted reduced number.
@ -157,6 +169,10 @@ Min and Max return the smallest or largest value in the series respectively. If
Sum returns the total of all values in the series. If series is of zero length, the sum will be 0. If there are any NaN or Null values in the series, NaN is returned.
#### Last
Last returns the last number in the series. If the series has no values then returns NaN.
### Resample
Resample changes the time stamps in each time series to have a consistent time interval. The main use case is so you can resample time series that do not share the same timestamps so math can be performed between them. This can be done by resample each of the two series, and then in a Math operation referencing the resampled variables.

View File

@ -53,6 +53,21 @@ var builtins = map[string]parse.Func{
VariantReturn: true,
F: isNumber,
},
"round": {
Args: []parse.ReturnType{parse.TypeVariantSet},
VariantReturn: true,
F: round,
},
"ceil": {
Args: []parse.ReturnType{parse.TypeVariantSet},
VariantReturn: true,
F: ceil,
},
"floor": {
Args: []parse.ReturnType{parse.TypeVariantSet},
VariantReturn: true,
F: floor,
},
}
// abs returns the absolute value for each result in NumberSet, SeriesSet, or Scalar
@ -253,3 +268,42 @@ func perNullableFloat(e *State, val Value, floatF func(x *float64) *float64) (Va
return newVal, nil
}
// round returns the rounded value for each result in NumberSet, SeriesSet, or Scalar
func round(e *State, varSet Results) (Results, error) {
newRes := Results{}
for _, res := range varSet.Values {
newVal, err := perFloat(e, res, math.Round)
if err != nil {
return newRes, err
}
newRes.Values = append(newRes.Values, newVal)
}
return newRes, nil
}
// ceil returns the rounded up value for each result in NumberSet, SeriesSet, or Scalar
func ceil(e *State, varSet Results) (Results, error) {
newRes := Results{}
for _, res := range varSet.Values {
newVal, err := perFloat(e, res, math.Ceil)
if err != nil {
return newRes, err
}
newRes.Values = append(newRes.Values, newVal)
}
return newRes, nil
}
// floor returns the rounded down value for each result in NumberSet, SeriesSet, or Scalar
func floor(e *State, varSet Results) (Results, error) {
newRes := Results{}
for _, res := range varSet.Values {
newVal, err := perFloat(e, res, math.Floor)
if err != nil {
return newRes, err
}
newRes.Values = append(newRes.Values, newVal)
}
return newRes, nil
}

View File

@ -69,6 +69,17 @@ func Count(fv *Float64Field) *float64 {
return &f
}
func Last(fv *Float64Field) *float64 {
var f float64
if fv.Len() == 0 {
f = math.NaN()
return &f
}
v := fv.GetValue(fv.Len() - 1)
f = *v
return &f
}
// Reduce turns the Series into a Number based on the given reduction function
func (s Series) Reduce(refID, rFunc string) (Number, error) {
var l data.Labels
@ -90,6 +101,8 @@ func (s Series) Reduce(refID, rFunc string) (Number, error) {
f = Max(&floatField)
case "count":
f = Count(&floatField)
case "last":
f = Last(&floatField)
default:
return number, fmt.Errorf("reduction %v not implemented", rFunc)
}

View File

@ -214,6 +214,19 @@ func TestSeriesReduce(t *testing.T) {
},
},
},
{
name: "last empty series",
red: "last",
varToReduce: "A",
vars: seriesEmpty,
errIs: require.NoError,
resultsIs: require.Equal,
results: Results{
[]Value{
makeNumber("", nil, NaN),
},
},
},
}
for _, tt := range tests {

View File

@ -12,7 +12,7 @@ interface Props {
const mathPlaceholder =
'Math operations on one more queries, you reference the query by ${refId} ie. $A, $B, $C etc\n' +
'Example: $A + $B\n' +
'Available functions: abs(), log(), is_number(), is_inf(), is_nan(), is_null()';
'Available functions: abs(), log(), is_number(), round(), ceil(), floor(), is_inf(), is_nan(), is_null(), ';
export const Math: FC<Props> = ({ labelWidth, onChange, query }) => {
const onExpressionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {

View File

@ -21,6 +21,7 @@ export const reducerTypes: Array<SelectableValue<string>> = [
{ value: ReducerID.mean, label: 'Mean', description: 'Get the average value' },
{ value: ReducerID.sum, label: 'Sum', description: 'Get the sum of all values' },
{ value: ReducerID.count, label: 'Count', description: 'Get the number of values' },
{ value: ReducerID.last, label: 'Last', description: 'Get the last value' },
];
export const downsamplingTypes: Array<SelectableValue<string>> = [