mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge PR #8613
This commit is contained in:
commit
560aec50b3
@ -29,12 +29,17 @@ export class DataProcessor {
|
||||
});
|
||||
}
|
||||
case 'histogram': {
|
||||
let histogramDataList = [
|
||||
{
|
||||
target: 'count',
|
||||
datapoints: _.concat([], _.flatten(_.map(options.dataList, 'datapoints'))),
|
||||
},
|
||||
];
|
||||
let histogramDataList;
|
||||
if (this.panel.stack) {
|
||||
histogramDataList = options.dataList;
|
||||
} else {
|
||||
histogramDataList = [
|
||||
{
|
||||
target: 'count',
|
||||
datapoints: _.concat([], _.flatten(_.map(options.dataList, 'datapoints'))),
|
||||
},
|
||||
];
|
||||
}
|
||||
return histogramDataList.map((item, index) => {
|
||||
return this.timeSeriesHandler(item, index, options);
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ import { appEvents, coreModule, updateLegendValues } from 'app/core/core';
|
||||
import GraphTooltip from './graph_tooltip';
|
||||
import { ThresholdManager } from './threshold_manager';
|
||||
import { EventManager } from 'app/features/annotations/all';
|
||||
import { convertValuesToHistogram, getSeriesValues } from './histogram';
|
||||
import { convertToHistogramData } from './histogram';
|
||||
import config from 'app/core/config';
|
||||
|
||||
/** @ngInject **/
|
||||
@ -236,16 +236,14 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
|
||||
}
|
||||
case 'histogram': {
|
||||
let bucketSize: number;
|
||||
let values = getSeriesValues(data);
|
||||
|
||||
if (data.length && values.length) {
|
||||
if (data.length) {
|
||||
let histMin = _.min(_.map(data, s => s.stats.min));
|
||||
let histMax = _.max(_.map(data, s => s.stats.max));
|
||||
let ticks = panel.xaxis.buckets || panelWidth / 50;
|
||||
bucketSize = tickStep(histMin, histMax, ticks);
|
||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||
data[0].data = histogram;
|
||||
options.series.bars.barWidth = bucketSize * 0.8;
|
||||
data = convertToHistogramData(data, bucketSize, ctrl.hiddenSeries, histMin, histMax);
|
||||
} else {
|
||||
bucketSize = 0;
|
||||
}
|
||||
@ -413,7 +411,13 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
|
||||
let defaultTicks = panelWidth / 50;
|
||||
|
||||
if (data.length && bucketSize) {
|
||||
ticks = _.map(data[0].data, point => point[0]);
|
||||
let tick_values = [];
|
||||
for (let d of data) {
|
||||
for (let point of d.data) {
|
||||
tick_values[point[0]] = true;
|
||||
}
|
||||
}
|
||||
ticks = Object.keys(tick_values).map(v => Number(v));
|
||||
min = _.min(ticks);
|
||||
max = _.max(ticks);
|
||||
|
||||
|
@ -29,16 +29,22 @@ export function getSeriesValues(dataList: TimeSeries[]): number[] {
|
||||
* @param values
|
||||
* @param bucketSize
|
||||
*/
|
||||
export function convertValuesToHistogram(values: number[], bucketSize: number): any[] {
|
||||
export function convertValuesToHistogram(values: number[], bucketSize: number, min: number, max: number): any[] {
|
||||
let histogram = {};
|
||||
|
||||
let minBound = getBucketBound(min, bucketSize);
|
||||
let maxBound = getBucketBound(max, bucketSize);
|
||||
let bound = minBound;
|
||||
let n = 0;
|
||||
while (bound <= maxBound) {
|
||||
histogram[bound] = 0;
|
||||
bound = minBound + bucketSize * n;
|
||||
n++;
|
||||
}
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let bound = getBucketBound(values[i], bucketSize);
|
||||
if (histogram[bound]) {
|
||||
histogram[bound] = histogram[bound] + 1;
|
||||
} else {
|
||||
histogram[bound] = 1;
|
||||
}
|
||||
histogram[bound] = histogram[bound] + 1;
|
||||
}
|
||||
|
||||
let histogam_series = _.map(histogram, (count, bound) => {
|
||||
@ -49,6 +55,31 @@ export function convertValuesToHistogram(values: number[], bucketSize: number):
|
||||
return _.sortBy(histogam_series, point => point[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert series into array of histogram data.
|
||||
* @param data Array of series
|
||||
* @param bucketSize
|
||||
*/
|
||||
export function convertToHistogramData(
|
||||
data: any,
|
||||
bucketSize: number,
|
||||
hiddenSeries: any,
|
||||
min: number,
|
||||
max: number
|
||||
): any[] {
|
||||
return data.map(series => {
|
||||
let values = getSeriesValues([series]);
|
||||
series.histogram = true;
|
||||
if (!hiddenSeries[series.alias]) {
|
||||
let histogram = convertValuesToHistogram(values, bucketSize, min, max);
|
||||
series.data = histogram;
|
||||
} else {
|
||||
series.data = [];
|
||||
}
|
||||
return series;
|
||||
});
|
||||
}
|
||||
|
||||
function getBucketBound(value: number, bucketSize: number): number {
|
||||
return Math.floor(value / bucketSize) * bucketSize;
|
||||
}
|
||||
|
@ -407,4 +407,48 @@ describe('grafanaGraph', function() {
|
||||
},
|
||||
10
|
||||
);
|
||||
|
||||
graphScenario('when graph is histogram, and enable stack', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.xaxis.mode = 'histogram';
|
||||
ctrl.panel.stack = true;
|
||||
ctrl.hiddenSeries = {};
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series1',
|
||||
});
|
||||
data[1] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series2',
|
||||
});
|
||||
});
|
||||
|
||||
it('should calculate correct histogram', function() {
|
||||
expect(ctx.plotData[0].data[0][0]).to.be(100);
|
||||
expect(ctx.plotData[0].data[0][1]).to.be(2);
|
||||
expect(ctx.plotData[1].data[0][0]).to.be(100);
|
||||
expect(ctx.plotData[1].data[0][1]).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
graphScenario('when graph is histogram, and some series are hidden', function(ctx) {
|
||||
ctx.setup(function(ctrl, data) {
|
||||
ctrl.panel.xaxis.mode = 'histogram';
|
||||
ctrl.panel.stack = false;
|
||||
ctrl.hiddenSeries = { series2: true };
|
||||
data[0] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series1',
|
||||
});
|
||||
data[1] = new TimeSeries({
|
||||
datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
|
||||
alias: 'series2',
|
||||
});
|
||||
});
|
||||
|
||||
it('should calculate correct histogram', function() {
|
||||
expect(ctx.plotData[0].data[0][0]).to.be(100);
|
||||
expect(ctx.plotData[0].data[0][1]).to.be(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -13,15 +13,15 @@ describe('Graph Histogam Converter', function() {
|
||||
bucketSize = 10;
|
||||
let expected = [[0, 2], [10, 3], [20, 2]];
|
||||
|
||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||
let histogram = convertValuesToHistogram(values, bucketSize, 1, 29);
|
||||
expect(histogram).toMatchObject(expected);
|
||||
});
|
||||
|
||||
it('Should not add empty buckets', () => {
|
||||
bucketSize = 5;
|
||||
let expected = [[0, 2], [10, 2], [15, 1], [20, 1], [25, 1]];
|
||||
let expected = [[0, 2], [5, 0], [10, 2], [15, 1], [20, 1], [25, 1]];
|
||||
|
||||
let histogram = convertValuesToHistogram(values, bucketSize);
|
||||
let histogram = convertValuesToHistogram(values, bucketSize, 1, 29);
|
||||
expect(histogram).toMatchObject(expected);
|
||||
});
|
||||
});
|
||||
|
@ -71,7 +71,7 @@
|
||||
|
||||
<div class="section gf-form-group">
|
||||
<h5 class="section-heading">Stacking & Null value</h5>
|
||||
<gf-form-switch class="gf-form" label="Stack" label-class="width-7" checked="ctrl.panel.stack" on-change="ctrl.render()">
|
||||
<gf-form-switch class="gf-form" label="Stack" label-class="width-7" checked="ctrl.panel.stack" on-change="ctrl.refresh()">
|
||||
</gf-form-switch>
|
||||
<gf-form-switch class="gf-form" ng-show="ctrl.panel.stack" label="Percent" label-class="width-7" checked="ctrl.panel.percentage" on-change="ctrl.render()">
|
||||
</gf-form-switch>
|
||||
|
Loading…
Reference in New Issue
Block a user