eseguiti fix e introdotta versione wavelets

This commit is contained in:
fredmaloggia
2025-12-09 20:35:39 +01:00
parent 9543d63591
commit 71c796b1f1
4 changed files with 2805 additions and 269 deletions

View File

@@ -9,6 +9,10 @@ from typing import Dict, List, Optional, Sequence, Tuple
import numpy as np
import pandas as pd
import pyodbc
try:
import pywt
except ImportError: # pragma: no cover - optional dependency
pywt = None
DEFAULT_CONFIG_PATH = Path("config/pattern_knn_config.json")
@@ -87,6 +91,58 @@ def z_norm(arr: np.ndarray) -> Optional[np.ndarray]:
return (arr - mu) / (sd + 1e-12)
def wavelet_denoise(
series: pd.Series,
wavelet: str = "db3",
level: int = 3,
mode: str = "symmetric",
threshold_mode: str = "soft",
) -> Optional[pd.Series]:
"""Denoise/reshape the series with a wavelet decomposition.
Keeps the original index length; if PyWavelets is missing the function
returns None so callers can gracefully fall back to the raw signal.
"""
if pywt is None:
print("[WARN] pywt non installato: salto il filtraggio wavelet.")
return None
s = pd.to_numeric(series, errors="coerce")
if s.dropna().empty:
return None
w = pywt.Wavelet(wavelet)
max_level = pywt.dwt_max_level(len(s.dropna()), w.dec_len)
lvl = max(1, min(level, max_level)) if max_level > 0 else 1
valid = s.dropna()
coeffs = pywt.wavedec(valid.values, w, mode=mode, level=lvl)
# Universal threshold (Donoho-Johnstone)
sigma = np.median(np.abs(coeffs[-1])) / 0.6745 if len(coeffs[-1]) > 0 else 0.0
thresh = sigma * np.sqrt(2 * np.log(len(valid))) if sigma > 0 else 0.0
if thresh <= 0:
coeffs_f = coeffs
else:
def _safe_thresh(c: np.ndarray) -> np.ndarray:
if c is None or c.size == 0:
return c
if threshold_mode == "hard":
return pywt.threshold(c, value=thresh, mode="hard")
# soft threshold without divide-by-zero warnings
mag = np.abs(c)
mask = mag > thresh
out = np.zeros_like(c)
out[mask] = np.sign(c[mask]) * (mag[mask] - thresh)
return out
coeffs_f = [coeffs[0]] + [_safe_thresh(c) for c in coeffs[1:]]
rec = pywt.waverec(coeffs_f, w, mode=mode)
rec = rec[: len(valid)]
filt = pd.Series(rec, index=valid.index)
# Re-allineamento all'indice originale
return filt.reindex(s.index).interpolate(limit_direction="both")
def build_pattern_library(
ret_series: pd.Series,
wp: int,