|
@@ -17,6 +17,9 @@ export enum ReducerID {
|
|
|
delta = 'delta',
|
|
delta = 'delta',
|
|
|
step = 'step',
|
|
step = 'step',
|
|
|
|
|
|
|
|
|
|
+ firstNotNull = 'firstNotNull',
|
|
|
|
|
+ lastNotNull = 'lastNotNull',
|
|
|
|
|
+
|
|
|
changeCount = 'changeCount',
|
|
changeCount = 'changeCount',
|
|
|
distinctCount = 'distinctCount',
|
|
distinctCount = 'distinctCount',
|
|
|
|
|
|
|
@@ -131,15 +134,29 @@ let hasBuiltIndex = false;
|
|
|
function getById(id: string): FieldReducerInfo | undefined {
|
|
function getById(id: string): FieldReducerInfo | undefined {
|
|
|
if (!hasBuiltIndex) {
|
|
if (!hasBuiltIndex) {
|
|
|
[
|
|
[
|
|
|
|
|
+ {
|
|
|
|
|
+ id: ReducerID.lastNotNull,
|
|
|
|
|
+ name: 'Last (not null)',
|
|
|
|
|
+ description: 'Last non-null value',
|
|
|
|
|
+ standard: true,
|
|
|
|
|
+ alias: 'current',
|
|
|
|
|
+ reduce: calculateLastNotNull,
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
id: ReducerID.last,
|
|
id: ReducerID.last,
|
|
|
name: 'Last',
|
|
name: 'Last',
|
|
|
- description: 'Last Value (current)',
|
|
|
|
|
|
|
+ description: 'Last Value',
|
|
|
standard: true,
|
|
standard: true,
|
|
|
- alias: 'current',
|
|
|
|
|
reduce: calculateLast,
|
|
reduce: calculateLast,
|
|
|
},
|
|
},
|
|
|
{ id: ReducerID.first, name: 'First', description: 'First Value', standard: true, reduce: calculateFirst },
|
|
{ id: ReducerID.first, name: 'First', description: 'First Value', standard: true, reduce: calculateFirst },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: ReducerID.firstNotNull,
|
|
|
|
|
+ name: 'First (not null)',
|
|
|
|
|
+ description: 'First non-null value',
|
|
|
|
|
+ standard: true,
|
|
|
|
|
+ reduce: calculateFirstNotNull,
|
|
|
|
|
+ },
|
|
|
{ id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true },
|
|
{ id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true },
|
|
|
{ id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true },
|
|
{ id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true },
|
|
|
{ id: ReducerID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
|
|
{ id: ReducerID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
|
|
@@ -231,6 +248,8 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
|
|
|
mean: null,
|
|
mean: null,
|
|
|
last: null,
|
|
last: null,
|
|
|
first: null,
|
|
first: null,
|
|
|
|
|
+ lastNotNull: undefined,
|
|
|
|
|
+ firstNotNull: undefined,
|
|
|
count: 0,
|
|
count: 0,
|
|
|
nonNullCount: 0,
|
|
nonNullCount: 0,
|
|
|
allIsNull: true,
|
|
allIsNull: true,
|
|
@@ -246,6 +265,10 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
|
|
|
|
|
|
|
|
for (let i = 0; i < data.rows.length; i++) {
|
|
for (let i = 0; i < data.rows.length; i++) {
|
|
|
let currentValue = data.rows[i][fieldIndex];
|
|
let currentValue = data.rows[i][fieldIndex];
|
|
|
|
|
+ if (i === 0) {
|
|
|
|
|
+ calcs.first = currentValue;
|
|
|
|
|
+ }
|
|
|
|
|
+ calcs.last = currentValue;
|
|
|
|
|
|
|
|
if (currentValue === null) {
|
|
if (currentValue === null) {
|
|
|
if (ignoreNulls) {
|
|
if (ignoreNulls) {
|
|
@@ -257,9 +280,9 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (currentValue !== null) {
|
|
if (currentValue !== null) {
|
|
|
- const isFirst = calcs.first === null;
|
|
|
|
|
|
|
+ const isFirst = calcs.firstNotNull === undefined;
|
|
|
if (isFirst) {
|
|
if (isFirst) {
|
|
|
- calcs.first = currentValue;
|
|
|
|
|
|
|
+ calcs.firstNotNull = currentValue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (isNumber(currentValue)) {
|
|
if (isNumber(currentValue)) {
|
|
@@ -268,12 +291,12 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
|
|
|
calcs.nonNullCount++;
|
|
calcs.nonNullCount++;
|
|
|
|
|
|
|
|
if (!isFirst) {
|
|
if (!isFirst) {
|
|
|
- const step = currentValue - calcs.last!;
|
|
|
|
|
|
|
+ const step = currentValue - calcs.lastNotNull!;
|
|
|
if (calcs.step > step) {
|
|
if (calcs.step > step) {
|
|
|
calcs.step = step; // the minimum interval
|
|
calcs.step = step; // the minimum interval
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (calcs.last! > currentValue) {
|
|
|
|
|
|
|
+ if (calcs.lastNotNull! > currentValue) {
|
|
|
// counter reset
|
|
// counter reset
|
|
|
calcs.previousDeltaUp = false;
|
|
calcs.previousDeltaUp = false;
|
|
|
if (i === data.rows.length - 1) {
|
|
if (i === data.rows.length - 1) {
|
|
@@ -307,7 +330,7 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
|
|
|
calcs.allIsZero = false;
|
|
calcs.allIsZero = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- calcs.last = currentValue;
|
|
|
|
|
|
|
+ calcs.lastNotNull = currentValue;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -331,10 +354,8 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
|
|
|
calcs.range = calcs.max - calcs.min;
|
|
calcs.range = calcs.max - calcs.min;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (calcs.first !== null && calcs.last !== null) {
|
|
|
|
|
- if (isNumber(calcs.first) && isNumber(calcs.last)) {
|
|
|
|
|
- calcs.diff = calcs.last - calcs.first;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (isNumber(calcs.firstNotNull) && isNumber(calcs.lastNotNull)) {
|
|
|
|
|
+ calcs.diff = calcs.lastNotNull - calcs.firstNotNull;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return calcs;
|
|
return calcs;
|
|
@@ -344,10 +365,41 @@ function calculateFirst(data: DataFrame, fieldIndex: number, ignoreNulls: boolea
|
|
|
return { first: data.rows[0][fieldIndex] };
|
|
return { first: data.rows[0][fieldIndex] };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function calculateFirstNotNull(
|
|
|
|
|
+ data: DataFrame,
|
|
|
|
|
+ fieldIndex: number,
|
|
|
|
|
+ ignoreNulls: boolean,
|
|
|
|
|
+ nullAsZero: boolean
|
|
|
|
|
+): FieldCalcs {
|
|
|
|
|
+ for (let idx = 0; idx < data.rows.length; idx++) {
|
|
|
|
|
+ const v = data.rows[idx][fieldIndex];
|
|
|
|
|
+ if (v != null) {
|
|
|
|
|
+ return { firstNotNull: v };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return { firstNotNull: undefined };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function calculateLast(data: DataFrame, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
|
|
function calculateLast(data: DataFrame, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
|
|
|
return { last: data.rows[data.rows.length - 1][fieldIndex] };
|
|
return { last: data.rows[data.rows.length - 1][fieldIndex] };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function calculateLastNotNull(
|
|
|
|
|
+ data: DataFrame,
|
|
|
|
|
+ fieldIndex: number,
|
|
|
|
|
+ ignoreNulls: boolean,
|
|
|
|
|
+ nullAsZero: boolean
|
|
|
|
|
+): FieldCalcs {
|
|
|
|
|
+ let idx = data.rows.length - 1;
|
|
|
|
|
+ while (idx >= 0) {
|
|
|
|
|
+ const v = data.rows[idx--][fieldIndex];
|
|
|
|
|
+ if (v != null) {
|
|
|
|
|
+ return { lastNotNull: v };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return { lastNotNull: undefined };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function calculateChangeCount(
|
|
function calculateChangeCount(
|
|
|
data: DataFrame,
|
|
data: DataFrame,
|
|
|
fieldIndex: number,
|
|
fieldIndex: number,
|