def _reconcile_from_S_and_G( series: TimeSeries, S: np.ndarray, G: np.ndarray ) -> TimeSeries: """ Returns the TimeSeries linearly reconciled from the projection matrix G and the summation matrix S. """ y_hat = series.all_values(copy=False) reconciled_values = S @ G @ y_hat # (n, m) * (m, n) * (time, n, samples) return series.with_values(reconciled_values)
def ts_inverse_transform(series: TimeSeries, transformer, *args, **kwargs) -> TimeSeries: component_mask = kwargs.get("component_mask", None) tr_out = transformer.inverse_transform( Scaler._reshape_in(series, component_mask=component_mask)) inv_transformed_vals = Scaler._reshape_out( series, tr_out, component_mask=component_mask) return series.with_values(inv_transformed_vals)
def ts_inverse_transform(series: TimeSeries, lmbda: Union[Sequence[float], pd.core.series.Series], **kwargs) -> TimeSeries: component_mask = kwargs.get("component_mask", None) vals = BoxCox._reshape_in(series, component_mask=component_mask) inv_transformed_vals = np.stack( [inv_boxcox(vals[:, i], lmbda[i]) for i in range(series.width)], axis=1) return series.with_values( BoxCox._reshape_out(series, inv_transformed_vals, component_mask=component_mask))
def filter(self, series: TimeSeries, num_samples: int = 1) -> TimeSeries: """ Fits the Gaussian Process on the observations and returns samples from the Gaussian Process, or its mean values if `num_samples` is set to 1. Parameters ---------- series The series of observations used to infer the values according to the specified Gaussian Process. This must be a deterministic series (containing one sample). num_samples: int, default: 1 Number of times a prediction is sampled from the Gaussian Process. If set to 1, the mean values will be returned instead. Returns ------- TimeSeries A stochastic ``TimeSeries`` sampled from the Gaussian Process, or its mean if `num_samples` is set to 1. """ super().filter(series) values = series.values(copy=False) if series.has_datetime_index: times = np.arange(series.n_timesteps).reshape(-1, 1) else: times = series.time_index.values.reshape(-1, 1) not_nan_mask = np.all(~np.isnan(values), axis=1) self.model.fit(times[not_nan_mask, :], values[not_nan_mask, :]) if num_samples == 1: filtered_values = self.model.predict(times) else: filtered_values = self.model.sample_y(times, n_samples=num_samples) filtered_values = filtered_values.reshape(len(times), -1, num_samples) return series.with_values(filtered_values)
def filter( self, series: TimeSeries, covariates: Optional[TimeSeries] = None, num_samples: int = 1, ) -> TimeSeries: """ Sequentially applies the Kalman filter on the provided series of observations. Parameters ---------- series : TimeSeries The series of outputs (observations) used to infer the underlying outputs according to the specified Kalman process. This must be a deterministic series (containing one sample). covariates : Optional[TimeSeries] An optional series of inputs (control signal), necessary if the Kalman filter was initialized with covariates. This must be a deterministic series (containing one sample). num_samples : int, default: 1 The number of samples to generate from the inferred distribution of the output z. If this is set to 1, the output is a `TimeSeries` containing a single sample using the mean of the distribution. Returns ------- TimeSeries A (stochastic) `TimeSeries` of the inferred output z, of the same width as the input series. """ super().filter(series) raise_if( self.kf is None, "The Kalman filter has not been fitted yet. Call `fit()` first " "or provide Kalman filter in constructor.", ) raise_if_not( series.width == self.dim_y, "The provided TimeSeries dimensionality does not match " "the output dimensionality of the Kalman filter.", ) raise_if( covariates is not None and not self._expect_covariates, "Covariates were provided, but the Kalman filter was not fitted with covariates.", ) if self._expect_covariates: raise_if( covariates is None, "The Kalman filter was fitted with covariates, but these were not provided.", ) raise_if_not( covariates.is_deterministic, "The covariates must be deterministic (observations).", ) covariates = covariates.slice_intersect(series) raise_if_not( series.has_same_time_as(covariates), "The number of timesteps in the series and the covariates must match.", ) kf = deepcopy(self.kf) y_values = series.values(copy=False) if self._expect_covariates: u_values = covariates.values(copy=False) # set control signal to 0 if it contains NaNs: u_values = np.nan_to_num(u_values, copy=True, nan=0.0) else: u_values = np.zeros((len(y_values), 0)) # For each time step, we'll sample "n_samples" from a multivariate Gaussian # whose mean vector and covariance matrix come from the Kalman filter. sampled_outputs = np.zeros((len(y_values), self.dim_y, num_samples)) for i in range(len(y_values)): y = y_values[i, :].reshape(-1, 1) u = u_values[i, :].reshape(-1, 1) if np.isnan(y).any(): y = None kf.step(y, u) mean_vec = kf.y_filtereds[-1].reshape(self.dim_y, ) if num_samples == 1: sampled_outputs[i, :, 0] = mean_vec else: # The measurement covariance matrix is given by the sum of the covariance matrix of the # state estimate (transformed by C) and the covariance matrix of the measurement noise. cov_matrix = ( kf.state_space.c @ kf.p_filtereds[-1] @ kf.state_space.c.T + kf.r) sampled_outputs[i, :, :] = np.random.multivariate_normal( mean_vec, cov_matrix, size=num_samples).T return series.with_values(sampled_outputs)