From b940bbc8a273032ca01091709acc83170db358e9 Mon Sep 17 00:00:00 2001 From: Tony Bagnall Date: Fri, 18 Oct 2024 09:54:55 +0100 Subject: [PATCH] rework base class --- tsml_eval/_wip/forecasting/base.py | 53 ++++++++++++++++++----- tsml_eval/_wip/forecasting/window_base.py | 26 +++++++---- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/tsml_eval/_wip/forecasting/base.py b/tsml_eval/_wip/forecasting/base.py index f90a01fd..26b83aea 100644 --- a/tsml_eval/_wip/forecasting/base.py +++ b/tsml_eval/_wip/forecasting/base.py @@ -38,40 +38,71 @@ class BaseForecaster(BaseSeriesEstimator, ABC): predict value $window+i$. If horizon is 4, forecaster will used points $i$ to $window+i-1$ to predict value $window+i+3$. If None, the algorithm will internally determine what data to use to predict `horizon` steps ahead. - - To Do - ----- - axis """ - def __init__(self, horizon=1, window=None): + # TODO: add any forecasting specific tags + _tags = { + "capability:univariate": True, + "capability:multivariate": False, + "capability:missing_values": False, + "X_inner_type": "np.ndarray", # one of VALID_INNER_TYPES + } + def __init__(self, horizon=1, window=None, axis=1): self.horizon = horizon self.window = window self._is_fitted = False - super().__init__(axis=1) + super().__init__(axis) @abstractmethod - def fit(self, X): + def fit(self, y, X=None): """Fit forecaster to series X. + TODO: passing series as X makes sense in machine learning, but not in + forecasting Parameters ------- - X : np.ndarray - A time Time series on which to learn a forecaster + y : np.ndarray + A time series on which to learn a forecaster to predict horizon ahead + X : np.ndarray, default =None + Optional exogenous time series data assumed to be aligned with y + Returns ------- self - Fitted estimator + Fitted BaseForecaster. """ ... @abstractmethod - def predict(self, X): + def predict(self, y=None, X=None): """ + Parameters + ------- + Parameters + ------- + y : np.ndarray, default = None + A time series to predict the next horizon value for. If None, + predict the next horizon value after series seen in fit. + X : np.ndarray, default =None + Optional exogenous time series data assumed to be aligned with y + Returns ------- float single prediction. """ ... + + @abstractmethod + def forecast(self, y, X=None): + """ + + basically fit_predict. + + Returns + ------- + np.ndarray + single prediction directly after the last point in X. + """ + ... \ No newline at end of file diff --git a/tsml_eval/_wip/forecasting/window_base.py b/tsml_eval/_wip/forecasting/window_base.py index ad996c81..46377b71 100644 --- a/tsml_eval/_wip/forecasting/window_base.py +++ b/tsml_eval/_wip/forecasting/window_base.py @@ -8,10 +8,11 @@ def __init__(self, window, horizon=1, regressor=LinearRegression()): self.regressor = regressor super().__init__(horizon, window) - def fit(self, X): - """Fit forecaster to y, optionally using exogenous data X. + def fit(self, y, X=None): + """Fit forecaster to time series. - Split y into windows of length window and train the forecaster on each window + Split X into windows of length window and train the forecaster on each window + to predict the horizon ahead. Parameters ---------- @@ -23,17 +24,26 @@ def fit(self, X): Fitted estimator """ # Window data - X2=np.lib.stride_tricks.sliding_window_view(X, window_shape=self.window) - X2=X2[:-self.horizon] + y2=np.lib.stride_tricks.sliding_window_view(y, window_shape=self.window) + # Ignore the final horizon values: need to store these for pred with empty y + y2=y2[:-self.horizon] # Extract y y=X[self.window+self.horizon-1:] self.regressor.fit(X2,y) return self - def predict(self, X): + def predict(self, y=None, X=None): """Predict values for time series X. NOTE: will not be able to predict the first """ - X2 = np.lib.stride_tricks.sliding_window_view(X, window_shape=self.window) - return self.regressor.predict(X2[self.horizon:]) + # TODO deal with case y =None + return self.regressor.predict(y[-self.window:]) + + def forecast(self, y, X=None): + """Forecast values for time series X. + + NOTE: deal with horizons + """ + self.fit(y,X) + return self.predict(y[:self.window]) \ No newline at end of file