From 39331e7ca211b8243d38e4f13176c846a43bc00d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 28 May 2025 22:37:12 +0200 Subject: [PATCH] refactor(qav3): use scipy for zero phase gaussian smoothing MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../user_data/strategies/QuickAdapterV3.py | 2 +- quickadapter/user_data/strategies/Utils.py | 25 ++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/quickadapter/user_data/strategies/QuickAdapterV3.py b/quickadapter/user_data/strategies/QuickAdapterV3.py index e7afb10..7cdde08 100644 --- a/quickadapter/user_data/strategies/QuickAdapterV3.py +++ b/quickadapter/user_data/strategies/QuickAdapterV3.py @@ -712,7 +712,7 @@ class QuickAdapterV3(IStrategy): center=True, ).mean(std=std), "zero_phase_gaussian": zero_phase_gaussian( - series=series, window=gaussian_window, std=std + series=series, window=window, std=std ), "boxcar": series.rolling( window=odd_window, win_type="boxcar", center=True diff --git a/quickadapter/user_data/strategies/Utils.py b/quickadapter/user_data/strategies/Utils.py index b7a6f7f..70b163c 100644 --- a/quickadapter/user_data/strategies/Utils.py +++ b/quickadapter/user_data/strategies/Utils.py @@ -1,10 +1,9 @@ from enum import IntEnum import numpy as np import pandas as pd +import scipy as sp import talib.abstract as ta from typing import Callable, Union -from scipy.signal import convolve -from scipy.signal.windows import gaussian from technical import qtpylib @@ -44,18 +43,16 @@ def derive_gaussian_std_from_window(window: int) -> float: def zero_phase_gaussian(series: pd.Series, window: int, std: float) -> pd.Series: - kernel = gaussian(window, std=std) - kernel /= kernel.sum() - - padding_length = window - 1 - padded_series_values = np.pad( - series.values, (padding_length, padding_length), mode="edge" - ) - - forward = convolve(padded_series_values, kernel, mode="valid") - backward = convolve(forward[::-1], kernel, mode="valid")[::-1] - - return pd.Series(backward, index=series.index) + if len(series) == 0: + return series + if len(series) < window: + raise ValueError("Series length must be greater than or equal to window size") + series_values = series.to_numpy() + gaussian_coeffs = sp.signal.windows.gaussian(M=window, std=std, sym=True) + b = gaussian_coeffs / np.sum(gaussian_coeffs) + a = 1.0 + filtered_values = sp.signal.filtfilt(b, a, series_values) + return pd.Series(filtered_values, index=series.index) def top_change_percent(dataframe: pd.DataFrame, period: int) -> pd.Series: -- 2.43.0