TimeSeries: Support for log scale and negative numbers (#54812)

This commit is contained in:
Leon Sorokin 2022-09-08 11:52:57 -05:00 committed by GitHub
parent 6a7e1caced
commit eb90d0c4b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 86 additions and 28 deletions

View File

@ -21,7 +21,7 @@ GraphTransform: "constant" | "negative-Y" @cue
LineInterpolation: "linear" | "smooth" | "stepBefore" | "stepAfter" @cuetsy(kind="enum")
// TODO docs
ScaleDistribution: "linear" | "log" | "ordinal" @cuetsy(kind="enum")
ScaleDistribution: "linear" | "log" | "ordinal" | "symlog" @cuetsy(kind="enum")
// TODO docs
GraphGradientMode: "none" | "opacity" | "hue" | "scheme" @cuetsy(kind="enum")
@ -83,6 +83,7 @@ PointsConfig: {
ScaleDistributionConfig: {
type: ScaleDistribution
log?: number
linearThreshold?: number
} @cuetsy(kind="interface")
// TODO docs

View File

@ -46,6 +46,7 @@ export enum ScaleDistribution {
Linear = 'linear',
Log = 'log',
Ordinal = 'ordinal',
Symlog = 'symlog',
}
export enum GraphGradientMode {
@ -116,6 +117,7 @@ export interface PointsConfig {
}
export interface ScaleDistributionConfig {
linearThreshold?: number;
log?: number;
type: ScaleDistribution;
}

View File

@ -100,6 +100,7 @@ Object {
],
"scales": Object {
"__fixed/na-na/na-na/auto/linear/na": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,

View File

@ -217,6 +217,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{
direction: ScaleDirection.Up,
distribution: customConfig.scaleDistribution?.type,
log: customConfig.scaleDistribution?.log,
linearThreshold: customConfig.scaleDistribution?.linearThreshold,
min: field.config.min,
max: field.config.max,
softMin: customConfig.axisSoftMin,

View File

@ -124,6 +124,7 @@ describe('UPlotConfigBuilder', () => {
"time": true,
},
"scale-y": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -209,6 +210,7 @@ describe('UPlotConfigBuilder', () => {
],
"scales": Object {
"scale-y": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -274,6 +276,7 @@ describe('UPlotConfigBuilder', () => {
],
"scales": Object {
"scale-y": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -340,6 +343,7 @@ describe('UPlotConfigBuilder', () => {
],
"scales": Object {
"scale-y": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,

View File

@ -17,6 +17,7 @@ export interface ScaleProps {
orientation: ScaleOrientation;
direction: ScaleDirection;
log?: number;
linearThreshold?: number;
centeredZero?: boolean;
decimals?: DecimalCount;
}
@ -41,15 +42,21 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
centeredZero,
decimals,
} = this.props;
const distr = this.props.distribution;
const distribution = !isTime
? {
distr:
this.props.distribution === ScaleDistribution.Log
distr === ScaleDistribution.Symlog
? 4
: distr === ScaleDistribution.Log
? 3
: this.props.distribution === ScaleDistribution.Ordinal
: distr === ScaleDistribution.Ordinal
? 2
: 1,
log: this.props.distribution === ScaleDistribution.Log ? this.props.log || 2 : undefined,
log: distr === ScaleDistribution.Log || distr === ScaleDistribution.Symlog ? this.props.log ?? 2 : undefined,
asinh: distr === ScaleDistribution.Symlog ? this.props.linearThreshold ?? 1 : undefined,
}
: {};
@ -91,7 +98,7 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
return minMax;
}
if (scale.distr === 1 || scale.distr === 2) {
if (scale.distr === 1 || scale.distr === 2 || scale.distr === 4) {
if (centeredZero) {
let absMin = Math.abs(dataMin!);
let absMax = Math.abs(dataMax!);
@ -100,8 +107,14 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
dataMax = max;
}
// @ts-ignore here we may use hardMin / hardMax to make sure any extra padding is computed from a more accurate delta
minMax = uPlot.rangeNum(hardMinOnly ? hardMin : dataMin, hardMaxOnly ? hardMax : dataMax, rangeConfig);
if (scale.distr === 4) {
// TODO: switch to `, true)` after updating uPlot to 1.6.23+
// see https://github.com/leeoniya/uPlot/issues/749
minMax = uPlot.rangeAsinh(dataMin!, dataMax!, scale.log ?? 10, false);
} else {
// @ts-ignore here we may use hardMin / hardMax to make sure any extra padding is computed from a more accurate delta
minMax = uPlot.rangeNum(hardMinOnly ? hardMin : dataMin, hardMaxOnly ? hardMax : dataMax, rangeConfig);
}
} else if (scale.distr === 3) {
minMax = uPlot.rangeLog(dataMin!, dataMax!, scale.log ?? 10, true);
}

View File

@ -9,7 +9,7 @@ import {
} from '@grafana/data';
import { AxisColorMode, AxisConfig, AxisPlacement, ScaleDistribution, ScaleDistributionConfig } from '@grafana/schema';
import { graphFieldOptions, Select, HorizontalGroup, RadioButtonGroup } from '../../index';
import { graphFieldOptions, Select, RadioButtonGroup, Input, Field } from '../../index';
/**
* @alpha
@ -128,6 +128,10 @@ const DISTRIBUTION_OPTIONS: Array<SelectableValue<ScaleDistribution>> = [
label: 'Logarithmic',
value: ScaleDistribution.Log,
},
{
label: 'Symlog',
value: ScaleDistribution.Symlog,
},
];
const LOG_DISTRIBUTION_OPTIONS: Array<SelectableValue<number>> = [
@ -147,32 +151,48 @@ const LOG_DISTRIBUTION_OPTIONS: Array<SelectableValue<number>> = [
export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps<ScaleDistributionConfig>) => {
const type = value?.type ?? ScaleDistribution.Linear;
return (
<HorizontalGroup>
<RadioButtonGroup
value={type}
options={DISTRIBUTION_OPTIONS}
onChange={(v) => {
onChange({
...value,
type: v!,
log: v === ScaleDistribution.Linear ? undefined : 2,
});
}}
/>
{type === ScaleDistribution.Log && (
<Select
options={LOG_DISTRIBUTION_OPTIONS}
value={value.log || 2}
prefix={'base'}
width={12}
<>
<div style={{ marginBottom: 16 }}>
<RadioButtonGroup
value={type}
options={DISTRIBUTION_OPTIONS}
onChange={(v) => {
onChange({
...value,
log: v.value!,
type: v!,
log: v === ScaleDistribution.Linear ? undefined : value.log ?? 2,
});
}}
/>
</div>
{(type === ScaleDistribution.Log || type === ScaleDistribution.Symlog) && (
<Field label="Log base">
<Select
options={LOG_DISTRIBUTION_OPTIONS}
value={value.log ?? 2}
onChange={(v) => {
onChange({
...value,
log: v.value!,
});
}}
/>
</Field>
)}
</HorizontalGroup>
{type === ScaleDistribution.Symlog && (
<Field label="Linear threshold">
<Input
placeholder="1"
value={value.linearThreshold}
onChange={(v) => {
onChange({
...value,
linearThreshold: Number(v.currentTarget.value),
});
}}
/>
</Field>
)}
</>
);
};

View File

@ -105,6 +105,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -114,6 +115,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,
@ -256,6 +258,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -265,6 +268,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,
@ -407,6 +411,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -416,6 +421,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 2,
@ -558,6 +564,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -567,6 +574,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,
@ -709,6 +717,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -718,6 +727,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,
@ -860,6 +870,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -869,6 +880,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,
@ -1011,6 +1023,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -1020,6 +1033,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,
@ -1162,6 +1176,7 @@ Object {
],
"scales": Object {
"m/s": Object {
"asinh": undefined,
"auto": true,
"dir": 1,
"distr": 1,
@ -1171,6 +1186,7 @@ Object {
"time": undefined,
},
"x": Object {
"asinh": undefined,
"auto": true,
"dir": -1,
"distr": 2,