示例#1
0
    def test_kalman(self):
        """KalmanFilter test.
        Creates an increasing sequence of numbers, adds noise and
        assumes the kalman filter predicts values closer to real values
        """
        testing_signal = np.arange(1, 5, 0.1)

        noise = np.random.normal(0, 0.7, testing_signal.shape)
        testing_signal_with_noise = testing_signal + noise

        df = pd.DataFrame(data=testing_signal_with_noise, columns=["signal"])
        testing_signal_with_noise_ts = TimeSeries.from_dataframe(
            df, value_cols=["signal"])

        kf = KalmanFilter(dim_x=1)
        kf.fit(testing_signal_with_noise_ts)
        filtered_ts = kf.filter(testing_signal_with_noise_ts, num_samples=1)
        filtered_values = filtered_ts.univariate_values()

        noise_distance = testing_signal_with_noise - testing_signal
        prediction_distance = filtered_values - testing_signal

        self.assertGreater(noise_distance.std(), prediction_distance.std())
        self.assertEqual(filtered_ts.width, 1)
        self.assertEqual(filtered_ts.n_samples, 1)
示例#2
0
    def __init__(self, dim_x: int = 1, kf: Optional[Kalman] = None):
        """Kalman filter Forecaster

        This model uses a Kalman filter to produce forecasts. It uses a
        :class:`darts.models.filtering.kalman_filter.KalmanFilter` object
        and treats future values as missing values.

        The model can optionally receive a :class:`nfoursid.kalman.Kalman`
        object specifying the Kalman filter, or, if not specified, the filter
        will be trained using the N4SID system identification algorithm.

        Parameters
        ----------
        dim_x : int
            Size of the Kalman filter state vector.
        kf : nfoursid.kalman.Kalman
            Optionally, an instance of `nfoursid.kalman.Kalman`.
            If this is provided, the parameter dim_x is ignored. This instance will be copied for every
            call to `predict()`, so the state is not carried over from one time series to another across several
            calls to `predict()`.
            The various dimensionalities of the filter must match those of the `TimeSeries` used when
            calling `predict()`.
            If this is specified, it is still necessary to call `fit()` before calling `predict()`,
            although this will have no effect on the Kalman filter.
        """
        super().__init__()
        self.dim_x = dim_x
        self.kf = kf
        self.darts_kf = KalmanFilter(dim_x, kf)
示例#3
0
    def test_kalman_samples(self):
        kf = KalmanFilter(dim_x=1)

        series = tg.sine_timeseries(length=30, value_frequency=0.1)

        kf.fit(series)
        prediction = kf.filter(series, num_samples=10)

        self.assertEqual(prediction.width, 1)
        self.assertEqual(prediction.n_samples, 10)
示例#4
0
    def test_kalman_covariates(self):
        kf = KalmanFilter(dim_x=2)

        series = tg.sine_timeseries(length=30, value_frequency=0.1)
        covariates = -series.copy()

        kf.fit(series, covariates=covariates)
        prediction = kf.filter(series, covariates=covariates)

        self.assertEqual(prediction.width, 1)
        self.assertEqual(prediction.n_samples, 1)
示例#5
0
    def test_kalman_multivariate(self):
        kf = KalmanFilter(dim_x=3)

        sine_ts = tg.sine_timeseries(length=30, value_frequency=0.1)
        noise_ts = tg.gaussian_timeseries(length=30) * 0.1
        series = sine_ts.stack(noise_ts)

        kf.fit(series)
        prediction = kf.filter(series)

        self.assertEqual(prediction.width, 2)
        self.assertEqual(prediction.n_samples, 1)
示例#6
0
    def test_kalman_given_kf(self):
        nfoursid_ss = state_space.StateSpace(a=np.eye(2),
                                             b=np.ones((2, 1)),
                                             c=np.ones((1, 2)),
                                             d=np.ones((1, 1)))
        nfoursid_kf = kalman.Kalman(nfoursid_ss, np.ones((3, 3)) * 0.1)
        kf = KalmanFilter(dim_x=1, kf=nfoursid_kf)

        series = tg.sine_timeseries(length=30, value_frequency=0.1)

        prediction = kf.filter(series, covariates=-series.copy())

        self.assertEqual(kf.dim_u, 1)
        self.assertEqual(kf.dim_x, 2)
        self.assertEqual(prediction.width, 1)
        self.assertEqual(prediction.n_samples, 1)
示例#7
0
    def test_kalman_missing_values(self):
        sine = tg.sine_timeseries(
            length=100,
            value_frequency=0.05) + 0.1 * tg.gaussian_timeseries(length=100)
        values = sine.values()
        values[20:22] = np.nan
        values[28:40] = np.nan
        sine_holes = TimeSeries.from_values(values)
        sine = TimeSeries.from_values(sine.values())

        kf = KalmanFilter(dim_x=2)
        kf.fit(sine_holes[-50:])  # fit on the part with no holes

        # reconstructruction should succeed
        filtered_series = kf.filter(sine_holes, num_samples=100)

        # reconstruction error should be sufficiently small
        self.assertLess(rmse(filtered_series, sine), 0.1)
示例#8
0
class KalmanForecaster(DualCovariatesForecastingModel):
    def __init__(self, dim_x: int = 1, kf: Optional[Kalman] = None):
        """Kalman filter Forecaster

        This model uses a Kalman filter to produce forecasts. It uses a
        :class:`darts.models.filtering.kalman_filter.KalmanFilter` object
        and treats future values as missing values.

        The model can optionally receive a :class:`nfoursid.kalman.Kalman`
        object specifying the Kalman filter, or, if not specified, the filter
        will be trained using the N4SID system identification algorithm.

        Parameters
        ----------
        dim_x : int
            Size of the Kalman filter state vector.
        kf : nfoursid.kalman.Kalman
            Optionally, an instance of `nfoursid.kalman.Kalman`.
            If this is provided, the parameter dim_x is ignored. This instance will be copied for every
            call to `predict()`, so the state is not carried over from one time series to another across several
            calls to `predict()`.
            The various dimensionalities of the filter must match those of the `TimeSeries` used when
            calling `predict()`.
            If this is specified, it is still necessary to call `fit()` before calling `predict()`,
            although this will have no effect on the Kalman filter.
        """
        super().__init__()
        self.dim_x = dim_x
        self.kf = kf
        self.darts_kf = KalmanFilter(dim_x, kf)

    def __str__(self):
        return f"Kalman Filter Forecaster (dim_x={self.dim_x})"

    def _fit(self, series: TimeSeries, future_covariates: Optional[TimeSeries] = None):

        super()._fit(series, future_covariates)
        if self.kf is None:
            self.darts_kf.fit(series=series, covariates=future_covariates)

        return self

    def _predict(
        self,
        n: int,
        future_covariates: Optional[TimeSeries] = None,
        num_samples: int = 1,
    ) -> TimeSeries:

        super()._predict(n, future_covariates, num_samples)

        time_index = self._generate_new_dates(n)
        placeholder_vals = np.zeros((n, self.training_series.width)) * np.nan
        series_future = TimeSeries.from_times_and_values(
            time_index,
            placeholder_vals,
            columns=self.training_series.columns,
            static_covariates=self.training_series.static_covariates,
            hierarchy=self.training_series.hierarchy,
        )
        whole_series = self.training_series.append(series_future)
        filtered_series = self.darts_kf.filter(
            whole_series, covariates=future_covariates, num_samples=num_samples
        )

        return filtered_series[-n:]

    def _is_probabilistic(self) -> bool:
        return True