Skip to content

Commit fe309be

Browse files
authored
[Minor] Componentstacker dataclass and abstraction of stacking and unstacking of components (#1646)
* remove stackers from TimeNet init * refactor stackers to dict * simplify and document set_compunents_stacker * refactor set_components_stacker arg to stacker * add docstring to forward and introduce mode flag instead of passing components_stacker * refactor include components * fix covar_weights * simplify unstack components * use dict to index unstack component functions * remove unused import * fix component_stacker * convert to dataclass * simply stack function names * fix references * fix trend/time * fix pre-existing typo * improve seasonality stacker * revert seasonalities * rename stack/unstack * update timenet * use stack function * kwargs * use stacker abstraction * simplify names * names * fix seasons * explicit update of feature_list and no double returns * conform seasonalities * move stack_all to stacker * ruff
1 parent 773b67a commit fe309be

4 files changed

Lines changed: 232 additions & 206 deletions

File tree

neuralprophet/forecaster.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
from neuralprophet.plot_model_parameters_plotly import plot_parameters as plot_parameters_plotly
4545
from neuralprophet.plot_utils import get_valid_configuration, log_warning_deprecation_plotly, select_plotting_backend
4646
from neuralprophet.uncertainty import Conformal
47-
from neuralprophet.utils_time_dataset import ComponentStacker
4847

4948
log = logging.getLogger("NP.forecaster")
5049

@@ -1210,7 +1209,6 @@ def fit(
12101209
max_lags=self.config_model.max_lags,
12111210
config_seasonality=self.config_seasonality,
12121211
lagged_regressor_config=self.config_lagged_regressors,
1213-
feature_indices={},
12141212
)
12151213
dataset = self._create_dataset(df, predict_mode=False, components_stacker=train_components_stacker)
12161214
# Determine the max_number of epochs
@@ -1253,7 +1251,6 @@ def fit(
12531251
n_forecasts=self.config_model.n_forecasts,
12541252
config_seasonality=self.config_seasonality,
12551253
lagged_regressor_config=self.config_lagged_regressors,
1256-
feature_indices={},
12571254
)
12581255
dataset_val = self._create_dataset(df_val, predict_mode=False, components_stacker=val_components_stacker)
12591256
loader_val = DataLoader(dataset_val, batch_size=min(1024, len(dataset_val)), shuffle=False, drop_last=False)
@@ -1275,9 +1272,9 @@ def fit(
12751272
if not self.fitted:
12761273
self.model = self._init_model()
12771274

1278-
self.model.set_components_stacker(components_stacker=train_components_stacker, mode="train")
1275+
self.model.set_components_stacker(stacker=train_components_stacker, mode="train")
12791276
if validation_enabled:
1280-
self.model.set_components_stacker(components_stacker=val_components_stacker, mode="val")
1277+
self.model.set_components_stacker(stacker=val_components_stacker, mode="val")
12811278

12821279
# Find suitable learning rate if not set
12831280
if self.config_train.learning_rate is None:
@@ -1491,7 +1488,6 @@ def test(self, df: pd.DataFrame, verbose: bool = True):
14911488
max_lags=self.config_model.max_lags,
14921489
config_seasonality=self.config_seasonality,
14931490
lagged_regressor_config=self.config_lagged_regressors,
1494-
feature_indices={},
14951491
)
14961492
dataset = self._create_dataset(df, predict_mode=False, components_stacker=components_stacker)
14971493
self.model.set_components_stacker(components_stacker, mode="test")
@@ -2128,7 +2124,7 @@ def predict_seasonal_components(self, df: pd.DataFrame, quantile: float = 0.5):
21282124
prev_n_lags = self.config_ar.n_lags
21292125
prev_max_lags = self.config_model.max_lags
21302126
prev_n_forecasts = self.config_model.n_forecasts
2131-
prev_predict_components_stacker = self.model.predict_components_stacker
2127+
prev_predict_components_stacker = self.model.components_stacker["predict"]
21322128

21332129
self.config_model.max_lags = 0
21342130
self.config_ar.n_lags = 0
@@ -2138,7 +2134,7 @@ def predict_seasonal_components(self, df: pd.DataFrame, quantile: float = 0.5):
21382134
df = _check_dataframe(self, df, check_y=False, exogenous=False)
21392135
df = _normalize(df=df, config_normalization=self.config_normalization)
21402136
for df_name, df_i in df.groupby("ID"):
2141-
feature_unstackor = ComponentStacker(
2137+
feature_unstackor = utils_time_dataset.ComponentStacker(
21422138
n_lags=0,
21432139
max_lags=0,
21442140
n_forecasts=1,
@@ -2169,12 +2165,12 @@ def predict_seasonal_components(self, df: pd.DataFrame, quantile: float = 0.5):
21692165
meta_name_tensor = None
21702166
elif self.model.config_seasonality.global_local in ["local", "glocal"]:
21712167
meta = OrderedDict()
2172-
time_input = feature_unstackor.unstack_component("time", inputs_tensor)
2168+
time_input = feature_unstackor.unstack("time", inputs_tensor)
21732169
meta["df_name"] = [df_name for _ in range(time_input.shape[0])]
21742170
meta_name_tensor = torch.tensor([self.model.id_dict[i] for i in meta["df_name"]]) # type: ignore
21752171
else:
21762172
meta_name_tensor = None
2177-
seasonalities_input = feature_unstackor.unstack_component("seasonalities", inputs_tensor)
2173+
seasonalities_input = feature_unstackor.unstack("seasonalities", inputs_tensor)
21782174
for name in self.config_seasonality.periods:
21792175
features = seasonalities_input[name]
21802176
quantile_index = self.config_model.quantiles.index(quantile)
@@ -2198,7 +2194,7 @@ def predict_seasonal_components(self, df: pd.DataFrame, quantile: float = 0.5):
21982194
self.config_ar.n_lags = prev_n_lags
21992195
self.config_model.max_lags = prev_max_lags
22002196
self.config_model.n_forecasts = prev_n_forecasts
2201-
self.model.predict_components_stacker = prev_predict_components_stacker
2197+
self.model.components_stacker["predict"] = prev_predict_components_stacker
22022198

22032199
return df
22042200

@@ -2989,7 +2985,6 @@ def _predict_raw(self, df, df_name, include_components=False):
29892985
max_lags=self.config_model.max_lags,
29902986
config_seasonality=self.config_seasonality,
29912987
lagged_regressor_config=self.config_lagged_regressors,
2992-
feature_indices={},
29932988
)
29942989
dataset = self._create_dataset(df, predict_mode=True, components_stacker=components_stacker)
29952990
self.model.set_components_stacker(components_stacker, mode="predict")
@@ -3066,7 +3061,7 @@ def _predict_raw(self, df, df_name, include_components=False):
30663061
elif multiplicative:
30673062
# output absolute value of respective additive component
30683063
components[name] = value * trend * scale_y # type: ignore
3069-
3064+
self.model.reset_compute_components()
30703065
else:
30713066
components = None
30723067

neuralprophet/time_dataset.py

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -104,54 +104,36 @@ def __init__(
104104
self.df["ds"] = self.df["ds"].apply(lambda x: x.timestamp()) # Convert to Unix timestamp in seconds
105105
self.df_tensors["ds"] = torch.tensor(self.df["ds"].values, dtype=torch.int64)
106106

107+
self.seasonalities = None
107108
if self.config_seasonality is not None and hasattr(self.config_seasonality, "periods"):
108109
self.calculate_seasonalities()
109110

110111
# Construct index map
111112
self.sample2index_map, self.length = self.create_sample2index_map(self.df, self.df_tensors)
112113

114+
# Stack all features into one large tensor
113115
self.components_stacker = components_stacker
114-
115-
self.stack_all_features()
116+
self.all_features = self.stack_all_features()
116117

117118
def stack_all_features(self):
118119
"""
119120
Stack all features into one large tensor by calling individual stacking methods.
120121
"""
121-
feature_list = []
122-
123-
current_idx = 0
124-
125-
# Call individual stacking functions
126-
current_idx = self.components_stacker.stack_trend_component(self.df_tensors, feature_list, current_idx)
127-
current_idx = self.components_stacker.stack_targets_component(self.df_tensors, feature_list, current_idx)
128-
129-
current_idx = self.components_stacker.stack_lags_component(
130-
self.df_tensors, feature_list, current_idx, self.config_ar.n_lags
131-
)
132-
current_idx = self.components_stacker.stack_lagged_regerssors_component(
133-
self.df_tensors, feature_list, current_idx, self.config_lagged_regressors
134-
)
135-
current_idx = self.components_stacker.stack_additive_events_component(
136-
self.df_tensors, feature_list, current_idx, self.additive_event_and_holiday_names
137-
)
138-
current_idx = self.components_stacker.stack_multiplicative_events_component(
139-
self.df_tensors, feature_list, current_idx, self.multiplicative_event_and_holiday_names
140-
)
141-
current_idx = self.components_stacker.stack_additive_regressors_component(
142-
self.df_tensors, feature_list, current_idx, self.additive_regressors_names
143-
)
144-
current_idx = self.components_stacker.stack_multiplicative_regressors_component(
145-
self.df_tensors, feature_list, current_idx, self.multiplicative_regressors_names
146-
)
147-
148-
if self.config_seasonality is not None and hasattr(self.config_seasonality, "periods"):
149-
current_idx = self.components_stacker.stack_seasonalities_component(
150-
feature_list, current_idx, self.config_seasonality, self.seasonalities
151-
)
122+
# Add seasonalities to df_tensors, this needs to be done after create_sample2index_map, before stacking.
123+
self.df_tensors["seasonalities"] = self.seasonalities
124+
component_args: dict = {
125+
"time": {},
126+
"targets": {},
127+
"lags": {"n_lags": self.config_ar.n_lags},
128+
"lagged_regressors": {"config": self.config_lagged_regressors},
129+
"additive_events": {"names": self.additive_event_and_holiday_names},
130+
"multiplicative_events": {"names": self.multiplicative_event_and_holiday_names},
131+
"additive_regressors": {"names": self.additive_regressors_names},
132+
"multiplicative_regressors": {"names": self.multiplicative_regressors_names},
133+
"seasonalities": {"config": self.config_seasonality},
134+
}
152135

153-
# Concatenate all features into one big tensor
154-
self.all_features = torch.cat(feature_list, dim=1) # Concatenating along the second dimension
136+
return self.components_stacker.stack_all_features(self.df_tensors, component_args)
155137

156138
def calculate_seasonalities(self):
157139
"""Computes Fourier series components with the specified frequency and order."""

0 commit comments

Comments
 (0)