From 24abde98168503302f99b54d2253ffc8e780b691 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Tue, 10 Jan 2023 18:37:08 -0600 Subject: [PATCH] [v9.3.x] DataFrame: Add explicit histogram frame type (panel & transforms) --- .../transformers/histogram.test.ts | 10 +- .../transformations/transformers/histogram.ts | 94 ++++++++++++++----- .../grafana-data/src/types/dataFrameTypes.ts | 6 ++ .../app/plugins/panel/histogram/Histogram.tsx | 12 +-- .../panel/histogram/HistogramPanel.tsx | 14 +++ public/app/plugins/panel/histogram/utils.ts | 8 +- 6 files changed, 102 insertions(+), 42 deletions(-) diff --git a/packages/grafana-data/src/transformations/transformers/histogram.test.ts b/packages/grafana-data/src/transformations/transformers/histogram.test.ts index a2f0b1952a2..11c23fb9de3 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.test.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.test.ts @@ -35,7 +35,7 @@ describe('histogram frames frames', () => { "config": Object { "unit": "mph", }, - "name": "BucketMin", + "name": "xMin", "values": Array [ 1, 2, @@ -52,7 +52,7 @@ describe('histogram frames frames', () => { "config": Object { "unit": "mph", }, - "name": "BucketMax", + "name": "xMax", "values": Array [ 2, 3, @@ -145,7 +145,7 @@ describe('histogram frames frames', () => { ).toMatchInlineSnapshot(` Array [ Object { - "name": "BucketMin", + "name": "xMin", "values": Array [ 1, 2, @@ -159,7 +159,7 @@ describe('histogram frames frames', () => { ], }, Object { - "name": "BucketMax", + "name": "xMax", "values": Array [ 2, 3, @@ -173,7 +173,7 @@ describe('histogram frames frames', () => { ], }, Object { - "name": "Count", + "name": "count", "values": Array [ 1, 1, diff --git a/packages/grafana-data/src/transformations/transformers/histogram.ts b/packages/grafana-data/src/transformations/transformers/histogram.ts index 818e48dc9dd..29fbfab2ffd 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.ts @@ -2,8 +2,9 @@ import { map } from 'rxjs/operators'; import { getDisplayProcessor } from '../../field'; import { createTheme, GrafanaTheme2 } from '../../themes'; -import { SynchronousDataTransformerInfo } from '../../types'; +import { DataFrameType, SynchronousDataTransformerInfo } from '../../types'; import { DataFrame, Field, FieldConfig, FieldType } from '../../types/dataFrame'; +import { roundDecimals } from '../../utils'; import { ArrayVector } from '../../vector/ArrayVector'; import { DataTransformerID } from './ids'; @@ -99,19 +100,33 @@ export const histogramTransformer: SynchronousDataTransformerInfo 1) { + let vals = xMin.values.toArray(); + let bucketSize = roundDecimals(vals[1] - vals[0], 6); + + xMax = { + ...xMin, + name: histogramFrameBucketMaxFieldName, + values: new ArrayVector(vals.map((v) => v + bucketSize)), + }; + } + + if (!xMin && xMax && xMax?.values.length > 1) { + let vals = xMax.values.toArray(); + let bucketSize = roundDecimals(vals[1] - vals[0], 6); + + xMin = { + ...xMax, + name: histogramFrameBucketMinFieldName, + values: new ArrayVector(vals.map((v) => v - bucketSize)), + }; + } + + if (xMin && xMax && counts.length) { return { - bucketMin, - bucketMax, + xMin, + xMax, counts, }; } @@ -249,7 +288,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform } } - const bucketMin: Field = { + const xMin: Field = { name: histogramFrameBucketMinFieldName, values: new ArrayVector(joinedHists[0]), type: FieldType.number, @@ -262,8 +301,8 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform decimals: bucketDecimals, }, }; - const bucketMax = { - ...bucketMin, + const xMax = { + ...xMin, name: histogramFrameBucketMaxFieldName, values: new ArrayVector(joinedHists[0].map((v) => v + bucketSize!)), }; @@ -278,7 +317,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform counts = [ { ...counts[0], - name: 'Count', + name: 'count', values: new ArrayVector(vals), type: FieldType.number, state: undefined, @@ -291,8 +330,8 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform } return { - bucketMin, - bucketMax, + xMin, + xMax, counts, }; } @@ -363,13 +402,13 @@ function histogram( * @internal */ export function histogramFieldsToFrame(info: HistogramFields, theme?: GrafanaTheme2): DataFrame { - if (!info.bucketMin.display) { + if (!info.xMin.display) { const display = getDisplayProcessor({ - field: info.bucketMin, + field: info.xMin, theme: theme ?? createTheme(), }); - info.bucketMin.display = display; - info.bucketMax.display = display; + info.xMin.display = display; + info.xMax.display = display; } // ensure updated units are reflected on the count field used for y axis formatting @@ -379,7 +418,10 @@ export function histogramFieldsToFrame(info: HistogramFields, theme?: GrafanaThe }); return { - fields: [info.bucketMin, info.bucketMax, ...info.counts], - length: info.bucketMin.values.length, + length: info.xMin.values.length, + meta: { + type: DataFrameType.Histogram, + }, + fields: [info.xMin, info.xMax, ...info.counts], }; } diff --git a/packages/grafana-data/src/types/dataFrameTypes.ts b/packages/grafana-data/src/types/dataFrameTypes.ts index e84f4a0a97b..724157c64ac 100644 --- a/packages/grafana-data/src/types/dataFrameTypes.ts +++ b/packages/grafana-data/src/types/dataFrameTypes.ts @@ -26,4 +26,10 @@ export enum DataFrameType { * If the y value is actually ordinal, use `meta.custom` to specify the bucket lookup values */ HeatmapCells = 'heatmap-cells', + + /** + * Explicit fields for: + * xMin, xMax, count + */ + Histogram = 'histogram', } diff --git a/public/app/plugins/panel/histogram/Histogram.tsx b/public/app/plugins/panel/histogram/Histogram.tsx index 85721e7d7d0..0616e914829 100644 --- a/public/app/plugins/panel/histogram/Histogram.tsx +++ b/public/app/plugins/panel/histogram/Histogram.tsx @@ -175,7 +175,7 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => { let seriesIndex = 0; - // assumes BucketMax is [1] + // assumes xMin is [0], xMax is [1] for (let i = 2; i < frame.fields.length; i++) { const field = frame.fields[i]; @@ -209,10 +209,7 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => { softMax: customConfig.axisSoftMax, // The following properties are not used in the uPlot config, but are utilized as transport for legend config - dataFrameFieldIndex: { - fieldIndex: 1, - frameIndex: i - 2, - }, + dataFrameFieldIndex: field.state.origin, }); } @@ -272,11 +269,12 @@ export class Histogram extends React.Component { renderLegend(config: UPlotConfigBuilder) { const { legend } = this.props; - if (!config || legend.showLegend === false || !this.props.rawSeries) { + + if (!config || legend.showLegend === false) { return null; } - return ; + return ; } componentDidUpdate(prevProps: HistogramProps) { diff --git a/public/app/plugins/panel/histogram/HistogramPanel.tsx b/public/app/plugins/panel/histogram/HistogramPanel.tsx index 81513102dd6..fba60c5c61d 100644 --- a/public/app/plugins/panel/histogram/HistogramPanel.tsx +++ b/public/app/plugins/panel/histogram/HistogramPanel.tsx @@ -16,6 +16,20 @@ export const HistogramPanel = ({ data, options, width, height }: Props) => { if (!data?.series?.length) { return undefined; } + + // stamp origins for legend's calcs (from raw values) + data.series.forEach((frame, frameIndex) => { + frame.fields.forEach((field, fieldIndex) => { + field.state = { + ...field.state, + origin: { + frameIndex, + fieldIndex, + }, + }; + }); + }); + if (data.series.length === 1) { const info = getHistogramFields(data.series[0]); if (info) { diff --git a/public/app/plugins/panel/histogram/utils.ts b/public/app/plugins/panel/histogram/utils.ts index a98e8768f3d..51d041050bf 100644 --- a/public/app/plugins/panel/histogram/utils.ts +++ b/public/app/plugins/panel/histogram/utils.ts @@ -1,7 +1,7 @@ import { DataFrame, FieldType } from '@grafana/data'; import { - histogramFrameBucketMinFieldName, - histogramFrameBucketMaxFieldName, + isHistogramFrameBucketMinFieldName, + isHistogramFrameBucketMaxFieldName, } from '@grafana/data/src/transformations/transformers/histogram'; export function originalDataHasHistogram(frames?: DataFrame[]): boolean { @@ -14,8 +14,8 @@ export function originalDataHasHistogram(frames?: DataFrame[]): boolean { } if ( - frame.fields[0].name !== histogramFrameBucketMinFieldName || - frame.fields[1].name !== histogramFrameBucketMaxFieldName + !isHistogramFrameBucketMinFieldName(frame.fields[0].name) || + !isHistogramFrameBucketMaxFieldName(frame.fields[1].name) ) { return false; }