Commit cf1be5fd by Mitsuhiro Tanda

support multiple histogram series

parent e985a9cd
......@@ -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,15 +236,15 @@ 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;
data = convertToHistogramData(data, bucketSize, ctrl.hiddenSeries, panel.stack, histMin, histMax);
options.series.bars.barWidth = bucketSize * 0.8;
} else {
bucketSize = 0;
......@@ -413,7 +413,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,33 @@ 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
* @param stack
*/
export function convertToHistogramData(
data: any,
bucketSize: number,
hiddenSeries: any,
stack = false,
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>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment