From 1f1974b2997be471bbec831500bdff59d83079da Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 27 May 2020 18:31:51 -0500 Subject: [PATCH 1/3] [ML] Extend population preview chart to show actual and typical values as well in focusData --- .../explorer_chart_distribution.js | 17 +++++----- .../ml/public/application/util/chart_utils.js | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 6c7c3e9040216..91df210a4b69b 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -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'; @@ -137,22 +138,20 @@ 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 + const { min: yScaleDomainMin, max: yScaleDomainMax } = chartExtendedLimits(focusData); // 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 diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index 2caf964cb9774..9d240b8e1e96d 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -65,6 +65,38 @@ export function chartLimits(data = []) { return limits; } +export function chartExtendedLimits(data = []) { + 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; + } + 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, From c84dde2e8662c04d9245644b27c2c360ef671b81 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Fri, 29 May 2020 07:45:24 -0500 Subject: [PATCH 2/3] [ML] Add check so explorer chart won't extend negative if detector is a count --- .../explorer_charts/explorer_chart_distribution.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 91df210a4b69b..2edad896213c8 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -99,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, @@ -151,7 +151,10 @@ export class ExplorerChartDistribution extends React.Component { lineChartYScale = d3.scale .linear() .range([chartHeight, 0]) - .domain([yScaleDomainMin < 0 ? yScaleDomainMin : 0, yScaleDomainMax]) + .domain([ + yScaleDomainMin < 0 && functionDescription === 'count' ? 0 : yScaleDomainMin, + yScaleDomainMax, + ]) .nice(); } else if (chartType === CHART_TYPE.EVENT_DISTRIBUTION) { // avoid overflowing the border of the highlighted area From 4446f685118c8135c6b8c23cb0e4c15459b97299 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Fri, 29 May 2020 08:35:34 -0500 Subject: [PATCH 3/3] [ML] Update `count` y domain guard to be in chartExtendedLimits --- .../explorer_charts/explorer_chart_distribution.js | 11 ++++++----- .../plugins/ml/public/application/util/chart_utils.js | 8 +++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 2edad896213c8..7a18914957ba9 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -142,7 +142,11 @@ export class ExplorerChartDistribution extends React.Component { return d.entity === highlight; }); // calculate the max y domain based on value, typical, and actual - const { min: yScaleDomainMin, max: yScaleDomainMax } = chartExtendedLimits(focusData); + // 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 <= yScaleDomainMax; @@ -151,10 +155,7 @@ export class ExplorerChartDistribution extends React.Component { lineChartYScale = d3.scale .linear() .range([chartHeight, 0]) - .domain([ - yScaleDomainMin < 0 && functionDescription === 'count' ? 0 : yScaleDomainMin, - yScaleDomainMax, - ]) + .domain([yScaleDomainMin < 0 ? yScaleDomainMin : 0, yScaleDomainMax]) .nice(); } else if (chartType === CHART_TYPE.EVENT_DISTRIBUTION) { // avoid overflowing the border of the highlighted area diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index 9d240b8e1e96d..4ec7c5cb6d819 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -65,7 +65,7 @@ export function chartLimits(data = []) { return limits; } -export function chartExtendedLimits(data = []) { +export function chartExtendedLimits(data = [], functionDescription) { let _min = Infinity; let _max = -Infinity; data.forEach((d) => { @@ -95,6 +95,12 @@ export function chartExtendedLimits(data = []) { 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) {