Add derivative function (#81178)
This commit is contained in:
parent
a6dc527ade
commit
2dcb6b4687
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [derivative](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.derivative.md)
|
||||
|
||||
## ExpressionFunctionDefinitions.derivative property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
derivative: ExpressionFunctionDerivative;
|
||||
```
|
|
@ -18,6 +18,7 @@ export interface ExpressionFunctionDefinitions
|
|||
| --- | --- | --- |
|
||||
| [clog](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md) | <code>ExpressionFunctionClog</code> | |
|
||||
| [cumulative\_sum](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.cumulative_sum.md) | <code>ExpressionFunctionCumulativeSum</code> | |
|
||||
| [derivative](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.derivative.md) | <code>ExpressionFunctionDerivative</code> | |
|
||||
| [font](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md) | <code>ExpressionFunctionFont</code> | |
|
||||
| [kibana\_context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md) | <code>ExpressionFunctionKibanaContext</code> | |
|
||||
| [kibana](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md) | <code>ExpressionFunctionKibana</code> | |
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [derivative](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.derivative.md)
|
||||
|
||||
## ExpressionFunctionDefinitions.derivative property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
derivative: ExpressionFunctionDerivative;
|
||||
```
|
|
@ -18,6 +18,7 @@ export interface ExpressionFunctionDefinitions
|
|||
| --- | --- | --- |
|
||||
| [clog](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md) | <code>ExpressionFunctionClog</code> | |
|
||||
| [cumulative\_sum](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.cumulative_sum.md) | <code>ExpressionFunctionCumulativeSum</code> | |
|
||||
| [derivative](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.derivative.md) | <code>ExpressionFunctionDerivative</code> | |
|
||||
| [font](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md) | <code>ExpressionFunctionFont</code> | |
|
||||
| [kibana\_context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md) | <code>ExpressionFunctionKibanaContext</code> | |
|
||||
| [kibana](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md) | <code>ExpressionFunctionKibana</code> | |
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition } from '../types';
|
||||
import { Datatable, DatatableRow } from '../../expression_types';
|
||||
import { Datatable } from '../../expression_types';
|
||||
import { buildResultColumns, getBucketIdentifier } from './series_calculation_helpers';
|
||||
|
||||
export interface CumulativeSumArgs {
|
||||
by?: string[];
|
||||
|
@ -35,15 +36,6 @@ export type ExpressionFunctionCumulativeSum = ExpressionFunctionDefinition<
|
|||
Datatable
|
||||
>;
|
||||
|
||||
/**
|
||||
* Returns a string identifying the group of a row by a list of columns to group by
|
||||
*/
|
||||
function getBucketIdentifier(row: DatatableRow, groupColumns?: string[]) {
|
||||
return (groupColumns || [])
|
||||
.map((groupColumnId) => (row[groupColumnId] == null ? '' : String(row[groupColumnId])))
|
||||
.join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cumulative sum of a specified column in the data table.
|
||||
*
|
||||
|
@ -114,38 +106,17 @@ export const cumulativeSum: ExpressionFunctionCumulativeSum = {
|
|||
},
|
||||
|
||||
fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) {
|
||||
if (input.columns.some((column) => column.id === outputColumnId)) {
|
||||
throw new Error(
|
||||
i18n.translate('expressions.functions.cumulativeSum.columnConflictMessage', {
|
||||
defaultMessage:
|
||||
'Specified outputColumnId {columnId} already exists. Please pick another column id.',
|
||||
values: {
|
||||
columnId: outputColumnId,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
const resultColumns = buildResultColumns(
|
||||
input,
|
||||
outputColumnId,
|
||||
inputColumnId,
|
||||
outputColumnName
|
||||
);
|
||||
|
||||
const inputColumnDefinition = input.columns.find((column) => column.id === inputColumnId);
|
||||
|
||||
if (!inputColumnDefinition) {
|
||||
if (!resultColumns) {
|
||||
return input;
|
||||
}
|
||||
|
||||
const outputColumnDefinition = {
|
||||
...inputColumnDefinition,
|
||||
id: outputColumnId,
|
||||
name: outputColumnName || outputColumnId,
|
||||
};
|
||||
|
||||
const resultColumns = [...input.columns];
|
||||
// add output column after input column in the table
|
||||
resultColumns.splice(
|
||||
resultColumns.indexOf(inputColumnDefinition) + 1,
|
||||
0,
|
||||
outputColumnDefinition
|
||||
);
|
||||
|
||||
const accumulators: Partial<Record<string, number>> = {};
|
||||
return {
|
||||
...input,
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition } from '../types';
|
||||
import { Datatable } from '../../expression_types';
|
||||
import { buildResultColumns, getBucketIdentifier } from './series_calculation_helpers';
|
||||
|
||||
export interface DerivativeArgs {
|
||||
by?: string[];
|
||||
inputColumnId: string;
|
||||
outputColumnId: string;
|
||||
outputColumnName?: string;
|
||||
}
|
||||
|
||||
export type ExpressionFunctionDerivative = ExpressionFunctionDefinition<
|
||||
'derivative',
|
||||
Datatable,
|
||||
DerivativeArgs,
|
||||
Datatable
|
||||
>;
|
||||
|
||||
/**
|
||||
* Calculates the derivative of a specified column in the data table.
|
||||
*
|
||||
* Also supports multiple series in a single data table - use the `by` argument
|
||||
* to specify the columns to split the calculation by.
|
||||
* For each unique combination of all `by` columns a separate derivative will be calculated.
|
||||
* The order of rows won't be changed - this function is not modifying any existing columns, it's only
|
||||
* adding the specified `outputColumnId` column to every row of the table without adding or removing rows.
|
||||
*
|
||||
* Behavior:
|
||||
* * Will write the derivative of `inputColumnId` into `outputColumnId`
|
||||
* * If provided will use `outputColumnName` as name for the newly created column. Otherwise falls back to `outputColumnId`
|
||||
* * Derivative always start with an undefined value for the first row of a series, a cell will contain its own value minus the
|
||||
* value of the previous cell of the same series.
|
||||
*
|
||||
* Edge cases:
|
||||
* * Will return the input table if `inputColumnId` does not exist
|
||||
* * Will throw an error if `outputColumnId` exists already in provided data table
|
||||
* * If there is no previous row of the current series with a non `null` or `undefined` value, the output cell of the current row
|
||||
* will be set to `undefined`.
|
||||
* * If the row value contains `null` or `undefined`, it will be ignored and the output cell will be set to `undefined`
|
||||
* * If the value of the previous row of the same series contains `null` or `undefined`, the output cell of the current row will be set to `undefined` as well
|
||||
* * For all values besides `null` and `undefined`, the value will be cast to a number before it's used in the
|
||||
* calculation of the current series even if this results in `NaN` (like in case of objects).
|
||||
* * To determine separate series defined by the `by` columns, the values of these columns will be cast to strings
|
||||
* before comparison. If the values are objects, the return value of their `toString` method will be used for comparison.
|
||||
* Missing values (`null` and `undefined`) will be treated as empty strings.
|
||||
*/
|
||||
export const derivative: ExpressionFunctionDerivative = {
|
||||
name: 'derivative',
|
||||
type: 'datatable',
|
||||
|
||||
inputTypes: ['datatable'],
|
||||
|
||||
help: i18n.translate('expressions.functions.derivative.help', {
|
||||
defaultMessage: 'Calculates the derivative of a column in a data table',
|
||||
}),
|
||||
|
||||
args: {
|
||||
by: {
|
||||
help: i18n.translate('expressions.functions.derivative.args.byHelpText', {
|
||||
defaultMessage: 'Column to split the derivative calculation by',
|
||||
}),
|
||||
multi: true,
|
||||
types: ['string'],
|
||||
required: false,
|
||||
},
|
||||
inputColumnId: {
|
||||
help: i18n.translate('expressions.functions.derivative.args.inputColumnIdHelpText', {
|
||||
defaultMessage: 'Column to calculate the derivative of',
|
||||
}),
|
||||
types: ['string'],
|
||||
required: true,
|
||||
},
|
||||
outputColumnId: {
|
||||
help: i18n.translate('expressions.functions.derivative.args.outputColumnIdHelpText', {
|
||||
defaultMessage: 'Column to store the resulting derivative in',
|
||||
}),
|
||||
types: ['string'],
|
||||
required: true,
|
||||
},
|
||||
outputColumnName: {
|
||||
help: i18n.translate('expressions.functions.derivative.args.outputColumnNameHelpText', {
|
||||
defaultMessage: 'Name of the column to store the resulting derivative in',
|
||||
}),
|
||||
types: ['string'],
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) {
|
||||
const resultColumns = buildResultColumns(
|
||||
input,
|
||||
outputColumnId,
|
||||
inputColumnId,
|
||||
outputColumnName
|
||||
);
|
||||
|
||||
if (!resultColumns) {
|
||||
return input;
|
||||
}
|
||||
|
||||
const previousValues: Partial<Record<string, number>> = {};
|
||||
return {
|
||||
...input,
|
||||
columns: resultColumns,
|
||||
rows: input.rows.map((row) => {
|
||||
const newRow = { ...row };
|
||||
|
||||
const bucketIdentifier = getBucketIdentifier(row, by);
|
||||
const previousValue = previousValues[bucketIdentifier];
|
||||
const currentValue = newRow[inputColumnId];
|
||||
|
||||
if (currentValue != null && previousValue != null) {
|
||||
newRow[outputColumnId] = Number(currentValue) - previousValue;
|
||||
} else {
|
||||
newRow[outputColumnId] = undefined;
|
||||
}
|
||||
|
||||
if (currentValue != null) {
|
||||
previousValues[bucketIdentifier] = Number(currentValue);
|
||||
} else {
|
||||
previousValues[bucketIdentifier] = undefined;
|
||||
}
|
||||
|
||||
return newRow;
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
|
@ -26,6 +26,7 @@ import { variable } from './var';
|
|||
import { AnyExpressionFunctionDefinition } from '../types';
|
||||
import { theme } from './theme';
|
||||
import { cumulativeSum } from './cumulative_sum';
|
||||
import { derivative } from './derivative';
|
||||
|
||||
export const functionSpecs: AnyExpressionFunctionDefinition[] = [
|
||||
clog,
|
||||
|
@ -36,6 +37,7 @@ export const functionSpecs: AnyExpressionFunctionDefinition[] = [
|
|||
variable,
|
||||
theme,
|
||||
cumulativeSum,
|
||||
derivative,
|
||||
];
|
||||
|
||||
export * from './clog';
|
||||
|
@ -46,3 +48,4 @@ export * from './var_set';
|
|||
export * from './var';
|
||||
export * from './theme';
|
||||
export * from './cumulative_sum';
|
||||
export * from './derivative';
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Datatable, DatatableRow } from '../../expression_types';
|
||||
|
||||
/**
|
||||
* Returns a string identifying the group of a row by a list of columns to group by
|
||||
*/
|
||||
export function getBucketIdentifier(row: DatatableRow, groupColumns?: string[]) {
|
||||
return (groupColumns || [])
|
||||
.map((groupColumnId) => (row[groupColumnId] == null ? '' : String(row[groupColumnId])))
|
||||
.join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether input and output columns are defined properly
|
||||
* and builds column array of the output table if that's the case.
|
||||
*
|
||||
* * Throws an error if the output column exists already.
|
||||
* * Returns undefined if the input column doesn't exist.
|
||||
* @param input Input datatable
|
||||
* @param outputColumnId Id of the output column
|
||||
* @param inputColumnId Id of the input column
|
||||
* @param outputColumnName Optional name of the output column
|
||||
*/
|
||||
export function buildResultColumns(
|
||||
input: Datatable,
|
||||
outputColumnId: string,
|
||||
inputColumnId: string,
|
||||
outputColumnName: string | undefined
|
||||
) {
|
||||
if (input.columns.some((column) => column.id === outputColumnId)) {
|
||||
throw new Error(
|
||||
i18n.translate('expressions.functions.seriesCalculations.columnConflictMessage', {
|
||||
defaultMessage:
|
||||
'Specified outputColumnId {columnId} already exists. Please pick another column id.',
|
||||
values: {
|
||||
columnId: outputColumnId,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const inputColumnDefinition = input.columns.find((column) => column.id === inputColumnId);
|
||||
|
||||
if (!inputColumnDefinition) {
|
||||
return;
|
||||
}
|
||||
|
||||
const outputColumnDefinition = {
|
||||
...inputColumnDefinition,
|
||||
id: outputColumnId,
|
||||
name: outputColumnName || outputColumnId,
|
||||
};
|
||||
|
||||
const resultColumns = [...input.columns];
|
||||
// add output column after input column in the table
|
||||
resultColumns.splice(resultColumns.indexOf(inputColumnDefinition) + 1, 0, outputColumnDefinition);
|
||||
return resultColumns;
|
||||
}
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { functionWrapper } from './utils';
|
||||
import { derivative, DerivativeArgs } from '../derivative';
|
||||
import { ExecutionContext } from '../../../execution/types';
|
||||
import { Datatable } from '../../../expression_types/specs/datatable';
|
||||
|
||||
describe('interpreter/functions#derivative', () => {
|
||||
const fn = functionWrapper(derivative);
|
||||
const runFn = (input: Datatable, args: DerivativeArgs) =>
|
||||
fn(input, args, {} as ExecutionContext) as Datatable;
|
||||
|
||||
it('calculates derivative', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
|
||||
rows: [{ val: 5 }, { val: 7 }, { val: 3 }, { val: 2 }],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output' }
|
||||
);
|
||||
expect(result.columns).toContainEqual({
|
||||
id: 'output',
|
||||
name: 'output',
|
||||
meta: { type: 'number' },
|
||||
});
|
||||
expect(result.rows.map((row) => row.output)).toEqual([undefined, 2, -4, -1]);
|
||||
});
|
||||
|
||||
it('skips null or undefined values until there is real data', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
|
||||
rows: [
|
||||
{},
|
||||
{ val: null },
|
||||
{ val: undefined },
|
||||
{ val: 1 },
|
||||
{ val: 2 },
|
||||
{ val: undefined },
|
||||
{ val: undefined },
|
||||
{ val: 4 },
|
||||
{ val: 8 },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output' }
|
||||
);
|
||||
expect(result.columns).toContainEqual({
|
||||
id: 'output',
|
||||
name: 'output',
|
||||
meta: { type: 'number' },
|
||||
});
|
||||
expect(result.rows.map((row) => row.output)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
2 - 1,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
8 - 4,
|
||||
]);
|
||||
});
|
||||
|
||||
it('treats 0 as real data', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
|
||||
rows: [
|
||||
{},
|
||||
{ val: null },
|
||||
{ val: undefined },
|
||||
{ val: 1 },
|
||||
{ val: 2 },
|
||||
{ val: 0 },
|
||||
{ val: undefined },
|
||||
{ val: 0 },
|
||||
{ val: undefined },
|
||||
{ val: 0 },
|
||||
{ val: 8 },
|
||||
{ val: 0 },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output' }
|
||||
);
|
||||
expect(result.rows.map((row) => row.output)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
2 - 1,
|
||||
0 - 2,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
8 - 0,
|
||||
0 - 8,
|
||||
]);
|
||||
});
|
||||
|
||||
it('calculates derivative for multiple series', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{ id: 'val', name: 'val', meta: { type: 'number' } },
|
||||
{ id: 'split', name: 'split', meta: { type: 'string' } },
|
||||
],
|
||||
rows: [
|
||||
{ val: 1, split: 'A' },
|
||||
{ val: 2, split: 'B' },
|
||||
{ val: 3, split: 'B' },
|
||||
{ val: 4, split: 'A' },
|
||||
{ val: 5, split: 'A' },
|
||||
{ val: 6, split: 'A' },
|
||||
{ val: 7, split: 'B' },
|
||||
{ val: 8, split: 'B' },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output', by: ['split'] }
|
||||
);
|
||||
|
||||
expect(result.rows.map((row) => row.output)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
3 - 2,
|
||||
4 - 1,
|
||||
5 - 4,
|
||||
6 - 5,
|
||||
7 - 3,
|
||||
8 - 7,
|
||||
]);
|
||||
});
|
||||
|
||||
it('treats missing split column as separate series', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{ id: 'val', name: 'val', meta: { type: 'number' } },
|
||||
{ id: 'split', name: 'split', meta: { type: 'string' } },
|
||||
],
|
||||
rows: [
|
||||
{ val: 1, split: 'A' },
|
||||
{ val: 2, split: 'B' },
|
||||
{ val: 3 },
|
||||
{ val: 4, split: 'A' },
|
||||
{ val: 5 },
|
||||
{ val: 6, split: 'A' },
|
||||
{ val: 7, split: 'B' },
|
||||
{ val: 8, split: 'B' },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output', by: ['split'] }
|
||||
);
|
||||
expect(result.rows.map((row) => row.output)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
4 - 1,
|
||||
5 - 3,
|
||||
6 - 4,
|
||||
7 - 2,
|
||||
8 - 7,
|
||||
]);
|
||||
});
|
||||
|
||||
it('treats null like undefined and empty string for split columns', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{ id: 'val', name: 'val', meta: { type: 'number' } },
|
||||
{ id: 'split', name: 'split', meta: { type: 'string' } },
|
||||
],
|
||||
rows: [
|
||||
{ val: 1, split: 'A' },
|
||||
{ val: 2, split: 'B' },
|
||||
{ val: 3 },
|
||||
{ val: 4, split: 'A' },
|
||||
{ val: 5 },
|
||||
{ val: 6, split: 'A' },
|
||||
{ val: 7, split: null },
|
||||
{ val: 8, split: 'B' },
|
||||
{ val: 9, split: '' },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output', by: ['split'] }
|
||||
);
|
||||
expect(result.rows.map((row) => row.output)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
4 - 1,
|
||||
5 - 3,
|
||||
6 - 4,
|
||||
7 - 5,
|
||||
8 - 2,
|
||||
9 - 7,
|
||||
]);
|
||||
});
|
||||
|
||||
it('calculates derivative for multiple series by multiple split columns', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{ id: 'val', name: 'val', meta: { type: 'number' } },
|
||||
{ id: 'split', name: 'split', meta: { type: 'string' } },
|
||||
{ id: 'split2', name: 'split2', meta: { type: 'string' } },
|
||||
],
|
||||
rows: [
|
||||
{ val: 1, split: 'A', split2: 'C' },
|
||||
{ val: 2, split: 'B', split2: 'C' },
|
||||
{ val: 3, split2: 'C' },
|
||||
{ val: 4, split: 'A', split2: 'C' },
|
||||
{ val: 5 },
|
||||
{ val: 6, split: 'A', split2: 'D' },
|
||||
{ val: 7, split: 'B', split2: 'D' },
|
||||
{ val: 8, split: 'B', split2: 'D' },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output', by: ['split', 'split2'] }
|
||||
);
|
||||
expect(result.rows.map((row) => row.output)).toEqual([
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
4 - 1,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
8 - 7,
|
||||
]);
|
||||
});
|
||||
|
||||
it('splits separate series by the string representation of the cell values', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{ id: 'val', name: 'val', meta: { type: 'number' } },
|
||||
{ id: 'split', name: 'split', meta: { type: 'string' } },
|
||||
],
|
||||
rows: [
|
||||
{ val: 1, split: { anObj: 3 } },
|
||||
{ val: 2, split: { anotherObj: 5 } },
|
||||
{ val: 10, split: 5 },
|
||||
{ val: 11, split: '5' },
|
||||
],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output', by: ['split'] }
|
||||
);
|
||||
|
||||
expect(result.rows.map((row) => row.output)).toEqual([undefined, 2 - 1, undefined, 11 - 10]);
|
||||
});
|
||||
|
||||
it('casts values to number before calculating derivative', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
|
||||
rows: [{ val: 5 }, { val: '7' }, { val: '3' }, { val: 2 }],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output' }
|
||||
);
|
||||
expect(result.rows.map((row) => row.output)).toEqual([undefined, 7 - 5, 3 - 7, 2 - 3]);
|
||||
});
|
||||
|
||||
it('casts values to number before calculating derivative for NaN like values', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }],
|
||||
rows: [{ val: 5 }, { val: '7' }, { val: {} }, { val: 2 }, { val: 5 }],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output' }
|
||||
);
|
||||
expect(result.rows.map((row) => row.output)).toEqual([undefined, 7 - 5, NaN, NaN, 5 - 2]);
|
||||
});
|
||||
|
||||
it('copies over meta information from the source column', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
id: 'val',
|
||||
name: 'val',
|
||||
meta: {
|
||||
type: 'number',
|
||||
|
||||
field: 'afield',
|
||||
index: 'anindex',
|
||||
params: { id: 'number', params: { pattern: '000' } },
|
||||
source: 'synthetic',
|
||||
sourceParams: {
|
||||
some: 'params',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
rows: [{ val: 5 }],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output' }
|
||||
);
|
||||
expect(result.columns).toContainEqual({
|
||||
id: 'output',
|
||||
name: 'output',
|
||||
meta: {
|
||||
type: 'number',
|
||||
|
||||
field: 'afield',
|
||||
index: 'anindex',
|
||||
params: { id: 'number', params: { pattern: '000' } },
|
||||
source: 'synthetic',
|
||||
sourceParams: {
|
||||
some: 'params',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sets output name on output column if specified', () => {
|
||||
const result = runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
id: 'val',
|
||||
name: 'val',
|
||||
meta: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
],
|
||||
rows: [{ val: 5 }],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'output', outputColumnName: 'Output name' }
|
||||
);
|
||||
expect(result.columns).toContainEqual({
|
||||
id: 'output',
|
||||
name: 'Output name',
|
||||
meta: { type: 'number' },
|
||||
});
|
||||
});
|
||||
|
||||
it('returns source table if input column does not exist', () => {
|
||||
const input: Datatable = {
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
id: 'val',
|
||||
name: 'val',
|
||||
meta: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
],
|
||||
rows: [{ val: 5 }],
|
||||
};
|
||||
expect(runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe(input);
|
||||
});
|
||||
|
||||
it('throws an error if output column exists already', () => {
|
||||
expect(() =>
|
||||
runFn(
|
||||
{
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
{
|
||||
id: 'val',
|
||||
name: 'val',
|
||||
meta: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
],
|
||||
rows: [{ val: 5 }],
|
||||
},
|
||||
{ inputColumnId: 'val', outputColumnId: 'val' }
|
||||
)
|
||||
).toThrow();
|
||||
});
|
||||
});
|
|
@ -30,6 +30,7 @@ import {
|
|||
ExpressionFunctionVar,
|
||||
ExpressionFunctionTheme,
|
||||
ExpressionFunctionCumulativeSum,
|
||||
ExpressionFunctionDerivative,
|
||||
} from './specs';
|
||||
import { ExpressionAstFunction } from '../ast';
|
||||
import { PersistableStateDefinition } from '../../../kibana_utils/common';
|
||||
|
@ -133,4 +134,5 @@ export interface ExpressionFunctionDefinitions {
|
|||
var: ExpressionFunctionVar;
|
||||
theme: ExpressionFunctionTheme;
|
||||
cumulative_sum: ExpressionFunctionCumulativeSum;
|
||||
derivative: ExpressionFunctionDerivative;
|
||||
}
|
||||
|
|
|
@ -389,6 +389,10 @@ export interface ExpressionFunctionDefinitions {
|
|||
//
|
||||
// (undocumented)
|
||||
cumulative_sum: ExpressionFunctionCumulativeSum;
|
||||
// Warning: (ae-forgotten-export) The symbol "ExpressionFunctionDerivative" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
derivative: ExpressionFunctionDerivative;
|
||||
// Warning: (ae-forgotten-export) The symbol "ExpressionFunctionFont" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
|
|
|
@ -361,6 +361,10 @@ export interface ExpressionFunctionDefinitions {
|
|||
//
|
||||
// (undocumented)
|
||||
cumulative_sum: ExpressionFunctionCumulativeSum;
|
||||
// Warning: (ae-forgotten-export) The symbol "ExpressionFunctionDerivative" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
derivative: ExpressionFunctionDerivative;
|
||||
// Warning: (ae-forgotten-export) The symbol "ExpressionFunctionFont" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
|
|
Loading…
Reference in a new issue