Histogram: Add approx bucket count option (#80990)

This commit is contained in:
Leon Sorokin 2024-02-05 11:09:36 -06:00 committed by GitHub
parent ce0c6a9787
commit d5ac9340e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 99 additions and 14 deletions

View File

@ -93,13 +93,14 @@ TODO docs
It extends [OptionsWithLegend](#optionswithlegend) and [OptionsWithTooltip](#optionswithtooltip).
| Property | Type | Required | Default | Description |
|----------------|-----------------------------------------|----------|---------|----------------------------------------------------------------------------|
| `legend` | [VizLegendOptions](#vizlegendoptions) | **Yes** | | *(Inherited from [OptionsWithLegend](#optionswithlegend))*<br/>TODO docs |
| `tooltip` | [VizTooltipOptions](#viztooltipoptions) | **Yes** | | *(Inherited from [OptionsWithTooltip](#optionswithtooltip))*<br/>TODO docs |
| `bucketOffset` | int32 | No | `0` | Offset buckets by this amount |
| `bucketSize` | integer | No | | Size of each bucket |
| `combine` | boolean | No | | Combines multiple series into a single histogram |
| Property | Type | Required | Default | Description |
|----------------|-----------------------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
| `legend` | [VizLegendOptions](#vizlegendoptions) | **Yes** | | *(Inherited from [OptionsWithLegend](#optionswithlegend))*<br/>TODO docs |
| `tooltip` | [VizTooltipOptions](#viztooltipoptions) | **Yes** | | *(Inherited from [OptionsWithTooltip](#optionswithtooltip))*<br/>TODO docs |
| `bucketCount` | integer | No | `30` | Bucket count (approx)<br/>Constraint: `>0 & <=2147483647`. |
| `bucketOffset` | number | No | `0` | Offset buckets by this amount<br/>Constraint: `>=-340282346638528859811704183484516925440 & <=340282346638528859811704183484516925440`. |
| `bucketSize` | integer | No | | Size of each bucket |
| `combine` | boolean | No | | Combines multiple series into a single histogram |
### OptionsWithLegend

View File

@ -38,10 +38,13 @@ export const histogramBucketSizes = [
];
/* eslint-enable */
const DEFAULT_BUCKET_COUNT = 30;
const histFilter: number[] = [];
const histSort = (a: number, b: number) => a - b;
export interface HistogramTransformerInputs {
bucketCount?: number;
bucketSize?: string | number;
bucketOffset?: string | number;
combine?: boolean;
@ -51,6 +54,7 @@ export interface HistogramTransformerInputs {
* @alpha
*/
export interface HistogramTransformerOptions {
bucketCount?: number;
bucketSize?: number; // 0 is auto
bucketOffset?: number;
// xMin?: number;
@ -64,6 +68,10 @@ export interface HistogramTransformerOptions {
* @internal
*/
export const histogramFieldInfo = {
bucketCount: {
name: 'Bucket count',
description: 'approx bucket count',
},
bucketSize: {
name: 'Bucket size',
description: undefined,
@ -318,13 +326,12 @@ export function getHistogramFields(frame: DataFrame): HistogramFields | undefine
return undefined;
}
const APPROX_BUCKETS = 20;
/**
* @alpha
*/
export function buildHistogram(frames: DataFrame[], options?: HistogramTransformerOptions): HistogramFields | null {
let bucketSize = options?.bucketSize;
let bucketCount = options?.bucketCount ?? DEFAULT_BUCKET_COUNT;
let bucketOffset = options?.bucketOffset ?? 0;
// if bucket size is auto, try to calc from all numeric fields
@ -364,7 +371,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform
let range = max - min;
const targetSize = range / APPROX_BUCKETS;
const targetSize = range / bucketCount;
// choose bucket
for (let i = 0; i < histogramBucketSizes.length; i++) {

View File

@ -14,6 +14,10 @@ import * as common from '@grafana/schema';
export const pluginVersion = "10.4.0-pre";
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip {
/**
* Bucket count (approx)
*/
bucketCount?: number;
/**
* Offset buckets by this amount
*/
@ -29,6 +33,7 @@ export interface Options extends common.OptionsWithLegend, common.OptionsWithToo
}
export const defaultOptions: Partial<Options> = {
bucketCount: 30,
bucketOffset: 0,
};

View File

@ -28,10 +28,21 @@ export const HistogramTransformerEditor = ({
const labelWidth = 18;
const [isInvalid, setInvalid] = useState({
bucketCount: !numberOrVariableValidator(options.bucketCount || ''),
bucketSize: !numberOrVariableValidator(options.bucketSize || ''),
bucketOffset: !numberOrVariableValidator(options.bucketOffset || ''),
});
const onBucketCountChanged = useCallback(
(val?: number) => {
onChange({
...options,
bucketCount: val,
});
},
[onChange, options]
);
const onBucketSizeChanged = useCallback(
(val?: number) => {
onChange({
@ -52,6 +63,18 @@ export const HistogramTransformerEditor = ({
[onChange, options]
);
const onVariableBucketCountChanged = useCallback(
(value: string) => {
setInvalid({ ...isInvalid, bucketCount: !numberOrVariableValidator(value) });
onChange({
...options,
bucketCount: Number(value) === 0 ? undefined : Number(value),
});
},
[onChange, options, isInvalid]
);
const onVariableBucketSizeChanged = useCallback(
(value: string) => {
setInvalid({ ...isInvalid, bucketSize: !numberOrVariableValidator(value) });
@ -105,6 +128,20 @@ export const HistogramTransformerEditor = ({
return (
<div>
<InlineFieldRow>
<InlineField
labelWidth={labelWidth}
label={histogramFieldInfo.bucketCount.name}
tooltip={histogramFieldInfo.bucketCount.description}
>
<NumberInput
value={options.bucketCount}
placeholder="Default: 30"
onChange={onBucketCountChanged}
min={0}
/>
</InlineField>
</InlineFieldRow>
<InlineFieldRow>
<InlineField
labelWidth={labelWidth}
@ -138,6 +175,22 @@ export const HistogramTransformerEditor = ({
return (
<div>
<InlineFieldRow>
<InlineField
invalid={isInvalid.bucketCount}
error={'Value needs to be an integer or a variable'}
labelWidth={labelWidth}
label={histogramFieldInfo.bucketCount.name}
tooltip={histogramFieldInfo.bucketCount.description}
>
<SuggestionsInput
suggestions={variables}
value={options.bucketCount}
placeholder="Default: 30"
onChange={onVariableBucketCountChanged}
/>
</InlineField>
</InlineFieldRow>
<InlineFieldRow>
<InlineField
invalid={isInvalid.bucketSize}

View File

@ -37,6 +37,7 @@ function incrRoundUp(num: number, incr: number) {
export interface HistogramProps extends Themeable2 {
options: Options; // used for diff
alignedFrame: DataFrame; // This could take HistogramFields
bucketCount?: number;
bucketSize: number;
width: number;
height: number;
@ -310,13 +311,14 @@ export class Histogram extends React.Component<HistogramProps, State> {
}
componentDidUpdate(prevProps: HistogramProps) {
const { structureRev, alignedFrame, bucketSize } = this.props;
const { structureRev, alignedFrame, bucketSize, bucketCount } = this.props;
if (alignedFrame !== prevProps.alignedFrame) {
let newState = this.prepState(this.props, false);
if (newState) {
const shouldReconfig =
bucketCount !== prevProps.bucketCount ||
bucketSize !== prevProps.bucketSize ||
this.props.options !== prevProps.options ||
this.state.config === undefined ||

View File

@ -65,6 +65,7 @@ export const HistogramPanel = ({ data, options, width, height }: Props) => {
height={height}
alignedFrame={histogram}
bucketSize={bucketSize}
bucketCount={options.bucketCount}
>
{(config, alignedFrame) => {
return null; // <TooltipPlugin data={alignedFrame} config={config} mode={options.tooltip.mode} timeZone={timeZone} />;

View File

@ -17,6 +17,16 @@ export const plugin = new PanelPlugin<Options, FieldConfig>(HistogramPanel)
editor: () => null, // empty editor
showIf: (opts, data) => originalDataHasHistogram(data),
})
.addNumberInput({
path: 'bucketCount',
name: histogramFieldInfo.bucketCount.name,
description: histogramFieldInfo.bucketCount.description,
settings: {
placeholder: `Default: ${defaultOptions.bucketCount}`,
min: 0,
},
showIf: (opts, data) => !originalDataHasHistogram(data),
})
.addNumberInput({
path: 'bucketSize',
name: histogramFieldInfo.bucketSize.name,
@ -33,10 +43,9 @@ export const plugin = new PanelPlugin<Options, FieldConfig>(HistogramPanel)
name: histogramFieldInfo.bucketOffset.name,
description: histogramFieldInfo.bucketOffset.description,
settings: {
placeholder: '0',
placeholder: `Default: ${defaultOptions.bucketOffset}`,
min: 0,
},
defaultValue: defaultOptions.bucketOffset,
showIf: (opts, data) => !originalDataHasHistogram(data),
})
.addBooleanSwitch({

View File

@ -29,10 +29,12 @@ composableKinds: PanelCfg: {
common.OptionsWithLegend
common.OptionsWithTooltip
//Bucket count (approx)
bucketCount?: int32 & >0 | *30
//Size of each bucket
bucketSize?: int32
//Offset buckets by this amount
bucketOffset?: int32 | *0
bucketOffset?: float32 | *0
//Combines multiple series into a single histogram
combine?: bool
} @cuetsy(kind="interface")

View File

@ -11,6 +11,10 @@
import * as common from '@grafana/schema';
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip {
/**
* Bucket count (approx)
*/
bucketCount?: number;
/**
* Offset buckets by this amount
*/
@ -26,6 +30,7 @@ export interface Options extends common.OptionsWithLegend, common.OptionsWithToo
}
export const defaultOptions: Partial<Options> = {
bucketCount: 30,
bucketOffset: 0,
};