|
|
|
|
@ -54,7 +54,7 @@ describe('Stats Calculators', () => { |
|
|
|
|
it('should calculate basic stats', () => { |
|
|
|
|
const stats = reduceField({ |
|
|
|
|
field: basicTable.fields[0], |
|
|
|
|
reducers: ['first', 'last', 'mean', 'count'], |
|
|
|
|
reducers: [ReducerID.first, ReducerID.last, ReducerID.mean, ReducerID.count], |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
expect(stats.first).toEqual(10); |
|
|
|
|
@ -67,7 +67,7 @@ describe('Stats Calculators', () => { |
|
|
|
|
basicTable.fields[0].state = undefined; // clear the cache
|
|
|
|
|
const stats = reduceField({ |
|
|
|
|
field: basicTable.fields[0], |
|
|
|
|
reducers: ['first'], |
|
|
|
|
reducers: [ReducerID.first], |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// Should do the simple version that just looks up value
|
|
|
|
|
@ -172,6 +172,58 @@ describe('Stats Calculators', () => { |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
it('consistent results for first/last value with NaN', () => { |
|
|
|
|
const info = [ |
|
|
|
|
{ |
|
|
|
|
data: [NaN, 200, NaN], // first/last value is NaN
|
|
|
|
|
result: 200, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
data: [NaN, NaN, NaN], // All NaN
|
|
|
|
|
result: null, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
data: [undefined, undefined, undefined], // Empty row
|
|
|
|
|
result: null, |
|
|
|
|
}, |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
const stats = reduceField({ |
|
|
|
|
field: createField('x', info[0].data), |
|
|
|
|
reducers: [ReducerID.first, ReducerID.last, ReducerID.firstNotNull, ReducerID.lastNotNull, ReducerID.diffperc], |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
expect(stats[ReducerID.first]).toEqual(NaN); |
|
|
|
|
expect(stats[ReducerID.last]).toEqual(NaN); |
|
|
|
|
expect(stats[ReducerID.firstNotNull]).toEqual(200); |
|
|
|
|
expect(stats[ReducerID.lastNotNull]).toEqual(200); |
|
|
|
|
expect(stats[ReducerID.diffperc]).toEqual(0); |
|
|
|
|
|
|
|
|
|
const reducers = [ReducerID.lastNotNull, ReducerID.firstNotNull]; |
|
|
|
|
for (const input of info) { |
|
|
|
|
for (const reducer of reducers) { |
|
|
|
|
const v1 = reduceField({ |
|
|
|
|
field: createField('x', input.data), |
|
|
|
|
reducers: [reducer, ReducerID.mean], // uses standard path
|
|
|
|
|
})[reducer]; |
|
|
|
|
|
|
|
|
|
const v2 = reduceField({ |
|
|
|
|
field: createField('x', input.data), |
|
|
|
|
reducers: [reducer], // uses optimized path
|
|
|
|
|
})[reducer]; |
|
|
|
|
|
|
|
|
|
if (v1 !== v2 || v1 !== input.result) { |
|
|
|
|
const msg = |
|
|
|
|
`Invalid ${reducer} result for: ` + |
|
|
|
|
input.data.join(', ') + |
|
|
|
|
` Expected: ${input.result}` + // configured
|
|
|
|
|
` Received: Multiple: ${v1}, Single: ${v2}`; |
|
|
|
|
expect(msg).toEqual(null); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
it('count should ignoreNulls by default', () => { |
|
|
|
|
const someNulls = createField('x', [1, null, null, 1]); |
|
|
|
|
expect(reduce(someNulls, ReducerID.count)).toEqual(2); |
|
|
|
|
|