Transformations: Add percentage to unary operations (#97358)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
@@ -457,48 +457,124 @@ describe('calculateField transformer w/ timeseries', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('unary math', async () => {
|
||||
const unarySeries = toDataFrame({
|
||||
fields: [
|
||||
{ name: 'TheTime', type: FieldType.time, values: [1000, 2000, 3000, 4000] },
|
||||
{ name: 'A', type: FieldType.number, values: [1, -10, -200, 300] },
|
||||
],
|
||||
describe('unary math', () => {
|
||||
it('calculates absolute value accurately', async () => {
|
||||
const unarySeries = toDataFrame({
|
||||
fields: [
|
||||
{ name: 'TheTime', type: FieldType.time, values: [1000, 2000, 3000, 4000] },
|
||||
{ name: 'A', type: FieldType.number, values: [1, -10, -200, 300] },
|
||||
],
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
id: DataTransformerID.calculateField,
|
||||
options: {
|
||||
mode: CalculateFieldMode.UnaryOperation,
|
||||
unary: {
|
||||
fieldName: 'A',
|
||||
operator: UnaryOperationID.Abs,
|
||||
},
|
||||
replaceFields: true,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [unarySeries])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
const rows = new DataFrameView(filtered).toArray();
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
'abs(A)': 1,
|
||||
TheTime: 1000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 10,
|
||||
TheTime: 2000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 200,
|
||||
TheTime: 3000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 300,
|
||||
TheTime: 4000,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
id: DataTransformerID.calculateField,
|
||||
options: {
|
||||
mode: CalculateFieldMode.UnaryOperation,
|
||||
unary: {
|
||||
fieldName: 'A',
|
||||
operator: UnaryOperationID.Abs,
|
||||
it('calculates percentage value accurately', async () => {
|
||||
const testCases = [
|
||||
{
|
||||
values: [1, 2, 3, 4],
|
||||
result: [0.1, 0.2, 0.3, 0.4],
|
||||
},
|
||||
replaceFields: true,
|
||||
},
|
||||
};
|
||||
{
|
||||
values: [1, 0, 5, 4],
|
||||
result: [0.1, 0, 0.5, 0.4],
|
||||
},
|
||||
{
|
||||
values: [1, Infinity, 5, 4],
|
||||
result: [0.1, Infinity, 0.5, 0.4],
|
||||
},
|
||||
{
|
||||
values: [0, 0, 0, 0],
|
||||
result: [0, 0, 0, 0],
|
||||
},
|
||||
{
|
||||
values: ['a', 's', 2, 4],
|
||||
result: [NaN, NaN, 0.3333333333333333, 0.6666666666666666],
|
||||
},
|
||||
{
|
||||
values: [1e5, 1, 2, 4],
|
||||
result: [0.999930004899657, 0.000009999300048996571, 0.000019998600097993142, 0.000039997200195986284],
|
||||
},
|
||||
];
|
||||
|
||||
await expect(transformDataFrame([cfg], [unarySeries])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
const rows = new DataFrameView(filtered).toArray();
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
'abs(A)': 1,
|
||||
TheTime: 1000,
|
||||
const cfg = {
|
||||
id: DataTransformerID.calculateField,
|
||||
options: {
|
||||
mode: CalculateFieldMode.UnaryOperation,
|
||||
unary: {
|
||||
fieldName: 'A',
|
||||
operator: UnaryOperationID.Percent,
|
||||
},
|
||||
replaceFields: true,
|
||||
},
|
||||
{
|
||||
'abs(A)': 10,
|
||||
TheTime: 2000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 200,
|
||||
TheTime: 3000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 300,
|
||||
TheTime: 4000,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
testCases.forEach(async (testCase) => {
|
||||
const unarySeries = toDataFrame({
|
||||
fields: [
|
||||
{ name: 'TheTime', type: FieldType.time, values: [1000, 2000, 3000, 4000] },
|
||||
{ name: 'A', type: FieldType.number, values: testCase.values },
|
||||
],
|
||||
});
|
||||
|
||||
await expect(transformDataFrame([cfg], [unarySeries])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
const rows = new DataFrameView(filtered).toArray();
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
'percent(A)': testCase.result[0],
|
||||
TheTime: 1000,
|
||||
},
|
||||
{
|
||||
'percent(A)': testCase.result[1],
|
||||
TheTime: 2000,
|
||||
},
|
||||
{
|
||||
'percent(A)': testCase.result[2],
|
||||
TheTime: 3000,
|
||||
},
|
||||
{
|
||||
'percent(A)': testCase.result[3],
|
||||
TheTime: 4000,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -652,8 +652,19 @@ function getUnaryCreator(options: UnaryOptions, allFrames: DataFrame[]): ValuesC
|
||||
}
|
||||
|
||||
const arr = new Array(value.length);
|
||||
|
||||
let sum = 0;
|
||||
|
||||
if (options.operator === UnaryOperationID.Percent) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (Number.isFinite(value[i])) {
|
||||
sum += value[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i] = operator.operation(value[i]);
|
||||
arr[i] = operator.operation(value[i], sum);
|
||||
}
|
||||
|
||||
return arr;
|
||||
|
||||
@@ -7,9 +7,10 @@ export enum UnaryOperationID {
|
||||
Round = 'round',
|
||||
Floor = 'floor',
|
||||
Ceil = 'ceil',
|
||||
Percent = 'percent',
|
||||
}
|
||||
|
||||
export type UnaryOperation = (value: number) => number;
|
||||
export type UnaryOperation = (value: number, total?: number) => number;
|
||||
|
||||
interface UnaryOperatorInfo extends RegistryItem {
|
||||
operation: UnaryOperation;
|
||||
@@ -54,5 +55,11 @@ export const unaryOperators = new Registry<UnaryOperatorInfo>(() => {
|
||||
operation: (value: number) => Math.ceil(value),
|
||||
unaryOperationID: UnaryOperationID.Ceil,
|
||||
},
|
||||
{
|
||||
id: UnaryOperationID.Percent,
|
||||
name: 'Percentage',
|
||||
operation: (value: number, total = 1) => value / (total || 1),
|
||||
unaryOperationID: UnaryOperationID.Percent,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user