Skip to content

Commit f1799b8

Browse files
committed
[ML] Label anomalies with multi_bucket_impact (#34233)
* [ML] Label anomalies with multi_bucket_impact Add the multi_bucket_impact field to record results.
1 parent a2a5da7 commit f1799b8

File tree

7 files changed

+54
-2
lines changed

7 files changed

+54
-2
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/results/AnomalyRecord.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class AnomalyRecord implements ToXContentObject {
4848
* Result fields (all detector types)
4949
*/
5050
public static final ParseField PROBABILITY = new ParseField("probability");
51+
public static final ParseField MULTI_BUCKET_IMPACT = new ParseField("multi_bucket_impact");
5152
public static final ParseField DETECTOR_INDEX = new ParseField("detector_index");
5253
public static final ParseField BY_FIELD_NAME = new ParseField("by_field_name");
5354
public static final ParseField BY_FIELD_VALUE = new ParseField("by_field_value");
@@ -94,6 +95,7 @@ public class AnomalyRecord implements ToXContentObject {
9495
PARSER.declareLong(ConstructingObjectParser.constructorArg(), BUCKET_SPAN);
9596
PARSER.declareString((anomalyRecord, s) -> {}, Result.RESULT_TYPE);
9697
PARSER.declareDouble(AnomalyRecord::setProbability, PROBABILITY);
98+
PARSER.declareDouble(AnomalyRecord::setMultiBucketImpact, MULTI_BUCKET_IMPACT);
9799
PARSER.declareDouble(AnomalyRecord::setRecordScore, RECORD_SCORE);
98100
PARSER.declareDouble(AnomalyRecord::setInitialRecordScore, INITIAL_RECORD_SCORE);
99101
PARSER.declareInt(AnomalyRecord::setDetectorIndex, DETECTOR_INDEX);
@@ -117,6 +119,7 @@ public class AnomalyRecord implements ToXContentObject {
117119
private final String jobId;
118120
private int detectorIndex;
119121
private double probability;
122+
private Double multiBucketImpact;
120123
private String byFieldName;
121124
private String byFieldValue;
122125
private String correlatedByFieldValue;
@@ -155,6 +158,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
155158
builder.field(Job.ID.getPreferredName(), jobId);
156159
builder.field(Result.RESULT_TYPE.getPreferredName(), RESULT_TYPE_VALUE);
157160
builder.field(PROBABILITY.getPreferredName(), probability);
161+
if (multiBucketImpact != null) {
162+
builder.field(MULTI_BUCKET_IMPACT.getPreferredName(), multiBucketImpact);
163+
}
158164
builder.field(RECORD_SCORE.getPreferredName(), recordScore);
159165
builder.field(INITIAL_RECORD_SCORE.getPreferredName(), initialRecordScore);
160166
builder.field(BUCKET_SPAN.getPreferredName(), bucketSpan);
@@ -254,6 +260,14 @@ void setProbability(double value) {
254260
probability = value;
255261
}
256262

263+
public double getMultiBucketImpact() {
264+
return multiBucketImpact;
265+
}
266+
267+
void setMultiBucketImpact(double value) {
268+
multiBucketImpact = value;
269+
}
270+
257271
public String getByFieldName() {
258272
return byFieldName;
259273
}
@@ -376,7 +390,7 @@ void setInfluencers(List<Influence> influencers) {
376390

377391
@Override
378392
public int hashCode() {
379-
return Objects.hash(jobId, detectorIndex, bucketSpan, probability, recordScore,
393+
return Objects.hash(jobId, detectorIndex, bucketSpan, probability, multiBucketImpact, recordScore,
380394
initialRecordScore, typical, actual,function, functionDescription, fieldName,
381395
byFieldName, byFieldValue, correlatedByFieldValue, partitionFieldName,
382396
partitionFieldValue, overFieldName, overFieldValue, timestamp, isInterim,
@@ -399,6 +413,7 @@ public boolean equals(Object other) {
399413
&& this.detectorIndex == that.detectorIndex
400414
&& this.bucketSpan == that.bucketSpan
401415
&& this.probability == that.probability
416+
&& Objects.equals(this.multiBucketImpact, that.multiBucketImpact)
402417
&& this.recordScore == that.recordScore
403418
&& this.initialRecordScore == that.initialRecordScore
404419
&& Objects.deepEquals(this.typical, that.typical)

client/rest-high-level/src/test/java/org/elasticsearch/client/ml/job/results/AnomalyRecordTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public static AnomalyRecord createTestInstance(String jobId) {
3838
anomalyRecord.setActual(Collections.singletonList(randomDouble()));
3939
anomalyRecord.setTypical(Collections.singletonList(randomDouble()));
4040
anomalyRecord.setProbability(randomDouble());
41+
if (randomBoolean()) {
42+
anomalyRecord.setMultiBucketImpact(randomDouble());
43+
}
4144
anomalyRecord.setRecordScore(randomDouble());
4245
anomalyRecord.setInitialRecordScore(randomDouble());
4346
anomalyRecord.setInterim(randomBoolean());

docs/reference/ml/apis/resultsresource.asciidoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,11 @@ A record object has the following properties:
364364
//In scientific notation, a value of 3.24E-300 is highly unlikely and therefore
365365
//highly anomalous.
366366

367+
`multi_bucket_impact`::
368+
(number) an indication of how strongly an anomaly is multi bucket or single bucket.
369+
The value is on a scale of -5 to +5 where -5 means the anomaly is purely single
370+
bucket and +5 means the anomaly is purely multi bucket.
371+
367372
`record_score`::
368373
(number) A normalized score between 0-100, which is based on the probability
369374
of the anomalousness of this record. Unlike `initial_record_score`, this

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappings.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ private static void addAnomalyRecordFieldsToMapping(XContentBuilder builder) thr
372372
.startObject(AnomalyRecord.PROBABILITY.getPreferredName())
373373
.field(TYPE, DOUBLE)
374374
.endObject()
375+
.startObject(AnomalyRecord.MULTI_BUCKET_IMPACT.getPreferredName())
376+
.field(TYPE, DOUBLE)
377+
.endObject()
375378
.startObject(AnomalyRecord.FUNCTION.getPreferredName())
376379
.field(TYPE, KEYWORD)
377380
.endObject()

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/results/AnomalyRecord.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.elasticsearch.xpack.core.ml.job.config.Job;
2121
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
2222
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
23+
import org.elasticsearch.Version;
2324

2425
import java.io.IOException;
2526
import java.util.ArrayList;
@@ -45,6 +46,7 @@ public class AnomalyRecord implements ToXContentObject, Writeable {
4546
* Result fields (all detector types)
4647
*/
4748
public static final ParseField PROBABILITY = new ParseField("probability");
49+
public static final ParseField MULTI_BUCKET_IMPACT = new ParseField("multi_bucket_impact");
4850
public static final ParseField BY_FIELD_NAME = new ParseField("by_field_name");
4951
public static final ParseField BY_FIELD_VALUE = new ParseField("by_field_value");
5052
public static final ParseField CORRELATED_BY_FIELD_VALUE = new ParseField("correlated_by_field_value");
@@ -101,6 +103,7 @@ private static ConstructingObjectParser<AnomalyRecord, Void> createParser(boolea
101103
parser.declareLong(ConstructingObjectParser.constructorArg(), BUCKET_SPAN);
102104
parser.declareString((anomalyRecord, s) -> {}, Result.RESULT_TYPE);
103105
parser.declareDouble(AnomalyRecord::setProbability, PROBABILITY);
106+
parser.declareDouble(AnomalyRecord::setMultiBucketImpact, MULTI_BUCKET_IMPACT);
104107
parser.declareDouble(AnomalyRecord::setRecordScore, RECORD_SCORE);
105108
parser.declareDouble(AnomalyRecord::setInitialRecordScore, INITIAL_RECORD_SCORE);
106109
parser.declareInt(AnomalyRecord::setDetectorIndex, Detector.DETECTOR_INDEX);
@@ -128,6 +131,7 @@ private static ConstructingObjectParser<AnomalyRecord, Void> createParser(boolea
128131
private final String jobId;
129132
private int detectorIndex;
130133
private double probability;
134+
private Double multiBucketImpact;
131135
private String byFieldName;
132136
private String byFieldValue;
133137
private String correlatedByFieldValue;
@@ -169,6 +173,9 @@ public AnomalyRecord(StreamInput in) throws IOException {
169173
}
170174
detectorIndex = in.readInt();
171175
probability = in.readDouble();
176+
if (in.getVersion().onOrAfter(Version.V_6_5_0)) {
177+
multiBucketImpact = in.readOptionalDouble();
178+
}
172179
byFieldName = in.readOptionalString();
173180
byFieldValue = in.readOptionalString();
174181
correlatedByFieldValue = in.readOptionalString();
@@ -207,6 +214,9 @@ public void writeTo(StreamOutput out) throws IOException {
207214
}
208215
out.writeInt(detectorIndex);
209216
out.writeDouble(probability);
217+
if (out.getVersion().onOrAfter(Version.V_6_5_0)) {
218+
out.writeOptionalDouble(multiBucketImpact);
219+
}
210220
out.writeOptionalString(byFieldName);
211221
out.writeOptionalString(byFieldValue);
212222
out.writeOptionalString(correlatedByFieldValue);
@@ -256,6 +266,9 @@ XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws I
256266
builder.field(Job.ID.getPreferredName(), jobId);
257267
builder.field(Result.RESULT_TYPE.getPreferredName(), RESULT_TYPE_VALUE);
258268
builder.field(PROBABILITY.getPreferredName(), probability);
269+
if (multiBucketImpact != null) {
270+
builder.field(MULTI_BUCKET_IMPACT.getPreferredName(), multiBucketImpact);
271+
}
259272
builder.field(RECORD_SCORE.getPreferredName(), recordScore);
260273
builder.field(INITIAL_RECORD_SCORE.getPreferredName(), initialRecordScore);
261274
builder.field(BUCKET_SPAN.getPreferredName(), bucketSpan);
@@ -398,6 +411,14 @@ public void setProbability(double value) {
398411
probability = value;
399412
}
400413

414+
public double getMultiBucketImpact() {
415+
return multiBucketImpact;
416+
}
417+
418+
public void setMultiBucketImpact(double value) {
419+
multiBucketImpact = value;
420+
}
421+
401422
public String getByFieldName() {
402423
return byFieldName;
403424
}
@@ -528,7 +549,7 @@ public void setInfluencers(List<Influence> influencers) {
528549

529550
@Override
530551
public int hashCode() {
531-
return Objects.hash(jobId, detectorIndex, bucketSpan, probability, recordScore,
552+
return Objects.hash(jobId, detectorIndex, bucketSpan, probability, multiBucketImpact, recordScore,
532553
initialRecordScore, typical, actual,function, functionDescription, fieldName,
533554
byFieldName, byFieldValue, correlatedByFieldValue, partitionFieldName,
534555
partitionFieldValue, overFieldName, overFieldValue, timestamp, isInterim,
@@ -552,6 +573,7 @@ public boolean equals(Object other) {
552573
&& this.detectorIndex == that.detectorIndex
553574
&& this.bucketSpan == that.bucketSpan
554575
&& this.probability == that.probability
576+
&& Objects.equals(this.multiBucketImpact, that.multiBucketImpact)
555577
&& this.recordScore == that.recordScore
556578
&& this.initialRecordScore == that.initialRecordScore
557579
&& Objects.deepEquals(this.typical, that.typical)

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/results/ReservedFieldNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public final class ReservedFieldNames {
5757
AnomalyCause.FIELD_NAME.getPreferredName(),
5858

5959
AnomalyRecord.PROBABILITY.getPreferredName(),
60+
AnomalyRecord.MULTI_BUCKET_IMPACT.getPreferredName(),
6061
AnomalyRecord.BY_FIELD_NAME.getPreferredName(),
6162
AnomalyRecord.BY_FIELD_VALUE.getPreferredName(),
6263
AnomalyRecord.CORRELATED_BY_FIELD_VALUE.getPreferredName(),

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/results/AnomalyRecordTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public AnomalyRecord createTestInstance(String jobId) {
3737
anomalyRecord.setActual(Collections.singletonList(randomDouble()));
3838
anomalyRecord.setTypical(Collections.singletonList(randomDouble()));
3939
anomalyRecord.setProbability(randomDouble());
40+
if (randomBoolean()) {
41+
anomalyRecord.setMultiBucketImpact(randomDouble());
42+
}
4043
anomalyRecord.setRecordScore(randomDouble());
4144
anomalyRecord.setInitialRecordScore(randomDouble());
4245
anomalyRecord.setInterim(randomBoolean());

0 commit comments

Comments
 (0)