Skip to content

Commit

Permalink
[ML] Enables display of contextual data for population charts using o…
Browse files Browse the repository at this point in the history
…ther metrics than count. (#24083) (#24148)

- Enables support for showing contextual sampled data in population charts which use detector functions other than just count.
- Use arrow functions where applicable in Anomaly Explorer Charts code.
  • Loading branch information
walterra authored Oct 22, 2018
1 parent b22a1ba commit fe43b08
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export class ExplorerChartDistribution extends React.Component {
return d;
}
})
// Don't use an arrow function since we need access to `this`.
.each(function () {
maxYAxisLabelWidth = Math.max(this.getBBox().width + yAxis.tickPadding(), maxYAxisLabelWidth);
})
Expand Down Expand Up @@ -342,16 +343,17 @@ export class ExplorerChartDistribution extends React.Component {
// Create any new dots that are needed i.e. if number of chart points has increased.
dots.enter().append('circle')
.attr('r', LINE_CHART_ANOMALY_RADIUS)
// Don't use an arrow function since we need access to `this`.
.on('mouseover', function (d) {
showLineChartTooltip(d, this);
})
.on('mouseout', () => mlChartTooltipService.hide());

// Update all dots to new positions.
const threshold = mlSelectSeverityService.state.get('threshold');
dots.attr('cx', function (d) { return lineChartXScale(d.date); })
.attr('cy', function (d) { return lineChartYScale(d[CHART_Y_ATTRIBUTE]); })
.attr('class', function (d) {
dots.attr('cx', d => lineChartXScale(d.date))
.attr('cy', d => lineChartYScale(d[CHART_Y_ATTRIBUTE]))
.attr('class', (d) => {
let markerClass = 'metric-value';
if (_.has(d, 'anomalyScore') && Number(d.anomalyScore) >= threshold.val) {
markerClass += ' anomaly-marker ';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export class ExplorerChartSingleMetric extends React.Component {
return lineChartYScale.tickFormat()(d);
}
})
// Don't use an arrow function since we need access to `this`.
.each(function () {
maxYAxisLabelWidth = Math.max(this.getBBox().width + yAxis.tickPadding(), maxYAxisLabelWidth);
})
Expand Down Expand Up @@ -277,16 +278,17 @@ export class ExplorerChartSingleMetric extends React.Component {
// Create any new dots that are needed i.e. if number of chart points has increased.
dots.enter().append('circle')
.attr('r', LINE_CHART_ANOMALY_RADIUS)
// Don't use an arrow function since we need access to `this`.
.on('mouseover', function (d) {
showLineChartTooltip(d, this);
})
.on('mouseout', () => mlChartTooltipService.hide());

// Update all dots to new positions.
const threshold = mlSelectSeverityService.state.get('threshold');
dots.attr('cx', function (d) { return lineChartXScale(d.date); })
.attr('cy', function (d) { return lineChartYScale(d.value); })
.attr('class', function (d) {
dots.attr('cx', d => lineChartXScale(d.date))
.attr('cy', d => lineChartYScale(d.value))
.attr('class', (d) => {
let markerClass = 'metric-value';
if (_.has(d, 'anomalyScore') && Number(d.anomalyScore) >= threshold.val) {
markerClass += ` anomaly-marker ${getSeverityWithLow(d.anomalyScore)}`;
Expand All @@ -306,6 +308,7 @@ export class ExplorerChartSingleMetric extends React.Component {
.attr('d', d3.svg.symbol().size(MULTI_BUCKET_SYMBOL_SIZE).type('cross'))
.attr('transform', d => `translate(${lineChartXScale(d.date)}, ${lineChartYScale(d.value)})`)
.attr('class', d => `anomaly-marker multi-bucket ${getSeverityWithLow(d.anomalyScore)}`)
// Don't use an arrow function since we need access to `this`.
.on('mouseover', function (d) {
showLineChartTooltip(d, this);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function explorerChartsContainerServiceFactory(
// For now just take first 6 (or 8 if 4 charts per row).
const maxSeriesToPlot = Math.max(chartsPerRow * 2, 6);
const recordsToPlot = allSeriesRecords.slice(0, maxSeriesToPlot);
const seriesConfigs = buildDataConfigs(recordsToPlot);
const seriesConfigs = recordsToPlot.map(buildConfig);

// Calculate the time range of the charts, which is a function of the chart width and max job bucket span.
data.tooManyBuckets = false;
Expand Down Expand Up @@ -504,11 +504,6 @@ export function explorerChartsContainerServiceFactory(
return recordsForSeries;
}

function buildDataConfigs(anomalyRecords) {
// Build the chart configuration for each anomaly record.
return anomalyRecords.map(buildConfig);
}

function calculateChartRange(seriesConfigs, earliestMs, latestMs, chartWidth, recordsToPlot, timeFieldName) {
let tooManyBuckets = false;
// Calculate the time range for the charts.
Expand Down
17 changes: 10 additions & 7 deletions x-pack/plugins/ml/public/services/results_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,8 @@ function getEventRateData(
// Returned response contains a results property, which is an object
// of document counts against time (epoch millis).
const SAMPLER_TOP_TERMS_SHARD_SIZE = 200;
const ENTITY_AGGREGATION_SIZE = 10;
const AGGREGATION_MIN_DOC_COUNT = 1;
function getEventDistributionData(
index,
types,
Expand All @@ -1412,8 +1414,7 @@ function getEventDistributionData(
latestMs,
interval) {
return new Promise((resolve, reject) => {
// only get this data for count (used by rare chart)
if (metricFunction !== 'count' || splitField === undefined) {
if (splitField === undefined) {
return resolve([]);
}

Expand Down Expand Up @@ -1464,7 +1465,7 @@ function getEventDistributionData(
date_histogram: {
field: timeFieldName,
interval: interval,
min_doc_count: 0
min_doc_count: AGGREGATION_MIN_DOC_COUNT
},
aggs: {
sample: {
Expand All @@ -1475,7 +1476,8 @@ function getEventDistributionData(
entities: {
terms: {
field: splitField.fieldName,
size: 10
size: ENTITY_AGGREGATION_SIZE,
min_doc_count: AGGREGATION_MIN_DOC_COUNT
}
}
}
Expand All @@ -1491,7 +1493,7 @@ function getEventDistributionData(
}

if (metricFieldName !== undefined && metricFieldName !== '') {
body.aggs.byTime.aggs = {};
body.aggs.byTime.aggs.sample.aggs.entities.aggs = {};

const metricAgg = {
[metricFunction]: {
Expand All @@ -1502,7 +1504,7 @@ function getEventDistributionData(
if (metricFunction === 'percentiles') {
metricAgg[metricFunction].percents = [ML_MEDIAN_PERCENTS];
}
body.aggs.byTime.aggs.metric = metricAgg;
body.aggs.byTime.aggs.sample.aggs.entities.aggs.metric = metricAgg;
}

ml.esSearch({
Expand All @@ -1516,10 +1518,11 @@ function getEventDistributionData(
const date = +dataForTime.key;
const entities = _.get(dataForTime, ['sample', 'entities', 'buckets'], []);
entities.forEach((entity) => {
const value = (metricFunction === 'count') ? entity.doc_count : entity.metric.value;
d.push({
date,
entity: entity.key,
value: entity.doc_count
value
});
});
return d;
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/public/util/chart_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export function getChartType(config) {
return CHART_TYPE.EVENT_DISTRIBUTION;
} else if (
POPULATION_DISTRIBUTION_ENABLED &&
config.functionDescription === 'count' &&
config.functionDescription !== 'rare' &&
config.entityFields.some(f => f.fieldType === 'over')
) {
return CHART_TYPE.POPULATION_DISTRIBUTION;
Expand Down

0 comments on commit fe43b08

Please sign in to comment.