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.
@@ -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