|
5 | 5 | For the implementation of only weekly/monthly seasonality, specify "enable_weekly" of "enable_monthly" arguments of RunWindStats().
|
6 | 6 | """
|
7 | 7 |
|
8 |
| -from windstats import WindStats, WindStatsConfig |
9 |
| -from windstats_monthly import MonthlyWindStats, MonthlyWindStatsConfig |
10 |
| -from ts_datasets.anomaly import NAB |
| 8 | +from merlion.models.anomaly.windstats import WindStats, WindStatsConfig |
| 9 | +from merlion.models.anomaly.windstats_monthly import MonthlyWindStats, MonthlyWindStatsConfig |
11 | 10 | from merlion.utils import TimeSeries
|
12 |
| -from merlion.post_process.threshold import AggregateAlarms |
13 | 11 |
|
14 | 12 | class RunWindStats:
|
15 |
| - def __init__(self, threshold, enable_weekly = True, enable_monthly = True, WeeklyWindStatsConfig = WindStatsConfig(), MonthlyWindStatsConfig = MonthlyWindStatsConfig()): |
| 13 | + def __init__( |
| 14 | + self, |
| 15 | + threshold, |
| 16 | + enable_weekly = True, |
| 17 | + enable_monthly = True, |
| 18 | + post_rule_on_anom_score = False, |
| 19 | + WeeklyWindStatsConfig = WindStatsConfig(), |
| 20 | + MonthlyWindStatsConfig = MonthlyWindStatsConfig(), |
| 21 | + return_score = True |
| 22 | + ): |
16 | 23 | """
|
17 | 24 | Users can customize the configuration for weekly or monthly-based windstats. If not, then the default configuration will apply.
|
18 | 25 | """
|
19 | 26 |
|
20 | 27 | self.enable_weekly = enable_weekly
|
21 | 28 | self.enable_monthly = enable_monthly
|
| 29 | + self.return_score = return_score |
22 | 30 | assert self.enable_weekly == True or self.enable_monthly == True, "Must enable either weekly or monthly seasonality, or both!"
|
23 | 31 |
|
24 | 32 | # Threshold on identifying anomaly based on anomaly score.
|
25 | 33 | self.threshold = threshold
|
| 34 | + # If apply post rules on anomaly score |
| 35 | + self.post_rule = post_rule_on_anom_score |
26 | 36 |
|
| 37 | + # Intialize according model if enable weekly/monthly analysis |
27 | 38 | if self.enable_weekly:
|
28 | 39 | self.model_weekly = WindStats(WeeklyWindStatsConfig)
|
29 |
| - |
30 | 40 | if self.enable_monthly:
|
31 | 41 | self.model_monthly = MonthlyWindStats(MonthlyWindStatsConfig)
|
32 | 42 |
|
| 43 | + # Identify anomaly based on the hard threshold. |
33 | 44 | def anomalyByScore(self, scores, threshold):
|
34 |
| - scores.loc[abs(scores["anom_score"]) <= threshold] = 0 |
35 |
| - scores.loc[abs(scores["anom_score"]) > threshold] = 1 |
| 45 | + labels = scores.copy() |
| 46 | + labels.loc[abs(labels["anom_score"]) <= threshold] = 0 |
| 47 | + labels.loc[abs(labels["anom_score"]) > threshold] = 1 |
36 | 48 |
|
37 |
| - scores.rename(columns = {"anom_score": "anomaly"}, inplace = True) |
38 |
| - return scores |
| 49 | + labels.rename(columns = {"anom_score": "anomaly"}, inplace = True) |
| 50 | + return labels |
| 51 | + |
| 52 | + # Filter anomaly scores based on post rules. Same as "get_anomaly_label" in WindStats |
| 53 | + def get_anomaly_label(self, model, ts): |
| 54 | + scores = model.train(ts) |
| 55 | + return model.post_rule(scores) if model.post_rule is not None else scores |
39 | 56 |
|
40 | 57 | def run(self, ts):
|
41 | 58 | if self.enable_weekly:
|
42 |
| - scores_weekly = self.model_weekly.train(ts).to_pd() |
43 |
| - scores_weekly = self.anomalyByScore(scores_weekly, self.threshold) |
| 59 | + if self.post_rule: |
| 60 | + scores_weekly = self.get_anomaly_label(self.model_weekly, ts).to_pd() |
| 61 | + else: |
| 62 | + scores_weekly = self.model_weekly.train(ts).to_pd() |
| 63 | + labels_weekly = self.anomalyByScore(scores_weekly, self.threshold) |
44 | 64 |
|
45 | 65 | if self.enable_monthly:
|
46 |
| - scores_monthly = self.model_monthly.train(ts).to_pd() |
47 |
| - scores_monthly = self.anomalyByScore(scores_monthly, self.threshold) |
| 66 | + if self.post_rule: |
| 67 | + scores_monthly = self.get_anomaly_label(self.model_monthly, ts).to_pd() |
| 68 | + else: |
| 69 | + scores_monthly = self.model_monthly.train(ts).to_pd() |
| 70 | + labels_monthly = self.anomalyByScore(scores_monthly, self.threshold) |
48 | 71 |
|
| 72 | + # Anomaly is identified if and only if it's detected in both weekly and monthly patterns. |
49 | 73 | if self.enable_weekly and self.enable_monthly:
|
50 |
| - return scores_weekly * scores_monthly |
| 74 | + if self.return_score: |
| 75 | + return scores_weekly, scores_monthly, scores_weekly * scores_monthly |
| 76 | + else: |
| 77 | + return scores_weekly, scores_monthly, labels_weekly * labels_monthly |
51 | 78 | elif self.enable_weekly:
|
52 |
| - return scores_weekly |
| 79 | + if self.return_score: |
| 80 | + return scores_weekly, None, scores_weekly |
| 81 | + else: |
| 82 | + return scores_weekly, None, labels_weekly |
53 | 83 | else:
|
54 |
| - return scores_monthly |
| 84 | + if self.return_score: |
| 85 | + return None, scores_monthly, scores_monthly |
| 86 | + else: |
| 87 | + return None, scores_monthly, labels_monthly |
0 commit comments