diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c4e91ec98..4b7b1eaa22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ but cannot always guarantee backwards compatibility. Changes that may **break co **Fixed** +- Fixed `MIDAS` failing on short multivariate or probabilistic series when padding to a complete low-frequency period. [#3304](https://github.com/unit8co/darts/pull/3304) by [Aryan R. Ganjoo](https://github.com/AryanRG). - Fixed `CatBoostModel` with a `"gaussian"` likelihood reporting the variance instead of the standard deviation when predicting with `predict_likelihood_parameters=True`. The `*_sigma` component now holds the standard deviation, consistent with the torch `GaussianLikelihood`. Sampled forecasts are unaffected. [#3302](https://github.com/unit8co/darts/pull/3302) by [Miral Amin](https://github.com/aminmiral). - Fixed scalar division by a `TimeSeries` raising an error for nonzero values. [#3298](https://github.com/unit8co/darts/pull/3298) by [Gabriel Demetrios Lafis](https://github.com/galafis). - Fixed compatibility with pandas 3.1: fitting and predicting models on series with a daily frequency failed with a `TypeError`, and `MIDAS` accepted a series whose frequency was already the target low frequency. [#3289](https://github.com/unit8co/darts/pull/3289) by [Nicolò Angileri](https://github.com/nicoloangileri). diff --git a/darts/dataprocessing/transformers/midas.py b/darts/dataprocessing/transformers/midas.py index e3bf8015ad..9ec5804f52 100644 --- a/darts/dataprocessing/transformers/midas.py +++ b/darts/dataprocessing/transformers/midas.py @@ -242,9 +242,9 @@ def up_sample(low_df: pd.DataFrame, high_period): first_idx = first_up_sampled.index.get_loc(df.index[0]) last_idx = first_idx + series_size - 1 - start_chunk = np.empty((first_idx, 1, 1)) + start_chunk = np.empty((first_idx, n_cols_in, n_samples)) start_chunk.fill(np.nan) - end_chunk = np.empty((max_size - 1 - last_idx, 1, 1)) + end_chunk = np.empty((max_size - 1 - last_idx, n_cols_in, n_samples)) end_chunk.fill(np.nan) arr = np.concatenate([start_chunk, arr, end_chunk]) # arr has shape (n time steps, n components, n samples) diff --git a/darts/tests/dataprocessing/transformers/test_midas.py b/darts/tests/dataprocessing/transformers/test_midas.py index 2c19d7e725..be84ea8747 100644 --- a/darts/tests/dataprocessing/transformers/test_midas.py +++ b/darts/tests/dataprocessing/transformers/test_midas.py @@ -485,6 +485,36 @@ def test_inverse_transform_low_freq_period_before_fitted_start(self): ) assert inversed == expected + @pytest.mark.parametrize("high_freq", ["MS", "ME"]) + @pytest.mark.parametrize("low_freq", ["QS", "QE"]) + @pytest.mark.parametrize( + "start,length", [(0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (2, 1)] + ) + @pytest.mark.parametrize("n_components,n_samples", [(1, 1), (2, 1), (1, 3), (2, 3)]) + def test_short_series_roundtrip( + self, high_freq, low_freq, start, length, n_components, n_samples + ): + monthly = TimeSeries.from_times_and_values( + times=pd.date_range("2020-01-01", periods=3, freq=high_freq)[ + start : start + length + ], + values=np.arange(length * n_components * n_samples, dtype=float).reshape( + length, n_components, n_samples + ), + ) + midas = MIDAS(low_freq=low_freq, strip=False) + quarterly = midas.fit_transform(monthly) + + # Each month occupies its own group of components, with absent months padded. + expected = np.full((3, n_components, n_samples), np.nan) + expected[start : start + length] = monthly.all_values() + np.testing.assert_equal( + quarterly.all_values(), expected.reshape(1, 3 * n_components, n_samples) + ) + expected_time = "2020-01-01" if low_freq == "QS" else "2020-03-31" + assert quarterly.start_time() == pd.Timestamp(expected_time) + assert midas.inverse_transform(quarterly) == monthly + def test_multiple_ts(self): """ Verify that MIDAS works as expected with multiple series of different "high" frequencies (monthly and quarterly