Transformations: Handle undefined values when calculating mean and total in add field from calculations (#78645)

* Transformations: Handle undefined values when calculating mean and total in add field from calculations

* fix typo

* change to != and add tests
This commit is contained in:
Oscar Kilhed 2023-11-27 10:25:55 +01:00 committed by GitHub
parent 703deeff0b
commit 1ab23108c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 6 deletions

View File

@ -597,6 +597,32 @@ describe('calculateField transformer w/ timeseries', () => {
expect(data.fields[1].values[2]).toEqual(2);
});
});
it('calculates cumulative total with undefined values', async () => {
const cfg = {
id: DataTransformerID.calculateField,
options: {
mode: CalculateFieldMode.CumulativeFunctions,
cumulative: {
field: 'x',
reducer: ReducerID.sum,
},
},
};
const series = toDataFrame({
fields: [{ name: 'x', type: FieldType.number, values: [1, undefined, 2, 3] }],
});
await expect(transformDataFrame([cfg], [series])).toEmitValuesWith((received) => {
const data = received[0][0];
expect(data.fields.length).toEqual(2);
expect(data.fields[1].values[0]).toEqual(1);
expect(data.fields[1].values[1]).toEqual(1);
expect(data.fields[1].values[2]).toEqual(3);
expect(data.fields[1].values[3]).toEqual(6);
});
});
it('calculates cumulative total with nulls', async () => {
const cfg = {
@ -743,6 +769,36 @@ describe('calculateField transformer w/ timeseries', () => {
});
});
it('calculates centered moving average with undefined values', async () => {
const cfg = {
id: DataTransformerID.calculateField,
options: {
mode: CalculateFieldMode.WindowFunctions,
window: {
windowAlignment: WindowAlignment.Centered,
field: 'x',
windowSize: 0.75,
windowSizeMode: WindowSizeMode.Percentage,
reducer: ReducerID.mean,
},
},
};
const series = toDataFrame({
fields: [{ name: 'x', type: FieldType.number, values: [1, undefined, 2, 7] }],
});
await expect(transformDataFrame([cfg], [series])).toEmitValuesWith((received) => {
const data = received[0][0];
expect(data.fields.length).toEqual(2);
expect(data.fields[1].values[0]).toEqual(1);
expect(data.fields[1].values[1]).toEqual(1.5);
expect(data.fields[1].values[2]).toEqual(4.5);
expect(data.fields[1].values[3]).toEqual(4.5);
});
});
it('calculates centered moving average with nulls', async () => {
const cfg = {
id: DataTransformerID.calculateField,

View File

@ -277,7 +277,7 @@ function getTrailingWindowValues(frame: DataFrame, reducer: ReducerID, selectedF
for (let i = 0; i < frame.length; i++) {
if (reducer === ReducerID.mean) {
const currentValue = selectedField.values[i];
if (currentValue !== null) {
if (currentValue !== null && currentValue !== undefined) {
count++;
sum += currentValue;
@ -315,7 +315,7 @@ function getCenteredWindowValues(frame: DataFrame, reducer: ReducerID, selectedF
if (i === 0) {
// We're at the start and need to prime the leading part of the window
for (let x = 0; x < leadingPartOfWindow + 1 && x < selectedField.values.length; x++) {
if (selectedField.values[x] !== null) {
if (selectedField.values[x] != null) {
sum += selectedField.values[x];
count++;
}
@ -323,14 +323,14 @@ function getCenteredWindowValues(frame: DataFrame, reducer: ReducerID, selectedF
} else {
if (last < selectedField.values.length) {
// Last is inside the data and should be added.
if (selectedField.values[last] !== null) {
if (selectedField.values[last] != null) {
sum += selectedField.values[last];
count++;
}
}
if (first > 0) {
// Remove values that have fallen outside of the window, if the start of the window isn't outside of the data.
if (selectedField.values[first - 1] !== null) {
if (selectedField.values[first - 1] != null) {
sum -= selectedField.values[first - 1];
count--;
}
@ -363,7 +363,7 @@ function calculateVariance(vals: number[]): number {
let nonNullCount = 0;
for (let i = 0; i < vals.length; i++) {
const currentValue = vals[i];
if (currentValue !== null) {
if (currentValue != null) {
nonNullCount++;
let _oldMean = runningMean;
runningMean += (currentValue - _oldMean) / nonNullCount;
@ -417,7 +417,7 @@ function getCumulativeCreator(options: CumulativeOptions, allFrames: DataFrame[]
let total = 0;
for (let i = 0; i < frame.length; i++) {
total += selectedField.values[i];
total += selectedField.values[i] ?? 0;
if (options.reducer === ReducerID.sum) {
vals.push(total);
} else if (options.reducer === ReducerID.mean) {