Transformations: Add standard deviation and variance reducers (#52769)

Co-authored-by: Selva <selvavm@hotmail.com>
Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
Ryan McKinley 2022-07-26 08:25:35 -07:00 committed by GitHub
parent a1c6147374
commit 5df6a95045
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 2 deletions

View File

@ -10,7 +10,7 @@ weight: 1100
# Reference: Calculation types
Refer to the following list of calculations you can perform in Grafana. You can find these calculations in the **Transform** tab and in the bar gauge, gauge, and stat visualizations.
The following table contains a list of calculations you can perform in Grafana. You can find these calculations in the **Transform** tab and in the bar gauge, gauge, and stat visualizations.
| Calculation | Description |
| :----------------- | :-------------------------------------------------------- |
@ -25,6 +25,8 @@ Refer to the following list of calculations you can perform in Grafana. You can
| First (not null) | First, not null value in a field |
| Max | Maximum value of a field |
| Mean | Mean value of all values in a field |
| Variance | Variance of all values in a field |
| StdDev | Standard deviation of all values in a field |
| Min | Minimum value of a field |
| Min (above zero) | Minimum, positive value of a field |
| Range | Difference between maximum and minimum values of a field |

View File

@ -80,11 +80,13 @@ describe('Stats Calculators', () => {
it('should get non standard stats', () => {
const stats = reduceField({
field: basicTable.fields[0],
reducers: [ReducerID.distinctCount, ReducerID.changeCount],
reducers: [ReducerID.distinctCount, ReducerID.changeCount, ReducerID.variance, ReducerID.stdDev],
});
expect(stats.distinctCount).toEqual(2);
expect(stats.changeCount).toEqual(1);
expect(stats.variance).toEqual(25);
expect(stats.stdDev).toEqual(5);
});
it('should calculate step', () => {

View File

@ -10,6 +10,8 @@ export enum ReducerID {
min = 'min',
logmin = 'logmin',
mean = 'mean',
variance = 'variance',
stdDev = 'stdDev',
last = 'last',
first = 'first',
count = 'count',
@ -152,6 +154,20 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
{ id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true },
{ id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true },
{ id: ReducerID.mean, name: 'Mean', description: 'Average Value', standard: true, aliasIds: ['avg'] },
{
id: ReducerID.variance,
name: 'Variance',
description: 'Variance of all values in a field',
standard: false,
reduce: calculateStdDev,
},
{
id: ReducerID.stdDev,
name: 'StdDev',
description: 'Standard deviation of all values in a field',
standard: false,
reduce: calculateStdDev,
},
{
id: ReducerID.sum,
name: 'Total',
@ -419,6 +435,33 @@ function calculateLastNotNull(field: Field, ignoreNulls: boolean, nullAsZero: bo
return { lastNotNull: null };
}
/** Calculates standard deviation and variance */
function calculateStdDev(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
// Only support number fields
if (!(field.type === FieldType.number || field.type === FieldType.time)) {
return { variance: 0, stdDev: 0 };
}
let squareSum = 0;
let runningMean = 0;
let runningNonNullCount = 0;
const data = field.values;
for (let i = 0; i < data.length; i++) {
const currentValue = data.get(i);
if (currentValue != null) {
runningNonNullCount++;
let _oldMean = runningMean;
runningMean += (currentValue - _oldMean) / runningNonNullCount;
squareSum += (currentValue - _oldMean) * (currentValue - runningMean);
}
}
if (runningNonNullCount > 0) {
const variance = squareSum / runningNonNullCount;
return { variance, stdDev: Math.sqrt(variance) };
}
return { variance: 0, stdDev: 0 };
}
function calculateChangeCount(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
const data = field.values;
let count = 0;