anomaly-pipeline 0.1.27__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anomaly_pipeline/__init__.py +2 -0
- anomaly_pipeline/helpers/DB_scan.py +188 -0
- anomaly_pipeline/helpers/IQR.py +71 -0
- anomaly_pipeline/helpers/MAD.py +88 -0
- anomaly_pipeline/helpers/Preprocessing.py +116 -0
- anomaly_pipeline/helpers/STD.py +70 -0
- anomaly_pipeline/helpers/__init__.py +1 -0
- anomaly_pipeline/helpers/baseline.py +112 -0
- anomaly_pipeline/helpers/cluster_functions.py +289 -0
- anomaly_pipeline/helpers/evaluation_info.py +121 -0
- anomaly_pipeline/helpers/evaluation_plots.py +546 -0
- anomaly_pipeline/helpers/ewma.py +119 -0
- anomaly_pipeline/helpers/fb_prophet.py +94 -0
- anomaly_pipeline/helpers/help_info.py +683 -0
- anomaly_pipeline/helpers/iso_forest_general.py +50 -0
- anomaly_pipeline/helpers/iso_forest_timeseries.py +123 -0
- anomaly_pipeline/helpers/percentile.py +65 -0
- anomaly_pipeline/main.py +63 -0
- anomaly_pipeline/pipeline.py +253 -0
- anomaly_pipeline-0.1.27.dist-info/METADATA +15 -0
- anomaly_pipeline-0.1.27.dist-info/RECORD +24 -0
- anomaly_pipeline-0.1.27.dist-info/WHEEL +5 -0
- anomaly_pipeline-0.1.27.dist-info/entry_points.txt +2 -0
- anomaly_pipeline-0.1.27.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import logging
|
|
3
|
+
import numpy as np
|
|
4
|
+
from prophet import Prophet
|
|
5
|
+
import warnings
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
|
|
10
|
+
warnings.filterwarnings("ignore")
|
|
11
|
+
|
|
12
|
+
# --- ADD THIS HELPER ---
|
|
13
|
+
@contextmanager
|
|
14
|
+
def suppress_stdout_stderr():
|
|
15
|
+
"""Redirects stdout and stderr to devnull to silence C++ logs."""
|
|
16
|
+
with open(os.devnull, 'w') as fnull:
|
|
17
|
+
old_stdout, old_stderr = sys.stdout, sys.stderr
|
|
18
|
+
sys.stdout, sys.stderr = fnull, fnull
|
|
19
|
+
try:
|
|
20
|
+
yield
|
|
21
|
+
finally:
|
|
22
|
+
sys.stdout, sys.stderr = old_stdout, old_stderr
|
|
23
|
+
|
|
24
|
+
def detect_time_series_anomalies_fb_walkforward(
|
|
25
|
+
group,
|
|
26
|
+
variable,
|
|
27
|
+
date_column,
|
|
28
|
+
eval_period,
|
|
29
|
+
interval_width
|
|
30
|
+
):
|
|
31
|
+
# 1. Silence the cmdstanpy logger completely
|
|
32
|
+
logger = logging.getLogger('cmdstanpy')
|
|
33
|
+
logger.addHandler(logging.NullHandler())
|
|
34
|
+
logger.propagate = False
|
|
35
|
+
logger.setLevel(logging.CRITICAL) # Only show critical errors
|
|
36
|
+
|
|
37
|
+
group = group.sort_values(date_column).copy()
|
|
38
|
+
group[date_column] = pd.to_datetime(group[date_column])
|
|
39
|
+
|
|
40
|
+
cutoff_date = group[date_column].max() - pd.Timedelta(weeks=eval_period)
|
|
41
|
+
|
|
42
|
+
group["FB_forecast"] = np.nan
|
|
43
|
+
group["FB_low"] = np.nan
|
|
44
|
+
group["FB_high"] = np.nan
|
|
45
|
+
|
|
46
|
+
train = group[group[date_column] <= cutoff_date].copy()
|
|
47
|
+
test = group[group[date_column] > cutoff_date].copy()
|
|
48
|
+
|
|
49
|
+
for i, row in test.iterrows():
|
|
50
|
+
prophet_train = train.rename(columns={date_column: "ds", variable: "y"})
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
model = Prophet(
|
|
54
|
+
weekly_seasonality=True,
|
|
55
|
+
yearly_seasonality=True,
|
|
56
|
+
daily_seasonality=False,
|
|
57
|
+
interval_width=interval_width
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# --- WRAP THE FIT IN THE MUTER ---
|
|
61
|
+
with suppress_stdout_stderr():
|
|
62
|
+
model.fit(prophet_train)
|
|
63
|
+
|
|
64
|
+
future = pd.DataFrame({"ds": [row[date_column]]})
|
|
65
|
+
fc = model.predict(future).iloc[0]
|
|
66
|
+
|
|
67
|
+
group.loc[i, "FB_forecast"] = fc["yhat"]
|
|
68
|
+
group.loc[i, "FB_low"] = max(fc["yhat_lower"], 0)
|
|
69
|
+
group.loc[i, "FB_high"] = fc["yhat_upper"]
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
# Note: We don't mute this so you can still see actual errors
|
|
73
|
+
print(f"Prophet failed for KEY={group['key'].iloc[0]} on date={row[date_column]}: {e}")
|
|
74
|
+
group.loc[i, "FB_forecast"] = train[variable].iloc[-1]
|
|
75
|
+
group.loc[i, "FB_low"] = max(train[variable].iloc[-1], 0)
|
|
76
|
+
group.loc[i, "FB_high"] = train[variable].iloc[-1]
|
|
77
|
+
|
|
78
|
+
new_train_row = row.to_frame().T
|
|
79
|
+
train = pd.concat([train, new_train_row], ignore_index=True)
|
|
80
|
+
|
|
81
|
+
group["FB_residual"] = group[variable] - group["FB_forecast"]
|
|
82
|
+
group["FB_anomaly"] = np.nan
|
|
83
|
+
mask = group[date_column] > cutoff_date
|
|
84
|
+
|
|
85
|
+
group.loc[mask & (group[variable] > group["FB_high"]), "FB_anomaly"] = "high"
|
|
86
|
+
group.loc[mask & (group[variable] < group["FB_low"]), "FB_anomaly"] = "low"
|
|
87
|
+
|
|
88
|
+
group["is_FB_anomaly"] = group["FB_anomaly"].notna()
|
|
89
|
+
|
|
90
|
+
train_mask = group[date_column] <= cutoff_date
|
|
91
|
+
group.loc[train_mask, "FB_residual"] = np.nan
|
|
92
|
+
group.loc[train_mask, "is_FB_anomaly"] = np.nan
|
|
93
|
+
|
|
94
|
+
return group
|