mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Transformations: Show row index as percent in 'Add field from calculation' (#74322)
* show row index as percentage in add field from calc transform * prettier * add test + modify docs * show quantile and add percentage unit default. add explanatory tooltip and docs * fix test * set unit percent only on quantile mode * Change naming to percentile * Rename to As Percentile * docs mods
This commit is contained in:
@@ -109,10 +109,11 @@ Use this transformation to add a new field calculated from two other fields. Eac
|
|||||||
- **Mode -** Select a mode:
|
- **Mode -** Select a mode:
|
||||||
- **Reduce row -** Apply selected calculation on each row of selected fields independently.
|
- **Reduce row -** Apply selected calculation on each row of selected fields independently.
|
||||||
- **Binary option -** Apply basic math operation(sum, multiply, etc) on values in a single row from two selected fields.
|
- **Binary option -** Apply basic math operation(sum, multiply, etc) on values in a single row from two selected fields.
|
||||||
- **Index -** Will insert a field with the row index.
|
- **Row index -** Insert a field with the row index.
|
||||||
- **Field name -** Select the names of fields you want to use in the calculation for the new field.
|
- **Field name -** Select the names of fields you want to use in the calculation for the new field.
|
||||||
- **Calculation -** If you select **Reduce row** mode, then the **Calculation** field appears. Click in the field to see a list of calculation choices you can use to create the new field. For information about available calculations, refer to [Calculation types][].
|
- **Calculation -** If you select **Reduce row** mode, then the **Calculation** field appears. Click in the field to see a list of calculation choices you can use to create the new field. For information about available calculations, refer to [Calculation types][].
|
||||||
- **Operation -** If you select **Binary option** mode, then the **Operation** fields appear. These fields allow you to do basic math operations on values in a single row from two selected fields. You can also use numerical values for binary operations.
|
- **Operation -** If you select **Binary option** mode, then the **Operation** fields appear. These fields allow you to do basic math operations on values in a single row from two selected fields. You can also use numerical values for binary operations.
|
||||||
|
- **As percentile -** If you select **Row index** mode, then the **As percentile** switch appears. This switch allows you to transform the row index as a percentage of the total number of rows.
|
||||||
- **Alias -** (Optional) Enter the name of your new field. If you leave this blank, then the field will be named to match the calculation.
|
- **Alias -** (Optional) Enter the name of your new field. If you leave this blank, then the field will be named to match the calculation.
|
||||||
- **Replace all fields -** (Optional) Select this option if you want to hide all other fields and display only your calculated field in the visualization.
|
- **Replace all fields -** (Optional) Select this option if you want to hide all other fields and display only your calculated field in the visualization.
|
||||||
|
|
||||||
|
|||||||
@@ -238,6 +238,34 @@ describe('calculateField transformer w/ timeseries', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can add percentage index field', async () => {
|
||||||
|
const cfg = {
|
||||||
|
id: DataTransformerID.calculateField,
|
||||||
|
options: {
|
||||||
|
mode: CalculateFieldMode.Index,
|
||||||
|
replaceFields: true,
|
||||||
|
index: {
|
||||||
|
asPercentile: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const series = toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'TheTime', type: FieldType.time, values: [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000] },
|
||||||
|
{ name: 'Field', type: FieldType.number, values: [2, 200, 3, 6, 3, 7, 9, 9] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(transformDataFrame([cfg], [series])).toEmitValuesWith((received) => {
|
||||||
|
const data = received[0][0];
|
||||||
|
expect(data.fields.length).toEqual(1);
|
||||||
|
expect(data.fields[0].values[2]).toEqual(0.25);
|
||||||
|
expect(data.fields[0].values[4]).toEqual(0.5);
|
||||||
|
expect(data.fields[0].values[6]).toEqual(0.75);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('uses template variable substituion', async () => {
|
it('uses template variable substituion', async () => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
id: DataTransformerID.calculateField,
|
id: DataTransformerID.calculateField,
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ export interface BinaryOptions {
|
|||||||
right: string;
|
right: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IndexOptions {
|
||||||
|
asPercentile: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const defaultReduceOptions: ReduceOptions = {
|
const defaultReduceOptions: ReduceOptions = {
|
||||||
reducer: ReducerID.sum,
|
reducer: ReducerID.sum,
|
||||||
};
|
};
|
||||||
@@ -49,6 +53,7 @@ export interface CalculateFieldTransformerOptions {
|
|||||||
// Only one should be filled
|
// Only one should be filled
|
||||||
reduce?: ReduceOptions;
|
reduce?: ReduceOptions;
|
||||||
binary?: BinaryOptions;
|
binary?: BinaryOptions;
|
||||||
|
index?: IndexOptions;
|
||||||
|
|
||||||
// Remove other fields
|
// Remove other fields
|
||||||
replaceFields?: boolean;
|
replaceFields?: boolean;
|
||||||
@@ -98,11 +103,19 @@ export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransf
|
|||||||
creator = getBinaryCreator(defaults(binaryOptions, defaultBinaryOptions), data);
|
creator = getBinaryCreator(defaults(binaryOptions, defaultBinaryOptions), data);
|
||||||
} else if (mode === CalculateFieldMode.Index) {
|
} else if (mode === CalculateFieldMode.Index) {
|
||||||
return data.map((frame) => {
|
return data.map((frame) => {
|
||||||
|
const indexArr = [...Array(frame.length).keys()];
|
||||||
|
|
||||||
|
if (options.index?.asPercentile) {
|
||||||
|
for (let i = 0; i < indexArr.length; i++) {
|
||||||
|
indexArr[i] = indexArr[i] / indexArr.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const f = {
|
const f = {
|
||||||
name: options.alias ?? 'Row',
|
name: options.alias ?? 'Row',
|
||||||
type: FieldType.number,
|
type: FieldType.number,
|
||||||
values: [...Array(frame.length).keys()],
|
values: indexArr,
|
||||||
config: {},
|
config: options.index?.asPercentile ? { unit: 'percentunit' } : {},
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
...frame,
|
...frame,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
CalculateFieldMode,
|
CalculateFieldMode,
|
||||||
CalculateFieldTransformerOptions,
|
CalculateFieldTransformerOptions,
|
||||||
getNameFromOptions,
|
getNameFromOptions,
|
||||||
|
IndexOptions,
|
||||||
ReduceOptions,
|
ReduceOptions,
|
||||||
} from '@grafana/data/src/transformations/transformers/calculateField';
|
} from '@grafana/data/src/transformations/transformers/calculateField';
|
||||||
import { FilterPill, HorizontalGroup, Input, LegacyForms, Select, StatsPicker } from '@grafana/ui';
|
import { FilterPill, HorizontalGroup, Input, LegacyForms, Select, StatsPicker } from '@grafana/ui';
|
||||||
@@ -142,6 +143,16 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onToggleRowIndexAsPercentile = () => {
|
||||||
|
const { options } = this.props;
|
||||||
|
this.props.onChange({
|
||||||
|
...options,
|
||||||
|
index: {
|
||||||
|
asPercentile: !options.index?.asPercentile ?? false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onModeChanged = (value: SelectableValue<CalculateFieldMode>) => {
|
onModeChanged = (value: SelectableValue<CalculateFieldMode>) => {
|
||||||
const { options, onChange } = this.props;
|
const { options, onChange } = this.props;
|
||||||
const mode = value.value ?? CalculateFieldMode.BinaryOperation;
|
const mode = value.value ?? CalculateFieldMode.BinaryOperation;
|
||||||
@@ -197,6 +208,22 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
|||||||
this.updateReduceOptions({ ...reduce, reducer });
|
this.updateReduceOptions({ ...reduce, reducer });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderRowIndex(options?: IndexOptions) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<LegacyForms.Switch
|
||||||
|
label="As percentile"
|
||||||
|
tooltip="Transform the row index as a percentile."
|
||||||
|
labelClass="width-8"
|
||||||
|
checked={!!options?.asPercentile}
|
||||||
|
onChange={this.onToggleRowIndexAsPercentile}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderReduceRow(options?: ReduceOptions) {
|
renderReduceRow(options?: ReduceOptions) {
|
||||||
const { names, selected } = this.state;
|
const { names, selected } = this.state;
|
||||||
options = defaults(options, { reducer: ReducerID.sum });
|
options = defaults(options, { reducer: ReducerID.sum });
|
||||||
@@ -353,6 +380,7 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
|||||||
</div>
|
</div>
|
||||||
{mode === CalculateFieldMode.BinaryOperation && this.renderBinaryOperation(options.binary)}
|
{mode === CalculateFieldMode.BinaryOperation && this.renderBinaryOperation(options.binary)}
|
||||||
{mode === CalculateFieldMode.ReduceRow && this.renderReduceRow(options.reduce)}
|
{mode === CalculateFieldMode.ReduceRow && this.renderReduceRow(options.reduce)}
|
||||||
|
{mode === CalculateFieldMode.Index && this.renderRowIndex(options.index)}
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<div className="gf-form-label width-8">Alias</div>
|
<div className="gf-form-label width-8">Alias</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user