mirror of
https://github.com/grafana/grafana.git
synced 2024-12-26 00:41:20 -06:00
DataFrame: Add explicit histogram frame type (panel & transforms) (#61195)
This commit is contained in:
parent
86b5fbbf60
commit
be1c5e13d5
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,7 @@ describe('histogram frames frames', () => {
|
||||
"config": {
|
||||
"unit": "mph",
|
||||
},
|
||||
"name": "BucketMin",
|
||||
"name": "xMin",
|
||||
"values": [
|
||||
1,
|
||||
2,
|
||||
@ -52,7 +52,7 @@ describe('histogram frames frames', () => {
|
||||
"config": {
|
||||
"unit": "mph",
|
||||
},
|
||||
"name": "BucketMax",
|
||||
"name": "xMax",
|
||||
"values": [
|
||||
2,
|
||||
3,
|
||||
@ -145,7 +145,7 @@ describe('histogram frames frames', () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"name": "BucketMin",
|
||||
"name": "xMin",
|
||||
"values": [
|
||||
1,
|
||||
2,
|
||||
@ -159,7 +159,7 @@ describe('histogram frames frames', () => {
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "BucketMax",
|
||||
"name": "xMax",
|
||||
"values": [
|
||||
2,
|
||||
3,
|
||||
@ -173,7 +173,7 @@ describe('histogram frames frames', () => {
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Count",
|
||||
"name": "count",
|
||||
"values": [
|
||||
1,
|
||||
1,
|
||||
|
@ -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';
|
||||
@ -100,19 +101,33 @@ export const histogramTransformer: SynchronousDataTransformerInfo<HistogramTrans
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const histogramFrameBucketMinFieldName = 'BucketMin';
|
||||
export const histogramFrameBucketMinFieldName = 'xMin';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const histogramFrameBucketMaxFieldName = 'BucketMax';
|
||||
export function isHistogramFrameBucketMinFieldName(v: string) {
|
||||
return v === histogramFrameBucketMinFieldName || v === 'BucketMin'; // REMOVE 'BuckentMin/Max'
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const histogramFrameBucketMaxFieldName = 'xMax';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function isHistogramFrameBucketMaxFieldName(v: string) {
|
||||
return v === histogramFrameBucketMaxFieldName || v === 'BucketMax'; // REMOVE 'BuckentMin/Max'
|
||||
}
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export interface HistogramFields {
|
||||
bucketMin: Field;
|
||||
bucketMax: Field;
|
||||
xMin: Field;
|
||||
xMax: Field;
|
||||
counts: Field[]; // frequency
|
||||
}
|
||||
|
||||
@ -122,22 +137,46 @@ export interface HistogramFields {
|
||||
* @alpha
|
||||
*/
|
||||
export function getHistogramFields(frame: DataFrame): HistogramFields | undefined {
|
||||
let bucketMin: Field | undefined = undefined;
|
||||
let bucketMax: Field | undefined = undefined;
|
||||
let xMin: Field | undefined = undefined;
|
||||
let xMax: Field | undefined = undefined;
|
||||
const counts: Field[] = [];
|
||||
for (const field of frame.fields) {
|
||||
if (field.name === histogramFrameBucketMinFieldName) {
|
||||
bucketMin = field;
|
||||
} else if (field.name === histogramFrameBucketMaxFieldName) {
|
||||
bucketMax = field;
|
||||
if (isHistogramFrameBucketMinFieldName(field.name)) {
|
||||
xMin = field;
|
||||
} else if (isHistogramFrameBucketMaxFieldName(field.name)) {
|
||||
xMax = field;
|
||||
} else if (field.type === FieldType.number) {
|
||||
counts.push(field);
|
||||
}
|
||||
}
|
||||
if (bucketMin && bucketMax && counts.length) {
|
||||
|
||||
// guess bucket size from single explicit bucket field
|
||||
if (!xMax && xMin && xMin.values.length > 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,
|
||||
};
|
||||
}
|
||||
@ -250,7 +289,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform
|
||||
}
|
||||
}
|
||||
|
||||
const bucketMin: Field = {
|
||||
const xMin: Field = {
|
||||
name: histogramFrameBucketMinFieldName,
|
||||
values: new ArrayVector(joinedHists[0]),
|
||||
type: FieldType.number,
|
||||
@ -263,8 +302,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!)),
|
||||
};
|
||||
@ -279,7 +318,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform
|
||||
counts = [
|
||||
{
|
||||
...counts[0],
|
||||
name: 'Count',
|
||||
name: 'count',
|
||||
values: new ArrayVector(vals),
|
||||
type: FieldType.number,
|
||||
state: undefined,
|
||||
@ -292,8 +331,8 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform
|
||||
}
|
||||
|
||||
return {
|
||||
bucketMin,
|
||||
bucketMax,
|
||||
xMin,
|
||||
xMax,
|
||||
counts,
|
||||
};
|
||||
}
|
||||
@ -364,13 +403,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
|
||||
@ -380,7 +419,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],
|
||||
};
|
||||
}
|
||||
|
@ -30,4 +30,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',
|
||||
}
|
||||
|
@ -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<HistogramProps, State> {
|
||||
|
||||
renderLegend(config: UPlotConfigBuilder) {
|
||||
const { legend } = this.props;
|
||||
if (!config || legend.showLegend === false || !this.props.rawSeries) {
|
||||
|
||||
if (!config || legend.showLegend === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <PlotLegend data={this.props.rawSeries} config={config} maxHeight="35%" maxWidth="60%" {...legend} />;
|
||||
return <PlotLegend data={this.props.rawSeries!} config={config} maxHeight="35%" maxWidth="60%" {...legend} />;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: HistogramProps) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user