def _inverse_transform(self, X, y=None): """Logic used by `inverse_transform` to reverse transformation on `X`. Parameters ---------- X : pd.Series or pd.DataFrame Data to be inverse transformed y : ignored argument for interface compatibility Additional data, e.g., labels for transformation Returns ------- Xt : pd.Series or pd.DataFrame, same type as X inverse transformed version of X """ Z = X is_df = isinstance(Z, pd.DataFrame) is_contained_by_fit_z, pad_z_inv = self._check_inverse_transform_index( Z) # If `Z` is entirely contained in fitted `_Z` we can just return # the values from the timeseires stored in `fit` as a shortcut if is_contained_by_fit_z: Z_inv = self._Z.loc[Z.index, :] if is_df else self._Z.loc[Z.index] else: Z_inv = Z.copy() for i, lag_info in enumerate( zip(self._lags[::-1], self._prior_cum_lags[::-1])): lag, prior_cum_lag = lag_info _lags = self._lags[::-1][i + 1:] _transformed = _diff_transform(self._Z, _lags) # Determine index values for initial values needed to reverse # the differencing for the specified lag if pad_z_inv: cutoff = Z_inv.index[0] else: cutoff = Z_inv.index[prior_cum_lag + lag] fh = ForecastingHorizon(np.arange(-1, -(lag + 1), -1)) index = fh.to_absolute(cutoff).to_pandas() if is_df: prior_n_timepoint_values = _transformed.loc[index, :] else: prior_n_timepoint_values = _transformed.loc[index] if pad_z_inv: Z_inv = pd.concat([prior_n_timepoint_values, Z_inv]) else: Z_inv.update(prior_n_timepoint_values) Z_inv = _inverse_diff(Z_inv, lag) if pad_z_inv: Z_inv = Z_inv.loc[Z.index, :] if is_df else Z_inv.loc[Z.index] Xt = Z_inv return Xt
def test_to_absolute_freq(freqstr): """Test conversion when anchorings included in frequency.""" train = pd.Series(1, index=pd.date_range("2021-10-06", freq=freqstr, periods=3)) fh = ForecastingHorizon([1, 2, 3]) abs_fh = fh.to_absolute(train.index[-1]) assert abs_fh._values.freqstr == freqstr
def _inverse_transform(self, Z, X=None): """Logic used by `inverse_transform` to reverse transformation on `Z`. Parameters ---------- Z : pd.Series or pd.DataFrame A time series to reverse the transformation on. Returns ------- Z_inv : pd.Series or pd.DataFrame The reconstructed timeseries after the transformation has been reversed. """ is_df = isinstance(Z, pd.DataFrame) is_contained_by_fit_z, pad_z_inv = self._check_inverse_transform_index( Z) # If `Z` is entirely contained in fitted `_Z` we can just return # the values from the timeseires stored in `fit` as a shortcut if is_contained_by_fit_z: Z_inv = self._Z.loc[Z.index, :] if is_df else self._Z.loc[Z.index] else: Z_inv = Z.copy() for i, lag_info in enumerate( zip(self._lags[::-1], self._prior_cum_lags[::-1])): lag, prior_cum_lag = lag_info _lags = self._lags[::-1][i + 1:] _transformed = _diff_transform(self._Z, _lags) # Determine index values for initial values needed to reverse # the differencing for the specified lag if pad_z_inv: cutoff = Z_inv.index[0] else: cutoff = Z_inv.index[prior_cum_lag + lag] fh = ForecastingHorizon(np.arange(-1, -(lag + 1), -1)) index = fh.to_absolute(cutoff).to_pandas() if is_df: prior_n_timepoint_values = _transformed.loc[index, :] else: prior_n_timepoint_values = _transformed.loc[index] if pad_z_inv: Z_inv = pd.concat([prior_n_timepoint_values, Z_inv]) else: Z_inv.update(prior_n_timepoint_values) Z_inv = _inverse_diff(Z_inv, lag) if pad_z_inv: Z_inv = Z_inv.loc[Z.index, :] if is_df else Z_inv.loc[Z.index] return Z_inv
def test_relative_to_relative(freqstr): """Test converting between relative and absolute.""" # Converts from relative to absolute and back to relative train = pd.Series(1, index=pd.date_range("2021-10-06", freq=freqstr, periods=3)) fh = ForecastingHorizon([1, 2, 3]) abs_fh = fh.to_absolute(train.index[-1]) converted_rel_fh = abs_fh.to_relative(train.index[-1]) assert_array_equal(fh, converted_rel_fh)
def test_absolute_to_absolute_with_integer_horizon(freqstr): """Test converting between absolute and relative with integer horizon.""" # Converts from absolute to relative and back to absolute train = pd.Series(1, index=pd.date_range("2021-10-06", freq=freqstr, periods=3)) fh = ForecastingHorizon([1, 2, 3]) abs_fh = fh.to_absolute(train.index[-1]) converted_abs_fh = abs_fh.to_relative(train.index[-1]).to_absolute( train.index[-1]) assert_array_equal(abs_fh, converted_abs_fh) assert converted_abs_fh._values.freqstr == freqstr
def test_relative_to_relative_with_timedelta_horizon(freqstr): """Test converting between relative and absolute with timedelta horizons.""" # Converts from relative to absolute and back to relative train = pd.Series(1, index=pd.date_range("2021-10-06", freq=freqstr, periods=3)) count, unit = _get_intervals_count_and_unit(freq=freqstr) fh = ForecastingHorizon( pd.timedelta_range(pd.to_timedelta(count, unit=unit), freq=freqstr, periods=3)) abs_fh = fh.to_absolute(train.index[-1]) converted_rel_fh = abs_fh.to_relative(train.index[-1]) assert_array_equal(converted_rel_fh, np.arange(1, 4))
def _check_inverse_transform_index(self, Z): """Check fitted series contains indices needed in inverse_transform.""" first_idx = Z.index.min() orig_first_idx, orig_last_idx = self._Z.index.min(), self._Z.index.max( ) is_contained_by_fitted_z = False is_future = False if first_idx < orig_first_idx: msg = [ "Some indices of `Z` are prior to timeseries used in `fit`.", "Reconstruction via `inverse_transform` is not possible.", ] raise ValueError(" ".join(msg)) elif Z.index.difference(self._Z.index).shape[0] == 0: is_contained_by_fitted_z = True elif first_idx > orig_last_idx: is_future = True pad_z_inv = self.drop_na or is_future cutoff = Z.index[0] if pad_z_inv else Z.index[ self._cumulative_lags[-1]] fh = ForecastingHorizon( np.arange(-1, -(self._cumulative_lags[-1] + 1), -1)) index = fh.to_absolute(cutoff).to_pandas() index_diff = index.difference(self._Z.index) if index_diff.shape[0] != 0 and not is_contained_by_fitted_z: msg = [ f"Inverse transform requires indices {index}", "to have been stored in `fit()`,", f"but the indices {index_diff} were not found.", ] raise ValueError(" ".join(msg)) return is_contained_by_fitted_z, pad_z_inv