[ML] Extend population preview chart to show actual and typical value (#67569)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Quynh Nguyen 2020-06-03 20:19:04 -05:00 committed by GitHub
parent 573409b9f0
commit 2266dd969e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 10 deletions

View file

@ -25,6 +25,7 @@ import {
getTickValues,
numTicksForDateFormat,
removeLabelOverlap,
chartExtendedLimits,
} from '../../util/chart_utils';
import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator';
import { getTimeBucketsFromCache } from '../../util/time_buckets';
@ -98,7 +99,7 @@ export class ExplorerChartDistribution extends React.Component {
const filteredChartData = init(config);
drawRareChart(filteredChartData);
function init({ chartData }) {
function init({ chartData, functionDescription }) {
const $el = $('.ml-explorer-chart');
// Clear any existing elements from the visualization,
@ -137,22 +138,24 @@ export class ExplorerChartDistribution extends React.Component {
});
if (chartType === CHART_TYPE.POPULATION_DISTRIBUTION) {
const focusData = chartData
.filter((d) => {
return d.entity === highlight;
})
.map((d) => d.value);
const focusExtent = d3.extent(focusData);
const focusData = chartData.filter((d) => {
return d.entity === highlight;
});
// calculate the max y domain based on value, typical, and actual
// also sets the min to be at least 0 if the series function type is `count`
const { min: yScaleDomainMin, max: yScaleDomainMax } = chartExtendedLimits(
focusData,
functionDescription
);
// now again filter chartData to include only the data points within the domain
chartData = chartData.filter((d) => {
return d.value <= focusExtent[1];
return d.value <= yScaleDomainMax;
});
lineChartYScale = d3.scale
.linear()
.range([chartHeight, 0])
.domain([0, focusExtent[1]])
.domain([yScaleDomainMin < 0 ? yScaleDomainMin : 0, yScaleDomainMax])
.nice();
} else if (chartType === CHART_TYPE.EVENT_DISTRIBUTION) {
// avoid overflowing the border of the highlighted area

View file

@ -65,6 +65,44 @@ export function chartLimits(data = []) {
return limits;
}
export function chartExtendedLimits(data = [], functionDescription) {
let _min = Infinity;
let _max = -Infinity;
data.forEach((d) => {
let metricValue = d.value;
const actualValue = Array.isArray(d.actual) ? d.actual[0] : d.actual;
const typicalValue = Array.isArray(d.typical) ? d.typical[0] : d.typical;
if (metricValue === null && d.anomalyScore !== undefined && d.actual !== undefined) {
// If an anomaly coincides with a gap in the data, use the anomaly actual value.
metricValue = actualValue;
}
if (d.anomalyScore !== undefined) {
_min = Math.min(_min, metricValue, actualValue, typicalValue);
_max = Math.max(_max, metricValue, actualValue, typicalValue);
} else {
_min = Math.min(_min, metricValue);
_max = Math.max(_max, metricValue);
}
});
const limits = { max: _max, min: _min };
// add padding of 5% of the difference between max and min
// if we ended up with the same value for both of them
if (limits.max === limits.min) {
const padding = limits.max * 0.05;
limits.max += padding;
limits.min -= padding;
}
// makes sure the domain starts at 0 if the aggregation is by count
// since the number should always be positive
if (functionDescription === 'count' && limits.min < 0) {
limits.min = 0;
}
return limits;
}
export function drawLineChartDots(data, lineChartGroup, lineChartValuesLine, radius = 1.5) {
// We need to do this because when creating a line for a chart which has data gaps,
// if there are single datapoints without any valid data before and after them,