From 8743c8b1706b755346fe0a2c58c2c219e3e6b3d4 Mon Sep 17 00:00:00 2001 From: David Kleindienst Date: Thu, 23 Feb 2023 10:36:27 +0100 Subject: [PATCH 1/4] unit test for component order independence --- .../transformers/test_reconciliation.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/darts/tests/dataprocessing/transformers/test_reconciliation.py b/darts/tests/dataprocessing/transformers/test_reconciliation.py index fe2100ed22..8be6eb09c1 100644 --- a/darts/tests/dataprocessing/transformers/test_reconciliation.py +++ b/darts/tests/dataprocessing/transformers/test_reconciliation.py @@ -2,6 +2,7 @@ import unittest import numpy as np +from pandas import date_range from darts import TimeSeries, concatenate from darts.dataprocessing.transformers.reconciliation import ( @@ -179,3 +180,47 @@ def test_more_intricate_hierarchy(self): recon = MinTReconciliator("wls_val") recon.fit(self.series_complex) self._assert_reconciliation_complex(recon) + + def test_reconcilliation_is_order_independent(self): + dates = date_range("2020-01-01", "2020-12-31", freq="D") + nr_dates = len(dates) + t1 = TimeSeries.from_times_and_values( + dates, 2 * np.ones(nr_dates), columns=["T1"] + ) + t2 = TimeSeries.from_times_and_values( + dates, 5 * np.ones(nr_dates), columns=["T2"] + ) + t3 = TimeSeries.from_times_and_values(dates, np.ones(nr_dates), columns=["T3"]) + tsum = TimeSeries.from_times_and_values( + dates, 9 * np.ones(nr_dates), columns=["T_sum"] + ) + ts_1 = concatenate([t1, t2, t3, tsum], axis="component") + ts_2 = concatenate([tsum, t1, t2, t3], axis="component") + + def assert_ts_are_equal(ts1, ts2): + for comp in ["T1", "T2", "T3", "T_sum"]: + self.assertEqual(ts1[comp], ts2[comp]) + + hierarchy = {"T1": ["T_sum"], "T2": ["T_sum"], "T3": ["T_sum"]} + ts_1 = ts_1.with_hierarchy(hierarchy) + ts_2 = ts_2.with_hierarchy(hierarchy) + assert_ts_are_equal(ts_1, ts_2) + + S1 = _get_summation_matrix(ts_1) + S2 = _get_summation_matrix(ts_2) + np.testing.assert_array_equal(S1, S2) + + ts_1_reconc = TopDownReconciliator().fit_transform(ts_1) + ts_2_reconc = TopDownReconciliator().fit_transform(ts_2) + + assert_ts_are_equal(ts_1_reconc, ts_2_reconc) + + ts_1_reconc = MinTReconciliator().fit_transform(ts_1) + ts_2_reconc = MinTReconciliator().fit_transform(ts_2) + + assert_ts_are_equal(ts_1_reconc, ts_2_reconc) + + ts_1_reconc = BottomUpReconciliator().transform(ts_1) + ts_2_reconc = BottomUpReconciliator().transform(ts_2) + + assert_ts_are_equal(ts_1_reconc, ts_2_reconc) From 4a03e9e4d7c89515d2be22f8d811a129304e31e8 Mon Sep 17 00:00:00 2001 From: David Kleindienst Date: Thu, 23 Feb 2023 12:56:04 +0100 Subject: [PATCH 2/4] remove incorrect assumption from test --- .../tests/dataprocessing/transformers/test_reconciliation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/darts/tests/dataprocessing/transformers/test_reconciliation.py b/darts/tests/dataprocessing/transformers/test_reconciliation.py index 8be6eb09c1..49d653a505 100644 --- a/darts/tests/dataprocessing/transformers/test_reconciliation.py +++ b/darts/tests/dataprocessing/transformers/test_reconciliation.py @@ -206,9 +206,8 @@ def assert_ts_are_equal(ts1, ts2): ts_2 = ts_2.with_hierarchy(hierarchy) assert_ts_are_equal(ts_1, ts_2) - S1 = _get_summation_matrix(ts_1) - S2 = _get_summation_matrix(ts_2) - np.testing.assert_array_equal(S1, S2) + P1 = BottomUpReconciliator().get_projection_matrix(ts_1) + P2 = BottomUpReconciliator().get_projection_matrix(ts_2) ts_1_reconc = TopDownReconciliator().fit_transform(ts_1) ts_2_reconc = TopDownReconciliator().fit_transform(ts_2) From 9384b0b8ce7114b86858f3edf1a86ba702888377 Mon Sep 17 00:00:00 2001 From: David Kleindienst Date: Thu, 23 Feb 2023 13:55:19 +0100 Subject: [PATCH 3/4] Fix get_projection_matrix --- darts/dataprocessing/transformers/reconciliation.py | 12 +++++++++--- .../transformers/test_reconciliation.py | 3 --- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/darts/dataprocessing/transformers/reconciliation.py b/darts/dataprocessing/transformers/reconciliation.py index b972869baa..0bdbfe972d 100644 --- a/darts/dataprocessing/transformers/reconciliation.py +++ b/darts/dataprocessing/transformers/reconciliation.py @@ -83,8 +83,14 @@ class BottomUpReconciliator(BaseDataTransformer): @staticmethod def get_projection_matrix(series): - n, m = series.n_components, len(series.bottom_level_components) - return np.concatenate([np.zeros((m, n - m)), np.eye(m)], axis=1) + leaves_seq = list(series.bottom_level_components) + n, m = series.n_components, len(leaves_seq) + leaves_indexes = {l: i for i, l in enumerate(leaves_seq)} + G = np.zeros((m, n)) + for i, c in enumerate(series.components): + if c in leaves_indexes: + G[leaves_indexes[c], i] = 1.0 + return G @staticmethod def ts_transform(series: TimeSeries, *args, **kwargs) -> TimeSeries: @@ -130,7 +136,7 @@ def get_projection_matrix(series): proportions = sum_base / sum_total G = np.zeros((m, n)) - G[:, 0] = proportions + G[:, list(series.components).index(series.top_level_component)] = proportions return G diff --git a/darts/tests/dataprocessing/transformers/test_reconciliation.py b/darts/tests/dataprocessing/transformers/test_reconciliation.py index 49d653a505..16136aceb0 100644 --- a/darts/tests/dataprocessing/transformers/test_reconciliation.py +++ b/darts/tests/dataprocessing/transformers/test_reconciliation.py @@ -206,9 +206,6 @@ def assert_ts_are_equal(ts1, ts2): ts_2 = ts_2.with_hierarchy(hierarchy) assert_ts_are_equal(ts_1, ts_2) - P1 = BottomUpReconciliator().get_projection_matrix(ts_1) - P2 = BottomUpReconciliator().get_projection_matrix(ts_2) - ts_1_reconc = TopDownReconciliator().fit_transform(ts_1) ts_2_reconc = TopDownReconciliator().fit_transform(ts_2) From f65cbd4f2d8f4cbc1dc593eb25ac3e29408232ab Mon Sep 17 00:00:00 2001 From: David Kleindienst Date: Thu, 23 Feb 2023 14:00:17 +0100 Subject: [PATCH 4/4] minor improvement --- darts/dataprocessing/transformers/reconciliation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/darts/dataprocessing/transformers/reconciliation.py b/darts/dataprocessing/transformers/reconciliation.py index 0bdbfe972d..268f2b208a 100644 --- a/darts/dataprocessing/transformers/reconciliation.py +++ b/darts/dataprocessing/transformers/reconciliation.py @@ -134,9 +134,9 @@ def get_projection_matrix(series): # compute proportions for each base component proportions = sum_base / sum_total - + top_level_index = list(series.components).index(series.top_level_component) G = np.zeros((m, n)) - G[:, list(series.components).index(series.top_level_component)] = proportions + G[:, top_level_index] = proportions return G