Skip to content

Commit a977d8e

Browse files
edsavagekcm
authored andcommitted
[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 75c220e commit a977d8e

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
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.xpack.core.ml.job.config.Job;
2020
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
2121
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
22+
import org.elasticsearch.Version;
2223

2324
import java.io.IOException;
2425
import java.util.ArrayList;
@@ -44,6 +45,7 @@ public class AnomalyRecord implements ToXContentObject, Writeable {
4445
* Result fields (all detector types)
4546
*/
4647
public static final ParseField PROBABILITY = new ParseField("probability");
48+
public static final ParseField MULTI_BUCKET_IMPACT = new ParseField("multi_bucket_impact");
4749
public static final ParseField BY_FIELD_NAME = new ParseField("by_field_name");
4850
public static final ParseField BY_FIELD_VALUE = new ParseField("by_field_value");
4951
public static final ParseField CORRELATED_BY_FIELD_VALUE = new ParseField("correlated_by_field_value");
@@ -100,6 +102,7 @@ private static ConstructingObjectParser<AnomalyRecord, Void> createParser(boolea
100102
parser.declareLong(ConstructingObjectParser.constructorArg(), BUCKET_SPAN);
101103
parser.declareString((anomalyRecord, s) -> {}, Result.RESULT_TYPE);
102104
parser.declareDouble(AnomalyRecord::setProbability, PROBABILITY);
105+
parser.declareDouble(AnomalyRecord::setMultiBucketImpact, MULTI_BUCKET_IMPACT);
103106
parser.declareDouble(AnomalyRecord::setRecordScore, RECORD_SCORE);
104107
parser.declareDouble(AnomalyRecord::setInitialRecordScore, INITIAL_RECORD_SCORE);
105108
parser.declareInt(AnomalyRecord::setDetectorIndex, Detector.DETECTOR_INDEX);
@@ -127,6 +130,7 @@ private static ConstructingObjectParser<AnomalyRecord, Void> createParser(boolea
127130
private final String jobId;
128131
private int detectorIndex;
129132
private double probability;
133+
private Double multiBucketImpact;
130134
private String byFieldName;
131135
private String byFieldValue;
132136
private String correlatedByFieldValue;
@@ -164,6 +168,9 @@ public AnomalyRecord(StreamInput in) throws IOException {
164168
jobId = in.readString();
165169
detectorIndex = in.readInt();
166170
probability = in.readDouble();
171+
if (in.getVersion().onOrAfter(Version.V_6_5_0)) {
172+
multiBucketImpact = in.readOptionalDouble();
173+
}
167174
byFieldName = in.readOptionalString();
168175
byFieldValue = in.readOptionalString();
169176
correlatedByFieldValue = in.readOptionalString();
@@ -198,6 +205,9 @@ public void writeTo(StreamOutput out) throws IOException {
198205
out.writeString(jobId);
199206
out.writeInt(detectorIndex);
200207
out.writeDouble(probability);
208+
if (out.getVersion().onOrAfter(Version.V_6_5_0)) {
209+
out.writeOptionalDouble(multiBucketImpact);
210+
}
201211
out.writeOptionalString(byFieldName);
202212
out.writeOptionalString(byFieldValue);
203213
out.writeOptionalString(correlatedByFieldValue);
@@ -247,6 +257,9 @@ XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws I
247257
builder.field(Job.ID.getPreferredName(), jobId);
248258
builder.field(Result.RESULT_TYPE.getPreferredName(), RESULT_TYPE_VALUE);
249259
builder.field(PROBABILITY.getPreferredName(), probability);
260+
if (multiBucketImpact != null) {
261+
builder.field(MULTI_BUCKET_IMPACT.getPreferredName(), multiBucketImpact);
262+
}
250263
builder.field(RECORD_SCORE.getPreferredName(), recordScore);
251264
builder.field(INITIAL_RECORD_SCORE.getPreferredName(), initialRecordScore);
252265
builder.field(BUCKET_SPAN.getPreferredName(), bucketSpan);
@@ -389,6 +402,14 @@ public void setProbability(double value) {
389402
probability = value;
390403
}
391404

405+
public double getMultiBucketImpact() {
406+
return multiBucketImpact;
407+
}
408+
409+
public void setMultiBucketImpact(double value) {
410+
multiBucketImpact = value;
411+
}
412+
392413
public String getByFieldName() {
393414
return byFieldName;
394415
}
@@ -519,7 +540,7 @@ public void setInfluencers(List<Influence> influencers) {
519540

520541
@Override
521542
public int hashCode() {
522-
return Objects.hash(jobId, detectorIndex, bucketSpan, probability, recordScore,
543+
return Objects.hash(jobId, detectorIndex, bucketSpan, probability, multiBucketImpact, recordScore,
523544
initialRecordScore, typical, actual,function, functionDescription, fieldName,
524545
byFieldName, byFieldValue, correlatedByFieldValue, partitionFieldName,
525546
partitionFieldValue, overFieldName, overFieldValue, timestamp, isInterim,
@@ -543,6 +564,7 @@ public boolean equals(Object other) {
543564
&& this.detectorIndex == that.detectorIndex
544565
&& this.bucketSpan == that.bucketSpan
545566
&& this.probability == that.probability
567+
&& Objects.equals(this.multiBucketImpact, that.multiBucketImpact)
546568
&& this.recordScore == that.recordScore
547569
&& this.initialRecordScore == that.initialRecordScore
548570
&& 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)