17
17
import java .io .IOException ;
18
18
import java .time .Instant ;
19
19
import java .util .ArrayList ;
20
+ import java .util .Arrays ;
20
21
import java .util .List ;
21
22
import java .util .Optional ;
22
23
@@ -312,6 +313,7 @@ public AnomalyResult(
312
313
* @param threshold Current threshold
313
314
* @param currentData imputed data if any
314
315
* @param featureImputed whether feature is imputed or not
316
+ * @param rules rules we apply on anomaly grade based on condition
315
317
* @return the converted AnomalyResult instance
316
318
*/
317
319
public static AnomalyResult fromRawTRCFResult (
@@ -338,15 +340,20 @@ public static AnomalyResult fromRawTRCFResult(
338
340
double [] likelihoodOfValues ,
339
341
Double threshold ,
340
342
double [] currentData ,
341
- boolean [] featureImputed
343
+ boolean [] featureImputed ,
344
+ List <Rule > rules
342
345
) {
343
346
List <DataByFeatureId > convertedRelevantAttribution = null ;
344
347
List <DataByFeatureId > convertedPastValuesList = null ;
345
348
List <ExpectedValueList > convertedExpectedValues = null ;
349
+ List <FeatureData > featuresForComparison = null ;
346
350
347
351
int featureSize = featureData == null ? 0 : featureData .size ();
348
352
349
353
if (grade > 0 ) {
354
+ // Get the top feature names based on the relevant attribution criteria
355
+ featuresForComparison = getTopFeatureNames (featureData , relevantAttribution );
356
+
350
357
if (relevantAttribution != null ) {
351
358
if (relevantAttribution .length == featureSize ) {
352
359
convertedRelevantAttribution = new ArrayList <>(featureSize );
@@ -425,6 +432,28 @@ public static AnomalyResult fromRawTRCFResult(
425
432
);
426
433
}
427
434
}
435
+
436
+ for (FeatureData feature : featuresForComparison ) {
437
+ Double valueToCompare = getValueToCompare (feature , convertedPastValuesList , featureData );
438
+ Double expectedValue = getExpectedValue (feature , convertedExpectedValues );
439
+ if (valueToCompare == null || expectedValue == null ) {
440
+ continue ; // Skip if either valueToCompare or expectedValue is missing
441
+ }
442
+ for (Rule rule : rules ) {
443
+ for (Condition condition : rule .getConditions ()) {
444
+ if (condition .getFeatureName ().equals (feature .getFeatureName ())) {
445
+ ThresholdType thresholdType = condition .getThresholdType ();
446
+ if (thresholdType == ThresholdType .ACTUAL_IS_BELOW_EXPECTED && valueToCompare < expectedValue ) {
447
+ LOG .info ("changed anomaly grade from: " + grade + " to 0d for detector: " + detectorId );
448
+ grade = 0d ;
449
+ } else if (thresholdType == ThresholdType .ACTUAL_IS_OVER_EXPECTED && valueToCompare > expectedValue ) {
450
+ LOG .info ("changed anomaly grade from: " + grade + " to 0d for detector: " + detectorId );
451
+ grade = 0d ;
452
+ }
453
+ }
454
+ }
455
+ }
456
+ }
428
457
}
429
458
430
459
List <FeatureImputed > featureImputedList = new ArrayList <>();
@@ -468,6 +497,69 @@ public static AnomalyResult fromRawTRCFResult(
468
497
);
469
498
}
470
499
500
+ private static Double getValueToCompare (
501
+ FeatureData feature ,
502
+ List <DataByFeatureId > convertedPastValuesList ,
503
+ List <FeatureData > featureData
504
+ ) {
505
+ String featureId = feature .getFeatureId ();
506
+ if (convertedPastValuesList != null ) {
507
+ for (DataByFeatureId data : convertedPastValuesList ) {
508
+ if (data .getFeatureId ().equals (featureId )) {
509
+ return data .getData ();
510
+ }
511
+ }
512
+ } else {
513
+ for (FeatureData data : featureData ) {
514
+ if (data .getFeatureId ().equals (featureId )) {
515
+ return data .getData ();
516
+ }
517
+ }
518
+ }
519
+ return 0d ;
520
+ }
521
+
522
+ private static Double getExpectedValue (FeatureData feature , List <ExpectedValueList > convertedExpectedValues ) {
523
+ Double expectedValue = 0d ;
524
+ if (convertedExpectedValues != null ) {
525
+ for (ExpectedValueList expectedValueList : convertedExpectedValues ) {
526
+ if (expectedValueList != null && expectedValueList .getValueList () != null ) {
527
+ for (var data : expectedValueList .getValueList ()) {
528
+ if (data .getFeatureId ().equals (feature .getFeatureId ())) {
529
+ expectedValue = data .getData ();
530
+ }
531
+ }
532
+ }
533
+ }
534
+ }
535
+ return expectedValue ;
536
+ }
537
+
538
+ private static List <FeatureData > getTopFeatureNames (List <FeatureData > featureData , double [] relevantAttribution ) {
539
+ List <FeatureData > topFeatureNames = new ArrayList <>();
540
+
541
+ if (relevantAttribution == null || relevantAttribution .length == 0 || (relevantAttribution .length != featureData .size ())) {
542
+ topFeatureNames .addAll (featureData );
543
+ return topFeatureNames ;
544
+ }
545
+
546
+ // Find the maximum rounded value in a single pass and add corresponding feature names
547
+ double maxRoundedAttribution = Arrays
548
+ .stream (relevantAttribution )
549
+ .map (value -> Math .round (value * 100.0 ) / 100.0 )
550
+ .max ()
551
+ .orElse (Double .NaN );
552
+
553
+ // Collect feature names with values that match the max rounded value
554
+ for (int i = 0 ; i < relevantAttribution .length ; i ++) {
555
+ if (Math .round (relevantAttribution [i ] * 100.0 ) / 100.0 == maxRoundedAttribution ) {
556
+ topFeatureNames .add (featureData .get (i ));
557
+ }
558
+ }
559
+
560
+ return topFeatureNames ;
561
+ }
562
+
471
563
public AnomalyResult (StreamInput input ) throws IOException {
472
564
super (input );
473
565
this .modelId = input .readOptionalString ();
0 commit comments