Add derivative function (#81178)

This commit is contained in:
Joe Reuter 2020-11-02 10:30:44 +01:00 committed by GitHub
parent a6dc527ade
commit 2dcb6b4687
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 677 additions and 38 deletions

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) &gt; [derivative](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.derivative.md)
## ExpressionFunctionDefinitions.derivative property
<b>Signature:</b>
```typescript
derivative: ExpressionFunctionDerivative;
```

View file

@ -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> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) &gt; [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) &gt; [derivative](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.derivative.md)
## ExpressionFunctionDefinitions.derivative property
<b>Signature:</b>
```typescript
derivative: ExpressionFunctionDerivative;
```

View file

@ -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> | |

View file

@ -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,

View file

@ -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;
}),
};
},
};

View file

@ -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';

View file

@ -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;
}

View file

@ -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();
});
});

View file

@ -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;
}

View file

@ -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)

View file

@ -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)