Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Anomaly Explorer: Display markers for scheduled events in distribution type anomaly charts #192377

2 changes: 2 additions & 0 deletions x-pack/plugins/ml/common/constants/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export const CHART_TYPE = {
} as const;

export type ChartType = (typeof CHART_TYPE)[keyof typeof CHART_TYPE];

export const SCHEDULE_EVENT_MARKER_ENTITY = 'schedule_event_marker_entity';
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { CHART_TYPE } from '../explorer_constants';
import { CHART_HEIGHT, TRANSPARENT_BACKGROUND } from './constants';
import { filter } from 'rxjs';
import { drawCursor } from './utils/draw_anomaly_explorer_charts_cursor';
import { SCHEDULE_EVENT_MARKER_ENTITY } from '../../../../common/constants/charts';

const CONTENT_WRAPPER_HEIGHT = 215;
const SCHEDULED_EVENT_MARKER_HEIGHT = 5;
Expand Down Expand Up @@ -158,12 +159,29 @@ export class ExplorerChartDistribution extends React.Component {
.key((d) => d.entity)
.entries(chartData)
.sort((a, b) => {
// To display calendar event markers we populate the chart with fake data points.
// If a category has fake data points, it should be sorted to the end.
const aHasFakeData = a.values.some((d) => d.entity === SCHEDULE_EVENT_MARKER_ENTITY);
const bHasFakeData = b.values.some((d) => d.entity === SCHEDULE_EVENT_MARKER_ENTITY);

if (aHasFakeData && !bHasFakeData) {
return 1;
}

if (bHasFakeData && !aHasFakeData) {
return -1;
}

return b.values.length - a.values.length;
})
.filter((d, i) => {
// only filter for rare charts
if (chartType === CHART_TYPE.EVENT_DISTRIBUTION) {
return i < categoryLimit || d.key === highlight;
return (
i < categoryLimit ||
d.key === highlight ||
d.values.some((d) => d.entity === SCHEDULE_EVENT_MARKER_ENTITY)
);
}
return true;
})
Expand Down Expand Up @@ -373,7 +391,8 @@ export class ExplorerChartDistribution extends React.Component {
.orient('left')
.innerTickSize(0)
.outerTickSize(0)
.tickPadding(10);
.tickPadding(10)
.tickFormat((d) => (d === SCHEDULE_EVENT_MARKER_ENTITY ? null : d));

if (fieldFormat !== undefined) {
yAxis.tickFormat((d) => fieldFormat.convert(d, 'text'));
Expand Down Expand Up @@ -518,7 +537,8 @@ export class ExplorerChartDistribution extends React.Component {
const tooltipData = [{ label: formattedDate }];
const seriesKey = config.detectorLabel;

if (marker.entity !== undefined) {
// Hide entity for scheduled events with mocked value.
if (marker.entity !== undefined && marker.entity !== SCHEDULE_EVENT_MARKER_ENTITY) {
tooltipData.push({
label: i18n.translate('xpack.ml.explorer.distributionChart.entityLabel', {
defaultMessage: 'entity',
Expand Down Expand Up @@ -590,7 +610,11 @@ export class ExplorerChartDistribution extends React.Component {
});
}
}
} else if (chartType !== CHART_TYPE.EVENT_DISTRIBUTION) {
} else if (
chartType !== CHART_TYPE.EVENT_DISTRIBUTION &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still seeing undefined appear in the axis labels for a rare job:

Screenshot 2024-09-13 at 11 49 45

You can reproduce with the filebeat_ecs data, using the 'rare' job wizard to create a job with this config:

    "bucket_span": "3h",
    "detectors": [
      {
        "detector_description": "rare by \"http.request.method\"",
        "function": "rare",
        "by_field_name": "http.request.method",
        "detector_index": 0
      }
    ],
    "influencers": [
      "http.request.method"
    ],

Copy link
Contributor Author

@rbrtj rbrtj Sep 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I can't seem to reproduce it to have an undefined label with the same dataset and job config:
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retested @rbrtj and I can no longer reproduce this with the undefined label!

// Hide value for scheduled events with mocked value.
marker.entity !== SCHEDULE_EVENT_MARKER_ENTITY
) {
tooltipData.push({
label: i18n.translate(
'xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel',
Expand Down
16 changes: 13 additions & 3 deletions x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { parseInterval } from '../../../common/util/parse_interval';
import { getDatafeedAggregations } from '../../../common/util/datafeed_utils';
import { findAggField } from '../../../common/util/validation_utils';
import type { ChartType } from '../../../common/constants/charts';
import { CHART_TYPE } from '../../../common/constants/charts';
import { CHART_TYPE, SCHEDULE_EVENT_MARKER_ENTITY } from '../../../common/constants/charts';
import { getChartType } from '../../../common/util/chart_utils';
import type { MlJob } from '../..';

Expand Down Expand Up @@ -1124,9 +1124,19 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
each(scheduledEvents, (events, time) => {
const chartPoint = findChartPointForTime(chartDataForPointSearch, Number(time));
if (chartPoint !== undefined) {
// Note if the scheduled event coincides with an absence of the underlying metric data,
// we don't worry about plotting the event.
chartPoint.scheduledEvents = events;
// We do not want to create additional points for single metric charts
// as it could break the chart.
} else if (chartType !== CHART_TYPE.SINGLE_METRIC) {
// If there's no underlying metric data point for the scheduled event,
// create a new chart point with a value of 0.
const eventChartPoint: ChartPoint = {
date: Number(time),
value: 0,
entity: SCHEDULE_EVENT_MARKER_ENTITY,
scheduledEvents: events,
};
chartData.push(eventChartPoint);
peteharverson marked this conversation as resolved.
Show resolved Hide resolved
}
});
}
Expand Down