From 2dd3e7b6b7fa85d31e1930e90be333ccafac752f Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sun, 26 Jun 2022 16:39:31 +0200 Subject: [PATCH 01/16] categorical static covariate support for TFTModel --- darts/models/forecasting/tft_model.py | 149 +++++++++++++++++++--- darts/models/forecasting/tft_submodels.py | 121 ++++++++---------- 2 files changed, 184 insertions(+), 86 deletions(-) diff --git a/darts/models/forecasting/tft_model.py b/darts/models/forecasting/tft_model.py index caa24845e6..246e12d54c 100644 --- a/darts/models/forecasting/tft_model.py +++ b/darts/models/forecasting/tft_model.py @@ -19,6 +19,7 @@ _GateAddNorm, _GatedResidualNetwork, _InterpretableMultiHeadAttention, + _MultiEmbedding, _VariableSelectionNetwork, ) from darts.models.forecasting.torch_forecasting_model import MixedCovariatesTorchModel @@ -43,14 +44,15 @@ def __init__( output_dim: Tuple[int, int], variables_meta: Dict[str, Dict[str, List[str]]], num_static_components: int, - hidden_size: Union[int, List[int]] = 16, - lstm_layers: int = 1, - num_attention_heads: int = 4, - full_attention: bool = False, - feed_forward: str = "GatedResidualNetwork", - hidden_continuous_size: int = 8, - dropout: float = 0.1, - add_relative_index: bool = False, + hidden_size: Union[int, List[int]], + lstm_layers: int, + num_attention_heads: int, + full_attention: bool, + feed_forward: str, + hidden_continuous_size: int, + categorical_embedding_sizes: Dict[str, Tuple[int, int]], + dropout: float, + add_relative_index: bool, **kwargs, ): @@ -81,7 +83,10 @@ def __init__( Set the feedforward network block. default `GatedResidualNetwork` or one of the glu variant. Defaults to `GatedResidualNetwork`. hidden_continuous_size : int - default for hidden size for processing continuous variables' + default for hidden size for processing continuous variables. + categorical_embedding_sizes : int + Embedding size for categorical static covariates. Only effective if the target series contains + categorical (non-numeric) static covariates. dropout : float Fraction of neurons affected by Dropout. add_relative_index : bool @@ -103,6 +108,7 @@ def __init__( self.num_static_components = num_static_components self.hidden_size = hidden_size self.hidden_continuous_size = hidden_continuous_size + self.categorical_embedding_sizes = categorical_embedding_sizes self.lstm_layers = lstm_layers self.num_attention_heads = num_attention_heads self.full_attention = full_attention @@ -123,23 +129,41 @@ def __init__( # _attn: Attention # # processing inputs + # embeddings + self.input_embeddings = _MultiEmbedding( + embedding_sizes=categorical_embedding_sizes, + variable_names=self.categorical_static_variables, + ) + # continuous variable processing self.prescalers_linear = { name: nn.Linear( - 1 if name not in self.static_variables else self.num_static_components, + 1 + if name not in self.numeric_static_variables + else self.num_static_components, self.hidden_continuous_size, ) for name in self.reals } + # static (categorical and numerical) variables static_input_sizes = { - name: self.hidden_continuous_size for name in self.static_variables + name: self.input_embeddings.output_size[name] + for name in self.categorical_static_variables } + static_input_sizes.update( + { + name: self.hidden_continuous_size + for name in self.numeric_static_variables + } + ) self.static_covariates_vsn = _VariableSelectionNetwork( input_sizes=static_input_sizes, hidden_size=self.hidden_size, - input_embedding_flags={}, # this would be required for categorical inputs + input_embedding_flags={ + name: True for name in self.categorical_static_variables + }, dropout=self.dropout, prescalers=self.prescalers_linear, single_variable_grns={}, @@ -158,7 +182,7 @@ def __init__( self.encoder_vsn = _VariableSelectionNetwork( input_sizes=encoder_input_sizes, hidden_size=self.hidden_size, - input_embedding_flags={}, # this would be required for categorical inputs + input_embedding_flags={}, # this would be required for non-static categorical inputs dropout=self.dropout, context_size=self.hidden_size, prescalers=self.prescalers_linear, @@ -168,7 +192,7 @@ def __init__( self.decoder_vsn = _VariableSelectionNetwork( input_sizes=decoder_input_sizes, hidden_size=self.hidden_size, - input_embedding_flags={}, # this would be required for categorical inputs + input_embedding_flags={}, # this would be required for non-static categorical inputs dropout=self.dropout, context_size=self.hidden_size, prescalers=self.prescalers_linear, @@ -280,9 +304,22 @@ def static_variables(self) -> List[str]: """ List of all static variables in model """ - # TODO: (Darts: dbader) we might want to include static variables in the future? return self.variables_meta["model_config"]["static_input"] + @property + def numeric_static_variables(self) -> List[str]: + """ + List of numeric static variables in model + """ + return self.variables_meta["model_config"]["static_input_numeric"] + + @property + def categorical_static_variables(self) -> List[str]: + """ + List of categorical static variables in model + """ + return self.variables_meta["model_config"]["static_input_categorical"] + @property def encoder_variables(self) -> List[str]: """ @@ -442,9 +479,25 @@ def forward( # Embedding and variable selection if self.static_variables: - static_embedding = { - name: x_static[:, :, i] for i, name in enumerate(self.static_variables) - } + # categorical static covariate embeddings + static_embedding = self.input_embeddings( + torch.cat( + [ + x_static[:, :, idx] + for idx, name in enumerate(self.static_variables) + if name in self.categorical_static_variables + ], + dim=1, + ).int() + ) + # add numerical static covariates + static_embedding.update( + { + name: x_static[:, :, idx] + for idx, name in enumerate(self.static_variables) + if name in self.numeric_static_variables + } + ) static_embedding, static_covariate_var = self.static_covariates_vsn( static_embedding ) @@ -570,6 +623,7 @@ def __init__( feed_forward: str = "GatedResidualNetwork", dropout: float = 0.1, hidden_continuous_size: int = 8, + categorical_embedding_sizes: Optional[Dict[str, Tuple[int, int]]] = None, add_relative_index: bool = False, loss_fn: Optional[nn.Module] = None, likelihood: Optional[Likelihood] = None, @@ -609,7 +663,7 @@ def __init__( full_attention : bool If ``True``, applies multi-head attention query on past (encoder) and future (decoder) parts. Otherwise, only queries on future part. Defaults to ``False``. - feed_forward: str + feed_forward : str A feedforward network is a fully-connected layer with an activation. TFT Can be one of the glu variant's FeedForward Network (FFN)[2]. The glu variant's FeedForward Network are a series of FFNs designed to work better with Transformer based models. Defaults to ``"GatedResidualNetwork"``. @@ -622,6 +676,9 @@ def __init__( prediction time). hidden_continuous_size : int Default for hidden size for processing continuous variables + categorical_embedding_sizes : int + Embedding size for categorical static covariates. Only effective if the target series contains + categorical (non-numeric) static covariates. add_relative_index : bool Whether to add positional values to future covariates. Defaults to ``False``. This allows to use the TFTModel without having to pass future_covariates to :fun:`fit()` and @@ -783,6 +840,11 @@ def __init__( self.feed_forward = feed_forward self.dropout = dropout self.hidden_continuous_size = hidden_continuous_size + self.categorical_embedding_sizes = ( + categorical_embedding_sizes + if categorical_embedding_sizes is not None + else {} + ) self.add_relative_index = add_relative_index self.output_dim: Optional[Tuple[int, int]] = None @@ -880,25 +942,60 @@ def _create_model(self, train_sample: MixedCovariatesTrainTensorType) -> nn.Modu } reals_input = [] + categorical_input = [] time_varying_encoder_input = [] time_varying_decoder_input = [] static_input = [] + static_input_numeric = [] + static_input_categorical = [] + categorical_embedding_sizes = {} for input_var in type_names: if input_var in variables_meta["input"]: vars_meta = variables_meta["input"][input_var] - reals_input += vars_meta if input_var in [ "past_target", "past_covariate", "historic_future_covariate", ]: time_varying_encoder_input += vars_meta + reals_input += vars_meta elif input_var in ["future_covariate"]: time_varying_decoder_input += vars_meta + reals_input += vars_meta elif input_var in ["static_covariate"]: - static_input += vars_meta + static_covs = self.training_series.static_covariates + static_covs_is_real = static_covs.columns.isin( + static_covs.select_dtypes(include=np.number) + ) + cat_cols = static_covs.columns[~static_covs_is_real] + missing_embeddings = [ + col + for col in cat_cols + if col not in self.categorical_embedding_sizes + ] + raise_if( + len(missing_embeddings) > 0, + f"Missing embedding sizes for categorical static covarites: {missing_embeddings}", + logger, + ) + for idx, (static_var, col_name, is_real) in enumerate( + zip(vars_meta, static_covs.columns, static_covs_is_real) + ): + static_input.append(static_var) + if is_real: + static_input_numeric.append(static_var) + reals_input.append(static_var) + else: + static_input_categorical.append(static_var) + categorical_input.append(static_var) + categorical_embedding_sizes[ + vars_meta[idx] + ] = self.categorical_embedding_sizes[col_name] variables_meta["model_config"]["reals_input"] = list(dict.fromkeys(reals_input)) + variables_meta["model_config"]["categorical_input"] = list( + dict.fromkeys(categorical_input) + ) variables_meta["model_config"]["time_varying_encoder_input"] = list( dict.fromkeys(time_varying_encoder_input) ) @@ -908,10 +1005,19 @@ def _create_model(self, train_sample: MixedCovariatesTrainTensorType) -> nn.Modu variables_meta["model_config"]["static_input"] = list( dict.fromkeys(static_input) ) + variables_meta["model_config"]["static_input_numeric"] = list( + dict.fromkeys(static_input_numeric) + ) + variables_meta["model_config"]["static_input_categorical"] = list( + dict.fromkeys(static_input_categorical) + ) n_static_components = ( len(static_covariates) if static_covariates is not None else 0 ) + + self.categorical_embedding_sizes = categorical_embedding_sizes + return _TFTModule( output_dim=self.output_dim, variables_meta=variables_meta, @@ -923,6 +1029,7 @@ def _create_model(self, train_sample: MixedCovariatesTrainTensorType) -> nn.Modu full_attention=self.full_attention, feed_forward=self.feed_forward, hidden_continuous_size=self.hidden_continuous_size, + categorical_embedding_sizes=self.categorical_embedding_sizes, add_relative_index=self.add_relative_index, **self.pl_module_params, ) diff --git a/darts/models/forecasting/tft_submodels.py b/darts/models/forecasting/tft_submodels.py index 167c7007d0..731f9e11bb 100644 --- a/darts/models/forecasting/tft_submodels.py +++ b/darts/models/forecasting/tft_submodels.py @@ -34,6 +34,21 @@ HiddenState = Union[Tuple[torch.Tensor, torch.Tensor], torch.Tensor] +def get_embedding_size(n: int, max_size: int = 100) -> int: + """ + Determine empirically good embedding sizes (formula taken from fastai). + Args: + n (int): number of classes + max_size (int, optional): maximum embedding size. Defaults to 100. + Returns: + int: embedding size + """ + if n > 2: + return min(round(1.6 * n**0.56), max_size) + else: + return 1 + + class _TimeDistributedEmbeddingBag(nn.EmbeddingBag): def __init__(self, *args, batch_first: bool = False, **kwargs): super().__init__(*args, **kwargs) @@ -65,78 +80,54 @@ class _MultiEmbedding(nn.Module): def __init__( self, embedding_sizes: Dict[str, Tuple[int, int]], - categorical_groups: Dict[str, List[str]], - embedding_paddings: List[str], - x_categoricals: List[str], - max_embedding_size: int = None, + variable_names: List[str], ): + """Embedding layer for categorical variables including groups of categorical variables. + Enabled for static and dynamic categories (i.e. 3 dimensions for batch x time x categories). + + Parameters + ---------- + embedding_sizes + dictionary of embedding sizes, e.g. ``{'cat1': (10, 3)}`` + indicates that the first categorical variable has 10 unique values which are mapped to 3 embedding + dimensions. Use :py:func:`~pytorch_forecasting.utils.get_embedding_size` to automatically obtain + reasonable embedding sizes depending on the number of categories. + variable_names + list of categorical variable names to ensure ordered iterations. + """ super().__init__() self.embedding_sizes = embedding_sizes - self.categorical_groups = categorical_groups - self.embedding_paddings = embedding_paddings - self.max_embedding_size = max_embedding_size - self.x_categoricals = x_categoricals - - self.init_embeddings() - - def init_embeddings(self): - self.embeddings = nn.ModuleDict() - for name in self.embedding_sizes.keys(): - embedding_size = self.embedding_sizes[name][1] - if self.max_embedding_size is not None: - embedding_size = min(embedding_size, self.max_embedding_size) - # convert to list to become mutable - self.embedding_sizes[name] = list(self.embedding_sizes[name]) - self.embedding_sizes[name][1] = embedding_size - if name in self.categorical_groups: # embedding bag if related embeddings - self.embeddings[name] = _TimeDistributedEmbeddingBag( - self.embedding_sizes[name][0], - embedding_size, - mode="sum", - batch_first=True, - ) - else: - if name in self.embedding_paddings: - padding_idx = 0 - else: - padding_idx = None - self.embeddings[name] = nn.Embedding( - self.embedding_sizes[name][0], - embedding_size, - padding_idx=padding_idx, - ) - - def names(self): - return list(self.keys()) - - def items(self): - return self.embeddings.items() + self.variable_names = variable_names - def keys(self): - return self.embeddings.keys() + self.embeddings = nn.ModuleDict( + {name: nn.Embedding(*embedding_sizes[name]) for name in variable_names} + ) - def values(self): - return self.embeddings.values() + @property + def input_size(self) -> int: + return len(self.variable_names) - def __getitem__(self, name: str): - return self.embeddings[name] + @property + def output_size(self) -> Union[Dict[str, int], int]: + return {name: sizes[1] for name, sizes in self.embedding_sizes.items()} - def forward(self, x): - input_vectors = {} - for name, emb in self.embeddings.items(): - if name in self.categorical_groups: - input_vectors[name] = emb( - x[ - ..., - [ - self.x_categoricals.index(cat_name) - for cat_name in self.categorical_groups[name] - ], - ] - ) - else: - input_vectors[name] = emb(x[..., self.x_categoricals.index(name)]) - return input_vectors + def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]: + """ + Parameters + ---------- + x + input tensor of shape batch x (optional) time x categoricals in the order of ``variable_names``. + + Returns + ------- + dict + dictionary of category names to embeddings of shape batch x (optional) time x embedding_size if + ``embedding_size`` is given as dictionary. + """ + return { + name: self.embeddings[name](x[..., i]) + for i, name in enumerate(self.variable_names) + } class _TimeDistributedInterpolation(nn.Module): From 624feeee21c896632a0a60d2fe7f623b37baad6f Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sun, 26 Jun 2022 20:45:39 +0200 Subject: [PATCH 02/16] from_group_dataframe fix --- darts/models/forecasting/forecasting_model.py | 6 ++++++ darts/models/forecasting/tft_model.py | 2 +- darts/timeseries.py | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/darts/models/forecasting/forecasting_model.py b/darts/models/forecasting/forecasting_model.py index 82e33f4499..6b0732f292 100644 --- a/darts/models/forecasting/forecasting_model.py +++ b/darts/models/forecasting/forecasting_model.py @@ -104,6 +104,10 @@ def __init__(self, *args, **kwargs): # This is only used if the model has been fit on one time series. self.training_series: Optional[TimeSeries] = None + # Static covariates sample from the (first) target series used for training the model through the `fit()` + # function. + self.static_covariates: Optional[pd.DataFrame] = None + # state; whether the model has been fit (on a single time series) self._fit_called = False @@ -959,11 +963,13 @@ def fit( if isinstance(series, TimeSeries): # if only one series is provided, save it for prediction time (including covariates, if available) self.training_series = series + self.static_covariates = series.static_covariates if past_covariates is not None: self.past_covariate_series = past_covariates if future_covariates is not None: self.future_covariate_series = future_covariates else: + self.static_covariates = series[0].static_covariates if past_covariates is not None: self._expect_past_covariates = True if future_covariates is not None: diff --git a/darts/models/forecasting/tft_model.py b/darts/models/forecasting/tft_model.py index 246e12d54c..1c572507f8 100644 --- a/darts/models/forecasting/tft_model.py +++ b/darts/models/forecasting/tft_model.py @@ -963,7 +963,7 @@ def _create_model(self, train_sample: MixedCovariatesTrainTensorType) -> nn.Modu time_varying_decoder_input += vars_meta reals_input += vars_meta elif input_var in ["static_covariate"]: - static_covs = self.training_series.static_covariates + static_covs = self.static_covariates static_covs_is_real = static_covs.columns.isin( static_covs.select_dtypes(include=np.number) ) diff --git a/darts/timeseries.py b/darts/timeseries.py index f8c244d017..1a6708495c 100644 --- a/darts/timeseries.py +++ b/darts/timeseries.py @@ -765,7 +765,9 @@ def from_group_dataframe( # store static covariate Series and group DataFrame (without static cov columns) splits.append( ( - pd.DataFrame([static_cov_vals], columns=static_cov_cols), + pd.DataFrame([static_cov_vals], columns=static_cov_cols).astype( + {col: df[col].dtype for col in static_cov_cols} + ), group.drop(columns=static_cov_cols), ) ) From 34221b776b9fb1013d7043e38fed4205d6a7a6f2 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Fri, 8 Jul 2022 19:04:14 +0200 Subject: [PATCH 03/16] added static covariate transformer --- darts/dataprocessing/transformers/__init__.py | 1 + .../static_covariates_transformer.py | 249 ++++++++++++++++++ .../test_static_covariates_transformer.py | 98 +++++++ 3 files changed, 348 insertions(+) create mode 100644 darts/dataprocessing/transformers/static_covariates_transformer.py create mode 100644 darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py diff --git a/darts/dataprocessing/transformers/__init__.py b/darts/dataprocessing/transformers/__init__.py index d7f64af4fd..c59667a17b 100644 --- a/darts/dataprocessing/transformers/__init__.py +++ b/darts/dataprocessing/transformers/__init__.py @@ -15,3 +15,4 @@ TopDownReconciliator, ) from .scaler import Scaler +from .static_covariates_transformer import StaticCovariatesTransformer diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py new file mode 100644 index 0000000000..a96444ab3c --- /dev/null +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -0,0 +1,249 @@ +""" +Static Covariates Transformer +------ +""" + +from typing import Any, Iterator, List, Optional, Sequence, Tuple, Union + +import numpy as np +import pandas as pd +from sklearn.preprocessing import MinMaxScaler, OrdinalEncoder + +from darts.logging import get_logger, raise_log +from darts.timeseries import TimeSeries + +from .fittable_data_transformer import FittableDataTransformer +from .invertible_data_transformer import InvertibleDataTransformer +from .scaler import Scaler + +logger = get_logger(__name__) + + +class StaticCovariatesTransformer(InvertibleDataTransformer, FittableDataTransformer): + def __init__( + self, + scaler_numerical=None, + scaler_categorical=None, + name="StaticCovariatesTransformer", + n_jobs: int = 1, + verbose: bool = False, + ): + """Generic wrapper class for scalers/encoders/transformers of static covariates. + + The underlying `scaler_numerical` and `scaler_categorical` have to implement the ``fit()``, ``transform()`` + and ``inverse_transform()`` methods (typically from scikit-learn). + + `scaler_numerical` addresses numerical static covariate data of the underlying series. + `scaler_categorical` addresses categorical static covariate data. + + Parameters + ---------- + scaler_numerical + The scaler to transform numeric static covariate data with. It must provide ``fit()``, + ``transform()`` and ``inverse_transform()`` methods. + Default: :class:`sklearn.preprocessing.MinMaxScaler(feature_range=(0, 1))`; this will scale all + the values of a time series between 0 and 1. + scaler_categorical + The scaler to transform categorical static covariate data with. It must provide ``fit()``, + ``transform()`` and ``inverse_transform()`` methods. + Default: :class:`sklearn.preprocessing.OrdinalEncoder(feature_range=(0, 1))`; this will convert categories + into integer valued arrays where each integer stands for a specific category. + name + A specific name for the scaler + n_jobs + The number of jobs to run in parallel. Parallel jobs are created only when a ``Sequence[TimeSeries]`` is + passed as input to a method, parallelising operations regarding different ``TimeSeries``. Defaults to `1` + (sequential). Setting the parameter to `-1` means using all the available processors. + Note: for a small amount of data, the parallelisation overhead could end up increasing the total + required amount of time. + verbose + Optionally, whether to print operations progress + + Examples + -------- + >>> from darts.datasets import AirPassengersDataset + >>> from sklearn.preprocessing import MinMaxScaler, OrdicalEncoder + >>> from darts.dataprocessing.transformers import StaticCovariatesTransformer + >>> series = AirPassengersDataset().load() + >>> scaler_num = MinMaxScaler(feature_range=(-1, 1)) + >>> scaler_cat = OrdinalEncoder() + >>> transformer = StaticCovariatesTransformer(scaler_numerical=scaler_num, scaler_categorical=scaler_cat) + >>> series_transformed = transformer.fit_transform(series) + >>> print(series.static_covariates_values()) + [-1.] + >>> print(series_transformed.static_covariates_values()) + [2.] + """ + super().__init__(name=name, n_jobs=n_jobs, verbose=verbose) + + self.transformer_cont = Scaler( + scaler=scaler_numerical, name=name, n_jobs=n_jobs, verbose=verbose + ) + + if scaler_numerical is None: + self.scaler_numerical = MinMaxScaler(feature_range=(0, 1)) + if scaler_categorical is None: + self.scaler_categorical = OrdinalEncoder() + + for scaler, scaler_name in zip( + [self.scaler_numerical, self.scaler_categorical], + ["scaler_numerical", "scaler_categorical"], + ): + if ( + not callable(getattr(scaler, "fit", None)) + or not callable(getattr(scaler, "transform", None)) + or not callable(getattr(scaler, "inverse_transform", None)) + ): + raise_log( + ValueError( + f"The provided `{scaler_name}` object must have fit(), transform() and " + f"inverse_transform() methods" + ), + logger, + ) + + self._numeric_col_mask = None + + def fit( + self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs + ) -> "FittableDataTransformer": + + self._fit_called = True + + if isinstance(series, TimeSeries): + data = series.static_covariates + else: + data = pd.concat([s.static_covariates for s in series], axis=0) + + self._numeric_col_mask = data.columns.isin( + data.select_dtypes(include=np.number).columns + ) + data = data.to_numpy(copy=False) + self.scaler_numerical.fit(data[:, self._numeric_col_mask]) + self.scaler_categorical.fit(data[:, ~self._numeric_col_mask]) + return self + + def transform( + self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs + ) -> Union[TimeSeries, List[TimeSeries]]: + kwargs = {key: val for key, val in kwargs.items()} + kwargs["component_mask"] = self._numeric_col_mask + return super().transform(series, *args, **kwargs) + + def inverse_transform( + self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs + ) -> Union[TimeSeries, List[TimeSeries]]: + kwargs = {key: val for key, val in kwargs.items()} + kwargs["component_mask"] = self._numeric_col_mask + return super().inverse_transform(series, *args, **kwargs) + + @staticmethod + def ts_fit(series: TimeSeries): + raise NotImplementedError( + "StaticCovariatesTransformer does not use method `ts_fit()`" + ) + + @staticmethod + def ts_transform( + series: TimeSeries, transformer_cont, transformer_cat, **kwargs + ) -> TimeSeries: + component_mask = kwargs.get("component_mask") + assert component_mask is not None + vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( + series, component_mask=component_mask + ) + tr_out_cont = transformer_cont.transform(vals_cont) + tr_out_cat = transformer_cat.transform(vals_cat) + + transformed_vals = StaticCovariatesTransformer._reshape_out( + series, (tr_out_cont, tr_out_cat), component_mask=component_mask + ) + + return series.with_static_covariates( + pd.DataFrame( + transformed_vals, + columns=series.static_covariates.columns, + index=series.static_covariates.index, + ) + ) + + @staticmethod + def ts_inverse_transform( + series: TimeSeries, transformer_cont, transformer_cat, **kwargs + ) -> TimeSeries: + component_mask = kwargs.get("component_mask") + assert component_mask is not None + vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( + series, component_mask=component_mask + ) + tr_out_cont = transformer_cont.inverse_transform(vals_cont) + tr_out_cat = transformer_cat.inverse_transform(vals_cat) + + transformed_vals = StaticCovariatesTransformer._reshape_out( + series, (tr_out_cont, tr_out_cat), component_mask=component_mask + ) + + return series.with_static_covariates( + pd.DataFrame( + transformed_vals, + columns=series.static_covariates.columns, + index=series.static_covariates.index, + ) + ) + + def _transform_iterator( + self, series: Sequence[TimeSeries] + ) -> Iterator[Tuple[TimeSeries, Any, Any]]: + # since '_ts_fit()' returns the scaler objects, the 'fit()' call will save transformers instances into + # self.scaler_numerical and self.scaler_categorical + return zip( + series, + [self.scaler_numerical] * len(series), + [self.scaler_categorical] * len(series), + ) + + def _inverse_transform_iterator( + self, series: Sequence[TimeSeries] + ) -> Iterator[Tuple[TimeSeries, Any, Any]]: + # the same self.scaler_numerical and self.scaler_categorical will be used also for the 'ts_inverse_transform()' + return zip( + series, + [self.scaler_numerical] * len(series), + [self.scaler_categorical] * len(series), + ) + + @staticmethod + def _reshape_in( + series: TimeSeries, component_mask: Optional[np.ndarray] = None + ) -> Tuple[np.array, np.array]: + assert component_mask is not None + + # component mask points at continuous variables + vals = series.static_covariates_values(copy=False) + + # returns tuple of (continuous static covariates, categorical static covariates) + return vals[:, component_mask], vals[:, ~component_mask] + + @staticmethod + def _reshape_out( + series: TimeSeries, + vals: Tuple[np.ndarray, np.ndarray], + component_mask: Optional[np.ndarray] = None, + ) -> pd.DataFrame: + assert component_mask is not None + + vals_cont, vals_cat = vals + data = {} + idx_cont, idx_cat = 0, 0 + for col, is_numeric in zip(series.static_covariates.columns, component_mask): + if is_numeric: + data[col] = vals_cont[:, idx_cont] + idx_cont += 1 + else: + data[col] = vals_cat[:, idx_cat] + idx_cat += 1 + return pd.DataFrame( + data, + columns=series.static_covariates.columns, + index=series.static_covariates.index, + ) diff --git a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py new file mode 100644 index 0000000000..593e14c428 --- /dev/null +++ b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py @@ -0,0 +1,98 @@ +import numpy as np +import pandas as pd + +from darts import TimeSeries +from darts.dataprocessing.transformers import StaticCovariatesTransformer +from darts.tests.base_test_class import DartsBaseTestClass +from darts.utils import timeseries_generation as tg + + +class DataTransformerTestCase(DartsBaseTestClass): + series = tg.linear_timeseries(length=10) + static_covs1 = pd.DataFrame( + data={ + "cont1": [0, 1, 2], + "cat1": [1, 2, 3], + "cont2": [0.1, 0.2, 0.3], + "cat2": ["a", "b", "c"], + } + ) + static_covs1["cat1"] = static_covs1["cat1"].astype("O") + series1 = TimeSeries.from_times_and_values( + times=series.time_index, + values=np.concatenate([series.values()] * 3, axis=1), + columns=["comp1", "comp2", "comp3"], + static_covariates=static_covs1, + ) + + static_covs2 = pd.DataFrame( + data={ + "cont1": [2, 3, 4], + "cat1": [3, 4, 5], + "cont2": [0.3, 0.4, 0.5], + "cat2": ["c", "d", "e"], + } + ) + static_covs2["cat1"] = static_covs2["cat1"].astype("O") + series2 = TimeSeries.from_times_and_values( + times=series.time_index, + values=np.concatenate([series.values()] * 3, axis=1), + columns=["comp1", "comp2", "comp3"], + static_covariates=static_covs2, + ) + + def test_scaling_single_series(self): + # 3 categories for each categorical static covariate column (column idx 1 and 3) + for series in [self.series1, self.series2]: + scaler = StaticCovariatesTransformer() + series_tr = scaler.fit_transform(series) + + np.testing.assert_almost_equal( + series_tr.static_covariates_values(), + np.array( + [[0.0, 0.0, 0.0, 0.0], [0.5, 1.0, 0.5, 1.0], [1.0, 2.0, 1.0, 2.0]] + ), + ) + series_recovered = scaler.inverse_transform(series_tr) + self.assertTrue( + series.static_covariates.equals(series_recovered.static_covariates) + ) + + def test_scaling_multi_series(self): + # 5 categories in total for each categorical static covariate from multiple time series + scaler2 = StaticCovariatesTransformer() + series_tr2 = scaler2.fit_transform([self.series1, self.series2]) + + np.testing.assert_almost_equal( + series_tr2[0].static_covariates_values(), + np.array( + [[0.0, 0.0, 0.0, 0.0], [0.25, 1.0, 0.25, 1.0], [0.5, 2.0, 0.5, 2.0]] + ), + ) + series_recovered2 = scaler2.inverse_transform(series_tr2[0]) + self.assertTrue( + self.series1.static_covariates.equals(series_recovered2.static_covariates) + ) + + np.testing.assert_almost_equal( + series_tr2[1].static_covariates_values(), + np.array( + [[0.5, 2.0, 0.5, 2.0], [0.75, 3.0, 0.75, 3.0], [1.0, 4.0, 1.0, 4.0]] + ), + ) + series_recovered3 = scaler2.inverse_transform(series_tr2[1]) + self.assertTrue( + self.series2.static_covariates.equals(series_recovered3.static_covariates) + ) + + series_recovered_multi = scaler2.inverse_transform(series_tr2) + self.assertTrue( + self.series1.static_covariates.equals( + series_recovered_multi[0].static_covariates + ) + ) + self.assertTrue( + self.series2.static_covariates.equals( + series_recovered_multi[1].static_covariates + ) + ) From 6aa1635f49df805d3a7ae75254b1bee937cf46c2 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sat, 9 Jul 2022 12:21:33 +0200 Subject: [PATCH 04/16] OneHotEncoder support for StaticCovariatesTransformer --- .../static_covariates_transformer.py | 123 +++++++++++++----- .../test_static_covariates_transformer.py | 81 +++++++++--- 2 files changed, 152 insertions(+), 52 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index a96444ab3c..ed5ca38a6f 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -2,19 +2,19 @@ Static Covariates Transformer ------ """ - -from typing import Any, Iterator, List, Optional, Sequence, Tuple, Union +from collections import OrderedDict +from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union import numpy as np import pandas as pd -from sklearn.preprocessing import MinMaxScaler, OrdinalEncoder +from scipy.sparse import csr_matrix +from sklearn.preprocessing import MinMaxScaler, OneHotEncoder, OrdinalEncoder from darts.logging import get_logger, raise_log from darts.timeseries import TimeSeries from .fittable_data_transformer import FittableDataTransformer from .invertible_data_transformer import InvertibleDataTransformer -from .scaler import Scaler logger = get_logger(__name__) @@ -75,15 +75,12 @@ def __init__( [2.] """ super().__init__(name=name, n_jobs=n_jobs, verbose=verbose) - - self.transformer_cont = Scaler( - scaler=scaler_numerical, name=name, n_jobs=n_jobs, verbose=verbose + self.scaler_numerical = ( + MinMaxScaler() if scaler_numerical is None else scaler_numerical + ) + self.scaler_categorical = ( + OrdinalEncoder() if scaler_categorical is None else scaler_categorical ) - - if scaler_numerical is None: - self.scaler_numerical = MinMaxScaler(feature_range=(0, 1)) - if scaler_categorical is None: - self.scaler_categorical = OrdinalEncoder() for scaler, scaler_name in zip( [self.scaler_numerical, self.scaler_categorical], @@ -102,6 +99,8 @@ def __init__( logger, ) + # categoricals might need a mapping from input features to output (i.e. OneHotEncoding) + self._cat_feature_map = None self._numeric_col_mask = None def fit( @@ -118,9 +117,23 @@ def fit( self._numeric_col_mask = data.columns.isin( data.select_dtypes(include=np.number).columns ) + cat_cols = data.columns[~self._numeric_col_mask] + data = data.to_numpy(copy=False) self.scaler_numerical.fit(data[:, self._numeric_col_mask]) self.scaler_categorical.fit(data[:, ~self._numeric_col_mask]) + + if isinstance(self.scaler_categorical, OneHotEncoder): + self._cat_feature_map = OrderedDict( + { + col: [f"{col}_{cat}" for cat in categories] + for col, categories in zip( + cat_cols, self.scaler_categorical.categories_ + ) + } + ) + else: + self._cat_feature_map = OrderedDict({col: [col] for col in cat_cols}) return self def transform( @@ -128,13 +141,32 @@ def transform( ) -> Union[TimeSeries, List[TimeSeries]]: kwargs = {key: val for key, val in kwargs.items()} kwargs["component_mask"] = self._numeric_col_mask + kwargs["cat_feature_map"] = self._cat_feature_map return super().transform(series, *args, **kwargs) def inverse_transform( self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs ) -> Union[TimeSeries, List[TimeSeries]]: kwargs = {key: val for key, val in kwargs.items()} - kwargs["component_mask"] = self._numeric_col_mask + + component_mask = [] + cat_features = [len(vals) for vals in self._cat_feature_map.values()] + cat_idx = 0 + for col, is_numeric in zip( + series.static_covariates.columns, self._numeric_col_mask + ): + if is_numeric: + component_mask.append(True) + else: + component_mask += [False] * cat_features[cat_idx] + cat_idx += 1 + + kwargs["component_mask"] = np.array(component_mask) + kwargs["cat_feature_map"] = { + name: [col] + for col, names in self._cat_feature_map.items() + for name in names + } return super().inverse_transform(series, *args, **kwargs) @staticmethod @@ -148,48 +180,48 @@ def ts_transform( series: TimeSeries, transformer_cont, transformer_cat, **kwargs ) -> TimeSeries: component_mask = kwargs.get("component_mask") - assert component_mask is not None + cat_feature_map = kwargs.get("cat_feature_map") + vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=component_mask ) tr_out_cont = transformer_cont.transform(vals_cont) tr_out_cat = transformer_cat.transform(vals_cat) - transformed_vals = StaticCovariatesTransformer._reshape_out( - series, (tr_out_cont, tr_out_cat), component_mask=component_mask - ) + # sparse one hot encoding to dense array + if isinstance(tr_out_cat, csr_matrix): + tr_out_cat = tr_out_cat.toarray() - return series.with_static_covariates( - pd.DataFrame( - transformed_vals, - columns=series.static_covariates.columns, - index=series.static_covariates.index, - ) + transformed_df = StaticCovariatesTransformer._reshape_out( + series, + (tr_out_cont, tr_out_cat), + component_mask=component_mask, + cat_feature_map=cat_feature_map, ) + return series.with_static_covariates(transformed_df) + @staticmethod def ts_inverse_transform( series: TimeSeries, transformer_cont, transformer_cat, **kwargs ) -> TimeSeries: component_mask = kwargs.get("component_mask") - assert component_mask is not None + cat_feature_map = kwargs.get("cat_feature_map") + vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=component_mask ) tr_out_cont = transformer_cont.inverse_transform(vals_cont) tr_out_cat = transformer_cat.inverse_transform(vals_cat) - transformed_vals = StaticCovariatesTransformer._reshape_out( - series, (tr_out_cont, tr_out_cat), component_mask=component_mask + transformed_df = StaticCovariatesTransformer._reshape_out( + series, + (tr_out_cont, tr_out_cat), + component_mask=component_mask, + cat_feature_map=cat_feature_map, ) - return series.with_static_covariates( - pd.DataFrame( - transformed_vals, - columns=series.static_covariates.columns, - index=series.static_covariates.index, - ) - ) + return series.with_static_covariates(transformed_df) def _transform_iterator( self, series: Sequence[TimeSeries] @@ -229,21 +261,40 @@ def _reshape_out( series: TimeSeries, vals: Tuple[np.ndarray, np.ndarray], component_mask: Optional[np.ndarray] = None, + cat_feature_map: Optional[Dict[str, str]] = None, ) -> pd.DataFrame: assert component_mask is not None + assert cat_feature_map is not None vals_cont, vals_cat = vals + assert ( + len( + np.unique( + [name for names in cat_feature_map.values() for name in names] + ) + ) + == vals_cat.shape[1] + ) + data = {} idx_cont, idx_cat = 0, 0 + static_cov_columns = [] for col, is_numeric in zip(series.static_covariates.columns, component_mask): if is_numeric: data[col] = vals_cont[:, idx_cont] + static_cov_columns.append(col) idx_cont += 1 else: - data[col] = vals_cat[:, idx_cat] - idx_cat += 1 + # coverts one to one feature map (ordinal/label encoding) and one to multi feature (one hot encoding) + for col_name in cat_feature_map[col]: + if col_name not in static_cov_columns: + data[col_name] = vals_cat[:, idx_cat] + static_cov_columns.append(col_name) + idx_cat += 1 + else: + pass return pd.DataFrame( data, - columns=series.static_covariates.columns, + columns=static_cov_columns, index=series.static_covariates.index, ) diff --git a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py index 593e14c428..045b4d438c 100644 --- a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py +++ b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py @@ -1,5 +1,7 @@ import numpy as np import pandas as pd +import pytest +from sklearn.preprocessing import MinMaxScaler, OneHotEncoder from darts import TimeSeries from darts.dataprocessing.transformers import StaticCovariatesTransformer @@ -7,7 +9,7 @@ from darts.utils import timeseries_generation as tg -class DataTransformerTestCase(DartsBaseTestClass): +class StaticCovariatesTransformerTestCase(DartsBaseTestClass): series = tg.linear_timeseries(length=10) static_covs1 = pd.DataFrame( data={ @@ -43,25 +45,62 @@ class DataTransformerTestCase(DartsBaseTestClass): def test_scaling_single_series(self): # 3 categories for each categorical static covariate column (column idx 1 and 3) + test_values = np.array( + [[0.0, 0.0, 0.0, 0.0], [0.5, 1.0, 0.5, 1.0], [1.0, 2.0, 1.0, 2.0]] + ) for series in [self.series1, self.series2]: scaler = StaticCovariatesTransformer() - series_tr = scaler.fit_transform(series) + self.helper_test_scaling(series, scaler, test_values) - np.testing.assert_almost_equal( - series_tr.static_covariates_values(), - np.array( - [[0.0, 0.0, 0.0, 0.0], [0.5, 1.0, 0.5, 1.0], [1.0, 2.0, 1.0, 2.0]] - ), - ) - series_recovered = scaler.inverse_transform(series_tr) - self.assertTrue( - series.static_covariates.equals(series_recovered.static_covariates) + test_values = np.array( + [[-1.0, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, 1.0], [1.0, 2.0, 1.0, 2.0]] + ) + for series in [self.series1, self.series2]: + scaler = StaticCovariatesTransformer( + scaler_numerical=MinMaxScaler(feature_range=(-1, 1)) ) + self.helper_test_scaling(series, scaler, test_values) + + test_values = np.array( + [ + [0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.5, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0, 0.0], + [1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0], + ] + ) + for series in [self.series1, self.series2]: + scaler = StaticCovariatesTransformer(scaler_categorical=OneHotEncoder()) + self.helper_test_scaling(series, scaler, test_values) + + def test_custom_scaler(self): + # invalid scaler with missing inverse_transform + class InvalidScaler: + def fit(self): + pass + + def transform(self): + pass + + with pytest.raises(ValueError): + _ = StaticCovariatesTransformer(scaler_numerical=InvalidScaler()) + + with pytest.raises(ValueError): + _ = StaticCovariatesTransformer(scaler_categorical=InvalidScaler()) + + class ValidScaler(InvalidScaler): + def inverse_transform(self): + pass + + _ = StaticCovariatesTransformer(scaler_numerical=ValidScaler()) + _ = StaticCovariatesTransformer(scaler_categorical=ValidScaler()) + _ = StaticCovariatesTransformer( + scaler_numerical=ValidScaler(), scaler_categorical=ValidScaler() + ) def test_scaling_multi_series(self): # 5 categories in total for each categorical static covariate from multiple time series - scaler2 = StaticCovariatesTransformer() - series_tr2 = scaler2.fit_transform([self.series1, self.series2]) + scaler = StaticCovariatesTransformer() + series_tr2 = scaler.fit_transform([self.series1, self.series2]) np.testing.assert_almost_equal( series_tr2[0].static_covariates_values(), @@ -69,7 +108,7 @@ def test_scaling_multi_series(self): [[0.0, 0.0, 0.0, 0.0], [0.25, 1.0, 0.25, 1.0], [0.5, 2.0, 0.5, 2.0]] ), ) - series_recovered2 = scaler2.inverse_transform(series_tr2[0]) + series_recovered2 = scaler.inverse_transform(series_tr2[0]) self.assertTrue( self.series1.static_covariates.equals(series_recovered2.static_covariates) ) @@ -80,12 +119,12 @@ def test_scaling_multi_series(self): [[0.5, 2.0, 0.5, 2.0], [0.75, 3.0, 0.75, 3.0], [1.0, 4.0, 1.0, 4.0]] ), ) - series_recovered3 = scaler2.inverse_transform(series_tr2[1]) + series_recovered3 = scaler.inverse_transform(series_tr2[1]) self.assertTrue( self.series2.static_covariates.equals(series_recovered3.static_covariates) ) - series_recovered_multi = scaler2.inverse_transform(series_tr2) + series_recovered_multi = scaler.inverse_transform(series_tr2) self.assertTrue( self.series1.static_covariates.equals( series_recovered_multi[0].static_covariates @@ -96,3 +135,13 @@ def test_scaling_multi_series(self): series_recovered_multi[1].static_covariates ) ) + + def helper_test_scaling(self, series, scaler, test_values): + series_tr = scaler.fit_transform(series) + np.testing.assert_almost_equal( + series_tr.static_covariates_values(), test_values + ) + series_recovered = scaler.inverse_transform(series_tr) + self.assertTrue( + series.static_covariates.equals(series_recovered.static_covariates) + ) From 95b9b84f335c4121bb5cfbb9131b137cc50d375c Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sat, 9 Jul 2022 12:29:39 +0200 Subject: [PATCH 05/16] small fix --- .../transformers/static_covariates_transformer.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index ed5ca38a6f..5ce54511cd 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -147,14 +147,19 @@ def transform( def inverse_transform( self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs ) -> Union[TimeSeries, List[TimeSeries]]: + kwargs = {key: val for key, val in kwargs.items()} - component_mask = [] cat_features = [len(vals) for vals in self._cat_feature_map.values()] + static_covs = ( + series.static_covariates + if isinstance(series, TimeSeries) + else series[0].static_covariates + ) + + component_mask = [] cat_idx = 0 - for col, is_numeric in zip( - series.static_covariates.columns, self._numeric_col_mask - ): + for col, is_numeric in zip(static_covs.columns, self._numeric_col_mask): if is_numeric: component_mask.append(True) else: @@ -285,7 +290,7 @@ def _reshape_out( static_cov_columns.append(col) idx_cont += 1 else: - # coverts one to one feature map (ordinal/label encoding) and one to multi feature (one hot encoding) + # covers one to one feature map (ordinal/label encoding) and one to multi feature (one hot encoding) for col_name in cat_feature_map[col]: if col_name not in static_cov_columns: data[col_name] = vals_cat[:, idx_cat] From c82272f7efe52ec1776ad7880f11cfe9854aced2 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sun, 10 Jul 2022 14:16:32 +0200 Subject: [PATCH 06/16] TFTModel static covariate handling --- .../static_covariates_transformer.py | 62 ++++++++++------- darts/models/forecasting/block_rnn_model.py | 4 ++ darts/models/forecasting/nbeats.py | 4 ++ darts/models/forecasting/nhits.py | 4 ++ darts/models/forecasting/rnn_model.py | 4 ++ darts/models/forecasting/tcn_model.py | 4 ++ darts/models/forecasting/tft_model.py | 67 ++++++++++--------- .../forecasting/torch_forecasting_model.py | 27 ++++++++ darts/models/forecasting/transformer_model.py | 4 ++ 9 files changed, 123 insertions(+), 57 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index 5ce54511cd..89140f9ca1 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -120,20 +120,21 @@ def fit( cat_cols = data.columns[~self._numeric_col_mask] data = data.to_numpy(copy=False) - self.scaler_numerical.fit(data[:, self._numeric_col_mask]) - self.scaler_categorical.fit(data[:, ~self._numeric_col_mask]) - - if isinstance(self.scaler_categorical, OneHotEncoder): - self._cat_feature_map = OrderedDict( - { - col: [f"{col}_{cat}" for cat in categories] - for col, categories in zip( - cat_cols, self.scaler_categorical.categories_ - ) - } - ) - else: - self._cat_feature_map = OrderedDict({col: [col] for col in cat_cols}) + if sum(self._numeric_col_mask): + self.scaler_numerical.fit(data[:, self._numeric_col_mask]) + if sum(~self._numeric_col_mask): + self.scaler_categorical.fit(data[:, ~self._numeric_col_mask]) + if isinstance(self.scaler_categorical, OneHotEncoder): + self._cat_feature_map = OrderedDict( + { + col: [f"{col}_{cat}" for cat in categories] + for col, categories in zip( + cat_cols, self.scaler_categorical.categories_ + ) + } + ) + else: + self._cat_feature_map = OrderedDict({col: [col] for col in cat_cols}) return self def transform( @@ -167,11 +168,13 @@ def inverse_transform( cat_idx += 1 kwargs["component_mask"] = np.array(component_mask) - kwargs["cat_feature_map"] = { - name: [col] - for col, names in self._cat_feature_map.items() - for name in names - } + kwargs["cat_feature_map"] = OrderedDict( + { + name: [col] + for col, names in self._cat_feature_map.items() + for name in names + } + ) return super().inverse_transform(series, *args, **kwargs) @staticmethod @@ -190,12 +193,16 @@ def ts_transform( vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=component_mask ) - tr_out_cont = transformer_cont.transform(vals_cont) - tr_out_cat = transformer_cat.transform(vals_cat) - # sparse one hot encoding to dense array - if isinstance(tr_out_cat, csr_matrix): - tr_out_cat = tr_out_cat.toarray() + tr_out_cont, tr_out_cat = None, None + if sum(component_mask): + tr_out_cont = transformer_cont.transform(vals_cont) + if sum(~component_mask): + tr_out_cat = transformer_cat.transform(vals_cat) + + # sparse one hot encoding to dense array + if isinstance(tr_out_cat, csr_matrix): + tr_out_cat = tr_out_cat.toarray() transformed_df = StaticCovariatesTransformer._reshape_out( series, @@ -216,8 +223,11 @@ def ts_inverse_transform( vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=component_mask ) - tr_out_cont = transformer_cont.inverse_transform(vals_cont) - tr_out_cat = transformer_cat.inverse_transform(vals_cat) + tr_out_cont, tr_out_cat = None, None + if sum(component_mask): + tr_out_cont = transformer_cont.inverse_transform(vals_cont) + if sum(~component_mask): + tr_out_cat = transformer_cat.inverse_transform(vals_cat) transformed_df = StaticCovariatesTransformer._reshape_out( series, diff --git a/darts/models/forecasting/block_rnn_model.py b/darts/models/forecasting/block_rnn_model.py index 1eadf6b993..3dd8e9d606 100644 --- a/darts/models/forecasting/block_rnn_model.py +++ b/darts/models/forecasting/block_rnn_model.py @@ -325,6 +325,10 @@ def __init__( self.n_rnn_layers = n_rnn_layers self.dropout = dropout + @staticmethod + def _supports_static_covariates() -> bool: + return False + def _create_model(self, train_sample: Tuple[torch.Tensor]) -> torch.nn.Module: # samples are made of (past_target, past_covariates, future_target) input_dim = train_sample[0].shape[1] + ( diff --git a/darts/models/forecasting/nbeats.py b/darts/models/forecasting/nbeats.py index ace92aec02..189ec9c93b 100644 --- a/darts/models/forecasting/nbeats.py +++ b/darts/models/forecasting/nbeats.py @@ -762,6 +762,10 @@ def __init__( if isinstance(layer_widths, int): self.layer_widths = [layer_widths] * num_stacks + @staticmethod + def _supports_static_covariates() -> bool: + return False + def _create_model(self, train_sample: Tuple[torch.Tensor]) -> torch.nn.Module: # samples are made of (past_target, past_covariates, future_target) input_dim = train_sample[0].shape[1] + ( diff --git a/darts/models/forecasting/nhits.py b/darts/models/forecasting/nhits.py index a1e158a43c..7bd06b7a37 100644 --- a/darts/models/forecasting/nhits.py +++ b/darts/models/forecasting/nhits.py @@ -757,6 +757,10 @@ def _check_sizes(tup, name): return pooling_kernel_sizes, n_freq_downsample + @staticmethod + def _supports_static_covariates() -> bool: + return False + def _create_model(self, train_sample: Tuple[torch.Tensor]) -> torch.nn.Module: # samples are made of (past_target, past_covariates, future_target) input_dim = train_sample[0].shape[1] + ( diff --git a/darts/models/forecasting/rnn_model.py b/darts/models/forecasting/rnn_model.py index 0900224a01..2f709be84a 100644 --- a/darts/models/forecasting/rnn_model.py +++ b/darts/models/forecasting/rnn_model.py @@ -466,3 +466,7 @@ def _verify_train_dataset_type(self, train_dataset: TrainingDataset): train_dataset.ds_past.shift == 1, "RNNModel requires a shifted training dataset with shift=1.", ) + + @staticmethod + def _supports_static_covariates() -> bool: + return False diff --git a/darts/models/forecasting/tcn_model.py b/darts/models/forecasting/tcn_model.py index 834d6e7509..008805e146 100644 --- a/darts/models/forecasting/tcn_model.py +++ b/darts/models/forecasting/tcn_model.py @@ -490,3 +490,7 @@ def _build_train_dataset( shift=self.output_chunk_length, max_samples_per_ts=max_samples_per_ts, ) + + @staticmethod + def _supports_static_covariates() -> bool: + return False diff --git a/darts/models/forecasting/tft_model.py b/darts/models/forecasting/tft_model.py index 1c572507f8..9bab80f543 100644 --- a/darts/models/forecasting/tft_model.py +++ b/darts/models/forecasting/tft_model.py @@ -6,6 +6,7 @@ from typing import Dict, List, Optional, Sequence, Tuple, Union import numpy as np +import pandas as pd import torch from torch import nn from torch.nn import LSTM as _LSTM @@ -84,9 +85,12 @@ def __init__( Defaults to `GatedResidualNetwork`. hidden_continuous_size : int default for hidden size for processing continuous variables. - categorical_embedding_sizes : int - Embedding size for categorical static covariates. Only effective if the target series contains - categorical (non-numeric) static covariates. + categorical_embedding_sizes : dict + A dictionary containing embedding sizes for categorical static covariates. The keys are the column names + of the categorical static covariates. The values are tuples of integers with + `(number of unique categories, embedding size)`. For example `{"some_column": (64, 8)}`. + Note that `TorchForecastingModels` can only handle numeric data. Consider transforming/encoding your data + with `darts.dataprocessing.transformers.static_covariates_transformer.StaticCovariatesTransformer`. dropout : float Fraction of neurons affected by Dropout. add_relative_index : bool @@ -480,16 +484,19 @@ def forward( # Embedding and variable selection if self.static_variables: # categorical static covariate embeddings - static_embedding = self.input_embeddings( - torch.cat( - [ - x_static[:, :, idx] - for idx, name in enumerate(self.static_variables) - if name in self.categorical_static_variables - ], - dim=1, - ).int() - ) + if self.categorical_static_variables: + static_embedding = self.input_embeddings( + torch.cat( + [ + x_static[:, :, idx] + for idx, name in enumerate(self.static_variables) + if name in self.categorical_static_variables + ], + dim=1, + ).int() + ) + else: + static_embedding = {} # add numerical static covariates static_embedding.update( { @@ -963,26 +970,20 @@ def _create_model(self, train_sample: MixedCovariatesTrainTensorType) -> nn.Modu time_varying_decoder_input += vars_meta reals_input += vars_meta elif input_var in ["static_covariate"]: - static_covs = self.static_covariates - static_covs_is_real = static_covs.columns.isin( - static_covs.select_dtypes(include=np.number) - ) - cat_cols = static_covs.columns[~static_covs_is_real] - missing_embeddings = [ - col - for col in cat_cols - if col not in self.categorical_embedding_sizes - ] - raise_if( - len(missing_embeddings) > 0, - f"Missing embedding sizes for categorical static covarites: {missing_embeddings}", - logger, - ) - for idx, (static_var, col_name, is_real) in enumerate( - zip(vars_meta, static_covs.columns, static_covs_is_real) + if ( + self.static_covariates is None + ): # when training with fit_from_dataset + static_cols = pd.Index( + [i for i in range(static_covariates.shape[1])] + ) + else: + static_cols = self.static_covariates.columns + numeric_mask = ~static_cols.isin(self.categorical_embedding_sizes) + for idx, (static_var, col_name, is_numeric) in enumerate( + zip(vars_meta, static_cols, numeric_mask) ): static_input.append(static_var) - if is_real: + if is_numeric: static_input_numeric.append(static_var) reals_input.append(static_var) else: @@ -1083,6 +1084,10 @@ def _build_inference_dataset( output_chunk_length=self.output_chunk_length, ) + @staticmethod + def _supports_static_covariates() -> bool: + return True + def predict(self, n, *args, **kwargs): # since we have future covariates, the inference dataset for future input must be at least of length # `output_chunk_length`. If not, we would have to step back which causes past input to be shorter than diff --git a/darts/models/forecasting/torch_forecasting_model.py b/darts/models/forecasting/torch_forecasting_model.py index c81ee5d21a..f66a819fa7 100644 --- a/darts/models/forecasting/torch_forecasting_model.py +++ b/darts/models/forecasting/torch_forecasting_model.py @@ -26,6 +26,7 @@ from typing import Dict, List, Optional, Sequence, Tuple, Union import numpy as np +import pandas as pd import pytorch_lightning as pl import torch from pytorch_lightning import loggers as pl_loggers @@ -607,6 +608,30 @@ def _verify_past_future_covariates(self, past_covariates, future_covariates): """ pass + @staticmethod + @abstractmethod + def _supports_static_covariates() -> bool: + """ + Whether model supports static covariates + """ + pass + + def _verify_static_covariates(self, static_covariates: Optional[pd.DataFrame]): + """ + Verify that all static covariates are numeric. + """ + if static_covariates is not None and self._supports_static_covariates(): + numeric_mask = static_covariates.columns.isin( + static_covariates.select_dtypes(include=np.number) + ) + raise_if( + sum(~numeric_mask), + "TorchForecastingModels can only interpret numeric static covariate data. Consider " + "encoding/transforming categorical static covariates with " + "`darts.dataprocessing.transformers.static_covariates_transformer.StaticCovariatesTransformer`.", + logger, + ) + @random_method def fit( self, @@ -694,6 +719,7 @@ def fit( self._verify_past_future_covariates( past_covariates=past_covariates, future_covariates=future_covariates ) + self._verify_static_covariates(series[0].static_covariates) def wrap_fn( ts: Union[TimeSeries, Sequence[TimeSeries]] @@ -1056,6 +1082,7 @@ def predict( else future_covariates ) + self._verify_static_covariates(series[0].static_covariates) # encoders are set when calling fit(), but not when calling fit_from_dataset() if self.encoders is not None and self.encoders.encoding_available: past_covariates, future_covariates = self.encoders.encode_inference( diff --git a/darts/models/forecasting/transformer_model.py b/darts/models/forecasting/transformer_model.py index e7e9fe9352..e33768b2e2 100644 --- a/darts/models/forecasting/transformer_model.py +++ b/darts/models/forecasting/transformer_model.py @@ -454,3 +454,7 @@ def _create_model(self, train_sample: Tuple[torch.Tensor]) -> torch.nn.Module: custom_decoder=self.custom_decoder, **self.pl_module_params, ) + + @staticmethod + def _supports_static_covariates() -> bool: + return False From da1143bc50e35b390a28301a145a3f77c36d8787 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Mon, 11 Jul 2022 17:54:00 +0200 Subject: [PATCH 07/16] improved transformer with specifying which columns to transform --- .../static_covariates_transformer.py | 219 ++++++++++-------- .../test_static_covariates_transformer.py | 74 +++++- 2 files changed, 189 insertions(+), 104 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index 89140f9ca1..a620f454c9 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -22,32 +22,42 @@ class StaticCovariatesTransformer(InvertibleDataTransformer, FittableDataTransformer): def __init__( self, - scaler_numerical=None, - scaler_categorical=None, + scaler_num=None, + scaler_cat=None, + cols_num: Optional[List[str]] = None, + cols_cat: Optional[List[str]] = None, name="StaticCovariatesTransformer", n_jobs: int = 1, verbose: bool = False, ): """Generic wrapper class for scalers/encoders/transformers of static covariates. - The underlying `scaler_numerical` and `scaler_categorical` have to implement the ``fit()``, ``transform()`` + The underlying `scaler_num` and `scaler_cat` have to implement the ``fit()``, ``transform()`` and ``inverse_transform()`` methods (typically from scikit-learn). - `scaler_numerical` addresses numerical static covariate data of the underlying series. - `scaler_categorical` addresses categorical static covariate data. + `scaler_num` addresses numerical static covariate data of the underlying series. + `scaler_cat` addresses categorical static covariate data. Parameters ---------- - scaler_numerical + scaler_num The scaler to transform numeric static covariate data with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. Default: :class:`sklearn.preprocessing.MinMaxScaler(feature_range=(0, 1))`; this will scale all the values of a time series between 0 and 1. - scaler_categorical + scaler_cat The scaler to transform categorical static covariate data with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. Default: :class:`sklearn.preprocessing.OrdinalEncoder(feature_range=(0, 1))`; this will convert categories into integer valued arrays where each integer stands for a specific category. + cols_num + Optionally, a list of column names which for which to apply the numeric transformer `scaler_num`. + By default, the transformer will infer all numerical features and scale them with `scaler_num`. + If an empty list, no column will be scaled. + cols_cat + Optionally, a list of column names which for which to apply the categorical transformer `scaler_cat`. + By default, the transformer will infer all categorical features and transform them with `scaler_cat`. + If an empty list, no column will be transformed. name A specific name for the scaler n_jobs @@ -64,27 +74,26 @@ def __init__( >>> from darts.datasets import AirPassengersDataset >>> from sklearn.preprocessing import MinMaxScaler, OrdicalEncoder >>> from darts.dataprocessing.transformers import StaticCovariatesTransformer - >>> series = AirPassengersDataset().load() - >>> scaler_num = MinMaxScaler(feature_range=(-1, 1)) - >>> scaler_cat = OrdinalEncoder() - >>> transformer = StaticCovariatesTransformer(scaler_numerical=scaler_num, scaler_categorical=scaler_cat) + >>> static_covs = pd.DataFrame(data={"cont": [0, 1, 2], "cat": ["a", "b", "c"]}) + >>> series = TimeSeries.from_values( + >>> values=np.random.random((10, 3)), + >>> columns=["comp1", "comp2", "comp3"], + >>> static_covariates=static_covs, + >>> ) + >>> transformer = StaticCovariatesTransformer() >>> series_transformed = transformer.fit_transform(series) - >>> print(series.static_covariates_values()) + >>> print(series.static_covariates) [-1.] - >>> print(series_transformed.static_covariates_values()) + >>> print(series_transformed.static_covariates) [2.] """ super().__init__(name=name, n_jobs=n_jobs, verbose=verbose) - self.scaler_numerical = ( - MinMaxScaler() if scaler_numerical is None else scaler_numerical - ) - self.scaler_categorical = ( - OrdinalEncoder() if scaler_categorical is None else scaler_categorical - ) + self.scaler_num = MinMaxScaler() if scaler_num is None else scaler_num + self.scaler_cat = OrdinalEncoder() if scaler_cat is None else scaler_cat for scaler, scaler_name in zip( - [self.scaler_numerical, self.scaler_categorical], - ["scaler_numerical", "scaler_categorical"], + [self.scaler_num, self.scaler_cat], + ["scaler_num", "scaler_cat"], ): if ( not callable(getattr(scaler, "fit", None)) @@ -99,9 +108,15 @@ def __init__( logger, ) + # numeric/categorical cols will be inferred at fitting time, if user did not set them + self.cols = None + self.cols_num = cols_num + self.cols_cat = cols_cat + self.mask_num = None + self.mask_cat = None + # categoricals might need a mapping from input features to output (i.e. OneHotEncoding) - self._cat_feature_map = None - self._numeric_col_mask = None + self.col_map_cat = None def fit( self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs @@ -113,36 +128,47 @@ def fit( data = series.static_covariates else: data = pd.concat([s.static_covariates for s in series], axis=0) + self.cols = data.columns - self._numeric_col_mask = data.columns.isin( - data.select_dtypes(include=np.number).columns - ) - cat_cols = data.columns[~self._numeric_col_mask] + # get all numeric and categorical columns + mask_num = data.columns.isin(data.select_dtypes(include=np.number).columns) + mask_cat = ~mask_num + + # infer numeric and categorical columns if user didn't supply them at transformer construction + if self.cols_num is None: + self.cols_num = data.columns[mask_num] + if self.cols_cat is None: + self.cols_cat = data.columns[mask_cat] + + self.mask_num = data.columns.isin(self.cols_num) + self.mask_cat = data.columns.isin(self.cols_cat) data = data.to_numpy(copy=False) - if sum(self._numeric_col_mask): - self.scaler_numerical.fit(data[:, self._numeric_col_mask]) - if sum(~self._numeric_col_mask): - self.scaler_categorical.fit(data[:, ~self._numeric_col_mask]) - if isinstance(self.scaler_categorical, OneHotEncoder): - self._cat_feature_map = OrderedDict( + if sum(self.mask_num): + self.scaler_num.fit(data[:, self.mask_num]) + if sum(self.mask_cat): + self.scaler_cat.fit(data[:, self.mask_cat]) + if isinstance(self.scaler_cat, OneHotEncoder): + self.col_map_cat = OrderedDict( { col: [f"{col}_{cat}" for cat in categories] for col, categories in zip( - cat_cols, self.scaler_categorical.categories_ + self.cols_cat, self.scaler_cat.categories_ ) } ) else: - self._cat_feature_map = OrderedDict({col: [col] for col in cat_cols}) + self.col_map_cat = OrderedDict({col: [col] for col in self.cols_cat}) + else: + self.col_map_cat = {} return self def transform( self, series: Union[TimeSeries, Sequence[TimeSeries]], *args, **kwargs ) -> Union[TimeSeries, List[TimeSeries]]: kwargs = {key: val for key, val in kwargs.items()} - kwargs["component_mask"] = self._numeric_col_mask - kwargs["cat_feature_map"] = self._cat_feature_map + kwargs["component_mask"] = (self.mask_num, self.mask_cat) + kwargs["col_map_cat"] = self.col_map_cat return super().transform(series, *args, **kwargs) def inverse_transform( @@ -151,29 +177,27 @@ def inverse_transform( kwargs = {key: val for key, val in kwargs.items()} - cat_features = [len(vals) for vals in self._cat_feature_map.values()] - static_covs = ( - series.static_covariates - if isinstance(series, TimeSeries) - else series[0].static_covariates - ) - - component_mask = [] + cat_features = [len(vals) for vals in self.col_map_cat.values()] + component_mask_num, component_mask_cat = [], [] cat_idx = 0 - for col, is_numeric in zip(static_covs.columns, self._numeric_col_mask): - if is_numeric: - component_mask.append(True) - else: - component_mask += [False] * cat_features[cat_idx] + for col, is_num, is_cat in zip(self.cols, self.mask_num, self.mask_cat): + if is_num: + component_mask_num.append(True) + component_mask_cat.append(False) + elif is_cat: + component_mask_num += [False] * cat_features[cat_idx] + component_mask_cat += [True] * cat_features[cat_idx] cat_idx += 1 + else: # don't scale this feature/column + component_mask_num.append(False) + component_mask_cat.append(False) - kwargs["component_mask"] = np.array(component_mask) - kwargs["cat_feature_map"] = OrderedDict( - { - name: [col] - for col, names in self._cat_feature_map.items() - for name in names - } + kwargs["component_mask"] = ( + np.array(component_mask_num), + np.array(component_mask_cat), + ) + kwargs["col_map_cat"] = OrderedDict( + {name: [col] for col, names in self.col_map_cat.items() for name in names} ) return super().inverse_transform(series, *args, **kwargs) @@ -187,17 +211,17 @@ def ts_fit(series: TimeSeries): def ts_transform( series: TimeSeries, transformer_cont, transformer_cat, **kwargs ) -> TimeSeries: - component_mask = kwargs.get("component_mask") - cat_feature_map = kwargs.get("cat_feature_map") + component_mask_num, component_mask_cat = kwargs.get("component_mask") + col_map_cat = kwargs.get("col_map_cat") vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( - series, component_mask=component_mask + series, component_mask=(component_mask_num, component_mask_cat) ) tr_out_cont, tr_out_cat = None, None - if sum(component_mask): + if sum(component_mask_num): tr_out_cont = transformer_cont.transform(vals_cont) - if sum(~component_mask): + if sum(component_mask_cat): tr_out_cat = transformer_cat.transform(vals_cat) # sparse one hot encoding to dense array @@ -207,8 +231,8 @@ def ts_transform( transformed_df = StaticCovariatesTransformer._reshape_out( series, (tr_out_cont, tr_out_cat), - component_mask=component_mask, - cat_feature_map=cat_feature_map, + component_mask=(component_mask_num, component_mask_cat), + col_map_cat=col_map_cat, ) return series.with_static_covariates(transformed_df) @@ -217,23 +241,23 @@ def ts_transform( def ts_inverse_transform( series: TimeSeries, transformer_cont, transformer_cat, **kwargs ) -> TimeSeries: - component_mask = kwargs.get("component_mask") - cat_feature_map = kwargs.get("cat_feature_map") + component_mask_num, component_mask_cat = kwargs.get("component_mask") + col_map_cat = kwargs.get("col_map_cat") vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( - series, component_mask=component_mask + series, component_mask=(component_mask_num, component_mask_cat) ) tr_out_cont, tr_out_cat = None, None - if sum(component_mask): + if sum(component_mask_num): tr_out_cont = transformer_cont.inverse_transform(vals_cont) - if sum(~component_mask): + if sum(component_mask_cat): tr_out_cat = transformer_cat.inverse_transform(vals_cat) transformed_df = StaticCovariatesTransformer._reshape_out( series, (tr_out_cont, tr_out_cat), - component_mask=component_mask, - cat_feature_map=cat_feature_map, + component_mask=(component_mask_num, component_mask_cat), + col_map_cat=col_map_cat, ) return series.with_static_covariates(transformed_df) @@ -242,72 +266,81 @@ def _transform_iterator( self, series: Sequence[TimeSeries] ) -> Iterator[Tuple[TimeSeries, Any, Any]]: # since '_ts_fit()' returns the scaler objects, the 'fit()' call will save transformers instances into - # self.scaler_numerical and self.scaler_categorical + # self.scaler_num and self.scaler_cat return zip( series, - [self.scaler_numerical] * len(series), - [self.scaler_categorical] * len(series), + [self.scaler_num] * len(series), + [self.scaler_cat] * len(series), ) def _inverse_transform_iterator( self, series: Sequence[TimeSeries] ) -> Iterator[Tuple[TimeSeries, Any, Any]]: - # the same self.scaler_numerical and self.scaler_categorical will be used also for the 'ts_inverse_transform()' + # the same self.scaler_num and self.scaler_cat will be used also for the 'ts_inverse_transform()' return zip( series, - [self.scaler_numerical] * len(series), - [self.scaler_categorical] * len(series), + [self.scaler_num] * len(series), + [self.scaler_cat] * len(series), ) @staticmethod def _reshape_in( - series: TimeSeries, component_mask: Optional[np.ndarray] = None + series: TimeSeries, + component_mask: Optional[Tuple[np.ndarray, np.ndarray]] = None, ) -> Tuple[np.array, np.array]: assert component_mask is not None + component_mask_num, component_mask_cat = component_mask # component mask points at continuous variables vals = series.static_covariates_values(copy=False) # returns tuple of (continuous static covariates, categorical static covariates) - return vals[:, component_mask], vals[:, ~component_mask] + return vals[:, component_mask_num], vals[:, component_mask_cat] @staticmethod def _reshape_out( series: TimeSeries, vals: Tuple[np.ndarray, np.ndarray], - component_mask: Optional[np.ndarray] = None, - cat_feature_map: Optional[Dict[str, str]] = None, + component_mask: Optional[Tuple[np.ndarray, np.ndarray]] = None, + col_map_cat: Optional[Dict[str, str]] = None, ) -> pd.DataFrame: assert component_mask is not None - assert cat_feature_map is not None + assert col_map_cat is not None + component_mask_num, component_mask_cat = component_mask vals_cont, vals_cat = vals - assert ( - len( - np.unique( - [name for names in cat_feature_map.values() for name in names] - ) - ) - == vals_cat.shape[1] + + n_cat_cols = len( + np.unique([name for names in col_map_cat.values() for name in names]) ) + if vals_cat is None: + assert n_cat_cols == 0 + else: + assert n_cat_cols == vals_cat.shape[1] data = {} idx_cont, idx_cat = 0, 0 static_cov_columns = [] - for col, is_numeric in zip(series.static_covariates.columns, component_mask): - if is_numeric: + for col, is_num, is_cat in zip( + series.static_covariates.columns, component_mask_num, component_mask_cat + ): + if is_num: # numeric scaled column data[col] = vals_cont[:, idx_cont] static_cov_columns.append(col) idx_cont += 1 - else: + elif is_cat: # categorical transformed column # covers one to one feature map (ordinal/label encoding) and one to multi feature (one hot encoding) - for col_name in cat_feature_map[col]: + for col_name in col_map_cat[col]: if col_name not in static_cov_columns: data[col_name] = vals_cat[:, idx_cat] static_cov_columns.append(col_name) idx_cat += 1 else: pass + else: # is_num and is_cat are False -> feature not part of transformer, use original values + data[col] = series.static_covariates[col] + static_cov_columns.append(col) + return pd.DataFrame( data, columns=static_cov_columns, diff --git a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py index 045b4d438c..36a62bd7b5 100644 --- a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py +++ b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py @@ -18,8 +18,7 @@ class StaticCovariatesTransformerTestCase(DartsBaseTestClass): "cont2": [0.1, 0.2, 0.3], "cat2": ["a", "b", "c"], } - ) - static_covs1["cat1"] = static_covs1["cat1"].astype("O") + ).astype(dtype={"cat1": "O", "cat2": "O"}) series1 = TimeSeries.from_times_and_values( times=series.time_index, values=np.concatenate([series.values()] * 3, axis=1), @@ -57,7 +56,7 @@ def test_scaling_single_series(self): ) for series in [self.series1, self.series2]: scaler = StaticCovariatesTransformer( - scaler_numerical=MinMaxScaler(feature_range=(-1, 1)) + scaler_num=MinMaxScaler(feature_range=(-1, 1)) ) self.helper_test_scaling(series, scaler, test_values) @@ -69,9 +68,55 @@ def test_scaling_single_series(self): ] ) for series in [self.series1, self.series2]: - scaler = StaticCovariatesTransformer(scaler_categorical=OneHotEncoder()) + scaler = StaticCovariatesTransformer(scaler_cat=OneHotEncoder()) self.helper_test_scaling(series, scaler, test_values) + def test_single_type_scaler(self): + scaler_cont = StaticCovariatesTransformer() + series_cont = self.series1.with_static_covariates( + self.series1.static_covariates[["cont1", "cont2"]] + ) + test_cont = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]]) + self.helper_test_scaling(series_cont, scaler_cont, test_cont) + + scaler_cat = StaticCovariatesTransformer() + series_cat = self.series1.with_static_covariates( + self.series1.static_covariates[["cat1", "cat2"]] + ) + test_cat = np.array([[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]]) + self.helper_test_scaling(series_cat, scaler_cat, test_cat) + + def test_selected_columns(self): + test_cont = ( + pd.DataFrame( + [[0.0, 1, 0.0, "a"], [0.5, 2, 0.5, "b"], [1.0, 3, 1.0, "c"]], + ) + .astype(dtype={1: "O", 3: "O"}) + .values + ) + scaler_cont2 = StaticCovariatesTransformer( + cols_num=["cont1", "cont2"], cols_cat=[] + ) + self.helper_test_scaling(self.series1, scaler_cont2, test_cont) + + test_contcat = ( + pd.DataFrame( + [[0.0, 1, 0.0, 0.0], [1.0, 2, 0.5, 1.0], [2.0, 3, 1.0, 2.0]], + ) + .astype(dtype={1: "O"}) + .values + ) + scaler_contcat = StaticCovariatesTransformer( + cols_num=["cont2"], cols_cat=["cat2"] + ) + self.helper_test_scaling(self.series1, scaler_contcat, test_contcat) + + test_cat = pd.DataFrame( + [[0.0, 0.0, 0.1, 0.0], [1.0, 1.0, 0.2, 1], [2.0, 2.0, 0.3, 2.0]], + ).values + scaler_cat = StaticCovariatesTransformer(cols_num=[], cols_cat=["cat1", "cat2"]) + self.helper_test_scaling(self.series1, scaler_cat, test_cat) + def test_custom_scaler(self): # invalid scaler with missing inverse_transform class InvalidScaler: @@ -82,19 +127,19 @@ def transform(self): pass with pytest.raises(ValueError): - _ = StaticCovariatesTransformer(scaler_numerical=InvalidScaler()) + _ = StaticCovariatesTransformer(scaler_num=InvalidScaler()) with pytest.raises(ValueError): - _ = StaticCovariatesTransformer(scaler_categorical=InvalidScaler()) + _ = StaticCovariatesTransformer(scaler_cat=InvalidScaler()) class ValidScaler(InvalidScaler): def inverse_transform(self): pass - _ = StaticCovariatesTransformer(scaler_numerical=ValidScaler()) - _ = StaticCovariatesTransformer(scaler_categorical=ValidScaler()) + _ = StaticCovariatesTransformer(scaler_num=ValidScaler()) + _ = StaticCovariatesTransformer(scaler_cat=ValidScaler()) _ = StaticCovariatesTransformer( - scaler_numerical=ValidScaler(), scaler_categorical=ValidScaler() + scaler_num=ValidScaler(), scaler_cat=ValidScaler() ) def test_scaling_multi_series(self): @@ -138,9 +183,16 @@ def test_scaling_multi_series(self): def helper_test_scaling(self, series, scaler, test_values): series_tr = scaler.fit_transform(series) - np.testing.assert_almost_equal( - series_tr.static_covariates_values(), test_values + assert all( + [ + a == b + for a, b in zip( + series_tr.static_covariates_values().flatten(), + test_values.flatten(), + ) + ] ) + series_recovered = scaler.inverse_transform(series_tr) self.assertTrue( series.static_covariates.equals(series_recovered.static_covariates) From a2744382cecf4934a55edae6c60f660a3f30bfda Mon Sep 17 00:00:00 2001 From: dennisbader Date: Tue, 12 Jul 2022 10:49:13 +0200 Subject: [PATCH 08/16] docs improvements --- .../static_covariates_transformer.py | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index a620f454c9..439f1aee9a 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -32,11 +32,15 @@ def __init__( ): """Generic wrapper class for scalers/encoders/transformers of static covariates. - The underlying `scaler_num` and `scaler_cat` have to implement the ``fit()``, ``transform()`` + The underlying ``scaler_num`` and ``scaler_cat`` have to implement the ``fit()``, ``transform()`` and ``inverse_transform()`` methods (typically from scikit-learn). - `scaler_num` addresses numerical static covariate data of the underlying series. - `scaler_cat` addresses categorical static covariate data. + By default, numerical and categorical columns/features are inferred and allocated to ``scaler_num`` and + ``scaler_cat``, respectively. Alternatively, specify which columns to scale/transform with ``cols_num`` and + ``cols_cat``. + + Both ``scaler_num`` and ``scaler_cat`` are fit globally on static covariate data from all series passed + to ``StaticCovariatesTransformer.fit()`` Parameters ---------- @@ -48,7 +52,7 @@ def __init__( scaler_cat The scaler to transform categorical static covariate data with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. - Default: :class:`sklearn.preprocessing.OrdinalEncoder(feature_range=(0, 1))`; this will convert categories + Default: :class:`sklearn.preprocessing.OrdinalEncoder()`; this will convert categories into integer valued arrays where each integer stands for a specific category. cols_num Optionally, a list of column names which for which to apply the numeric transformer `scaler_num`. @@ -71,10 +75,11 @@ def __init__( Examples -------- - >>> from darts.datasets import AirPassengersDataset - >>> from sklearn.preprocessing import MinMaxScaler, OrdicalEncoder + >>> import numpy as np + >>> import pandas as pd + >>> from darts import TimeSeries >>> from darts.dataprocessing.transformers import StaticCovariatesTransformer - >>> static_covs = pd.DataFrame(data={"cont": [0, 1, 2], "cat": ["a", "b", "c"]}) + >>> static_covs = pd.DataFrame(data={"cont": [0, 2, 1], "cat": ["a", "c", "b"]}) >>> series = TimeSeries.from_values( >>> values=np.random.random((10, 3)), >>> columns=["comp1", "comp2", "comp3"], @@ -83,9 +88,17 @@ def __init__( >>> transformer = StaticCovariatesTransformer() >>> series_transformed = transformer.fit_transform(series) >>> print(series.static_covariates) - [-1.] + static_covariates cont cat + component + comp1 0.0 a + comp2 2.0 c + comp3 1.0 b >>> print(series_transformed.static_covariates) - [2.] + static_covariates cont cat + component + comp1 0.0 0.0 + comp2 1.0 2.0 + comp3 0.5 1.0 """ super().__init__(name=name, n_jobs=n_jobs, verbose=verbose) self.scaler_num = MinMaxScaler() if scaler_num is None else scaler_num From aef7e09c9881f159295c953070696b4a9c523c1f Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sun, 17 Jul 2022 10:35:46 +0200 Subject: [PATCH 09/16] docstring improvement for StaticCvoariatesTransformer --- .../static_covariates_transformer.py | 85 ++++++++++--------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index 439f1aee9a..3216c2ba2c 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -40,30 +40,30 @@ def __init__( ``cols_cat``. Both ``scaler_num`` and ``scaler_cat`` are fit globally on static covariate data from all series passed - to ``StaticCovariatesTransformer.fit()`` + to :class:`StaticCovariatesTransformer.fit()` Parameters ---------- scaler_num - The scaler to transform numeric static covariate data with. It must provide ``fit()``, + The scaler to transform numeric static covariate columns with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. Default: :class:`sklearn.preprocessing.MinMaxScaler(feature_range=(0, 1))`; this will scale all - the values of a time series between 0 and 1. + values between 0 and 1. scaler_cat - The scaler to transform categorical static covariate data with. It must provide ``fit()``, + The encoder to transform categorical static covariate columns with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. Default: :class:`sklearn.preprocessing.OrdinalEncoder()`; this will convert categories into integer valued arrays where each integer stands for a specific category. cols_num - Optionally, a list of column names which for which to apply the numeric transformer `scaler_num`. + Optionally, a list of column names for which to apply the numeric transformer ``scaler_num``. By default, the transformer will infer all numerical features and scale them with `scaler_num`. If an empty list, no column will be scaled. cols_cat - Optionally, a list of column names which for which to apply the categorical transformer `scaler_cat`. + Optionally, a list of column names for which to apply the categorical transformer `scaler_cat`. By default, the transformer will infer all categorical features and transform them with `scaler_cat`. If an empty list, no column will be transformed. name - A specific name for the scaler + A specific name for the :class:`StaticCovariatesTransformer`. n_jobs The number of jobs to run in parallel. Parallel jobs are created only when a ``Sequence[TimeSeries]`` is passed as input to a method, parallelising operations regarding different ``TimeSeries``. Defaults to `1` @@ -79,7 +79,7 @@ def __init__( >>> import pandas as pd >>> from darts import TimeSeries >>> from darts.dataprocessing.transformers import StaticCovariatesTransformer - >>> static_covs = pd.DataFrame(data={"cont": [0, 2, 1], "cat": ["a", "c", "b"]}) + >>> static_covs = pd.DataFrame(data={"num": [0, 2, 1], "cat": ["a", "c", "b"]}) >>> series = TimeSeries.from_values( >>> values=np.random.random((10, 3)), >>> columns=["comp1", "comp2", "comp3"], @@ -88,13 +88,13 @@ def __init__( >>> transformer = StaticCovariatesTransformer() >>> series_transformed = transformer.fit_transform(series) >>> print(series.static_covariates) - static_covariates cont cat + static_covariates num cat component comp1 0.0 a comp2 2.0 c comp3 1.0 b >>> print(series_transformed.static_covariates) - static_covariates cont cat + static_covariates num cat component comp1 0.0 0.0 comp2 1.0 2.0 @@ -123,10 +123,8 @@ def __init__( # numeric/categorical cols will be inferred at fitting time, if user did not set them self.cols = None - self.cols_num = cols_num - self.cols_cat = cols_cat - self.mask_num = None - self.mask_cat = None + self.cols_num, self.cols_cat = cols_num, cols_cat + self.mask_num, self.mask_cat = None, None # categoricals might need a mapping from input features to output (i.e. OneHotEncoding) self.col_map_cat = None @@ -162,6 +160,7 @@ def fit( if sum(self.mask_cat): self.scaler_cat.fit(data[:, self.mask_cat]) if isinstance(self.scaler_cat, OneHotEncoder): + # OneHotEncoder will generate more features, create a 1-many column map for that self.col_map_cat = OrderedDict( { col: [f"{col}_{cat}" for cat in categories] @@ -171,6 +170,7 @@ def fit( } ) else: + # other encoders will have a 1-1 column map self.col_map_cat = OrderedDict({col: [col] for col in self.cols_cat}) else: self.col_map_cat = {} @@ -190,6 +190,7 @@ def inverse_transform( kwargs = {key: val for key, val in kwargs.items()} + # check how many categorical features were generated per categorical column after transforming the data cat_features = [len(vals) for vals in self.col_map_cat.values()] component_mask_num, component_mask_cat = [], [] cat_idx = 0 @@ -198,6 +199,7 @@ def inverse_transform( component_mask_num.append(True) component_mask_cat.append(False) elif is_cat: + # some categorical encoders (OneHotEncoder) generate more features and we need to keep track of that component_mask_num += [False] * cat_features[cat_idx] component_mask_cat += [True] * cat_features[cat_idx] cat_idx += 1 @@ -221,19 +223,18 @@ def ts_fit(series: TimeSeries): ) @staticmethod - def ts_transform( - series: TimeSeries, transformer_cont, transformer_cat, **kwargs - ) -> TimeSeries: + def ts_transform(series: TimeSeries, *args, **kwargs) -> TimeSeries: + transformer_num, transformer_cat = args component_mask_num, component_mask_cat = kwargs.get("component_mask") col_map_cat = kwargs.get("col_map_cat") - vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( + vals_num, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=(component_mask_num, component_mask_cat) ) - tr_out_cont, tr_out_cat = None, None + tr_out_num, tr_out_cat = None, None if sum(component_mask_num): - tr_out_cont = transformer_cont.transform(vals_cont) + tr_out_num = transformer_num.transform(vals_num) if sum(component_mask_cat): tr_out_cat = transformer_cat.transform(vals_cat) @@ -243,7 +244,7 @@ def ts_transform( transformed_df = StaticCovariatesTransformer._reshape_out( series, - (tr_out_cont, tr_out_cat), + (tr_out_num, tr_out_cat), component_mask=(component_mask_num, component_mask_cat), col_map_cat=col_map_cat, ) @@ -251,24 +252,23 @@ def ts_transform( return series.with_static_covariates(transformed_df) @staticmethod - def ts_inverse_transform( - series: TimeSeries, transformer_cont, transformer_cat, **kwargs - ) -> TimeSeries: + def ts_inverse_transform(series: TimeSeries, *args, **kwargs) -> TimeSeries: + transformer_num, transformer_cat = args component_mask_num, component_mask_cat = kwargs.get("component_mask") col_map_cat = kwargs.get("col_map_cat") - vals_cont, vals_cat = StaticCovariatesTransformer._reshape_in( + vals_num, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=(component_mask_num, component_mask_cat) ) - tr_out_cont, tr_out_cat = None, None + tr_out_num, tr_out_cat = None, None if sum(component_mask_num): - tr_out_cont = transformer_cont.inverse_transform(vals_cont) + tr_out_num = transformer_num.inverse_transform(vals_num) if sum(component_mask_cat): tr_out_cat = transformer_cat.inverse_transform(vals_cat) transformed_df = StaticCovariatesTransformer._reshape_out( series, - (tr_out_cont, tr_out_cat), + (tr_out_num, tr_out_cat), component_mask=(component_mask_num, component_mask_cat), col_map_cat=col_map_cat, ) @@ -278,8 +278,7 @@ def ts_inverse_transform( def _transform_iterator( self, series: Sequence[TimeSeries] ) -> Iterator[Tuple[TimeSeries, Any, Any]]: - # since '_ts_fit()' returns the scaler objects, the 'fit()' call will save transformers instances into - # self.scaler_num and self.scaler_cat + # use numerical and categorical transformers for 'ts_transform()' return zip( series, [self.scaler_num] * len(series), @@ -289,7 +288,7 @@ def _transform_iterator( def _inverse_transform_iterator( self, series: Sequence[TimeSeries] ) -> Iterator[Tuple[TimeSeries, Any, Any]]: - # the same self.scaler_num and self.scaler_cat will be used also for the 'ts_inverse_transform()' + # use numerical and categorical transformers for 'ts_inverse_transform()' return zip( series, [self.scaler_num] * len(series), @@ -301,13 +300,12 @@ def _reshape_in( series: TimeSeries, component_mask: Optional[Tuple[np.ndarray, np.ndarray]] = None, ) -> Tuple[np.array, np.array]: + # we expect component mask to be (numeric component mask, categorical component mask) assert component_mask is not None component_mask_num, component_mask_cat = component_mask - # component mask points at continuous variables + # returns tuple of (numeric static covariates, categorical static covariates) vals = series.static_covariates_values(copy=False) - - # returns tuple of (continuous static covariates, categorical static covariates) return vals[:, component_mask_num], vals[:, component_mask_cat] @staticmethod @@ -317,30 +315,36 @@ def _reshape_out( component_mask: Optional[Tuple[np.ndarray, np.ndarray]] = None, col_map_cat: Optional[Dict[str, str]] = None, ) -> pd.DataFrame: + # we expect component mask to be (numeric component mask, categorical component mask) assert component_mask is not None + component_mask_num, component_mask_cat = component_mask + + # `col_map_cat` contains information to which features the original categorical feature were mapped + # (i.e. 1-1 mapping for OrdinalEncoder, or 1-many mapping for OneHotEncoder) assert col_map_cat is not None - component_mask_num, component_mask_cat = component_mask - vals_cont, vals_cat = vals + vals_num, vals_cat = vals + # infer the number of categorical output features n_cat_cols = len( np.unique([name for names in col_map_cat.values() for name in names]) ) + # quick check if everything is in order if vals_cat is None: assert n_cat_cols == 0 else: assert n_cat_cols == vals_cat.shape[1] data = {} - idx_cont, idx_cat = 0, 0 + idx_num, idx_cat = 0, 0 static_cov_columns = [] for col, is_num, is_cat in zip( series.static_covariates.columns, component_mask_num, component_mask_cat ): if is_num: # numeric scaled column - data[col] = vals_cont[:, idx_cont] + data[col] = vals_num[:, idx_num] static_cov_columns.append(col) - idx_cont += 1 + idx_num += 1 elif is_cat: # categorical transformed column # covers one to one feature map (ordinal/label encoding) and one to multi feature (one hot encoding) for col_name in col_map_cat[col]: @@ -350,10 +354,11 @@ def _reshape_out( idx_cat += 1 else: pass - else: # is_num and is_cat are False -> feature not part of transformer, use original values + else: # is_num and is_cat are False -> feature is not part of transformer, use original values data[col] = series.static_covariates[col] static_cov_columns.append(col) + # returns a pandas DataFrame of static covariates to be added to the series return pd.DataFrame( data, columns=static_cov_columns, From 91770f0eab748bd679da9885b6b09bd9c50e1b84 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sun, 17 Jul 2022 13:21:17 +0200 Subject: [PATCH 10/16] added static covariates notebook example --- .github/workflows/merge.yml | 2 +- docs/source/examples.rst | 11 + docs/userguide/covariates.md | 70 +- examples/14-transfer-learning.ipynb | 2 +- examples/15-static-covariates.ipynb | 997 ++++++++++++++++++++++++++++ 5 files changed, 1053 insertions(+), 29 deletions(-) create mode 100644 examples/15-static-covariates.ipynb diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index cbedaf58f6..86aa674b15 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example-name: [00-quickstart.ipynb, 01-multi-time-series-and-covariates.ipynb, 02-data-processing.ipynb, 03-FFT-examples.ipynb, 04-RNN-examples.ipynb, 05-TCN-examples.ipynb, 06-Transformer-examples.ipynb, 07-NBEATS-examples.ipynb, 08-DeepAR-examples.ipynb, 09-DeepTCN-examples.ipynb, 10-Kalman-filter-examples.ipynb, 11-GP-filter-examples.ipynb, 12-Dynamic-Time-Warping-example.ipynb, 13-TFT-examples.ipynb] + example-name: [00-quickstart.ipynb, 01-multi-time-series-and-covariates.ipynb, 02-data-processing.ipynb, 03-FFT-examples.ipynb, 04-RNN-examples.ipynb, 05-TCN-examples.ipynb, 06-Transformer-examples.ipynb, 07-NBEATS-examples.ipynb, 08-DeepAR-examples.ipynb, 09-DeepTCN-examples.ipynb, 10-Kalman-filter-examples.ipynb, 11-GP-filter-examples.ipynb, 12-Dynamic-Time-Warping-example.ipynb, 13-TFT-examples.ipynb, 15-static-covariates.ipynb] steps: - name: "1. Clone repository" uses: actions/checkout@v2 diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 8228854a21..8c279ae2a6 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -29,6 +29,17 @@ Data processing example notebook: examples/02-data-processing.ipynb +Static Covariates +================= + +Static covariates example notebook: + +.. toctree:: + :maxdepth: 1 + + examples/15-static-covariates.ipynb + + Transfer Learning Tutorial ========================== diff --git a/docs/userguide/covariates.md b/docs/userguide/covariates.md index db770b89a2..7b8dd5055e 100644 --- a/docs/userguide/covariates.md +++ b/docs/userguide/covariates.md @@ -1,5 +1,6 @@ # Covariates -This section was written for darts version 0.15.0 and later. +Sections about past and future covariates were written for darts version 0.15.0 and later. +Sections about static covariates were written for darts version 0.20.0 and later. ## Summary - TL;DR In Darts, **covariates** refer to external data that can be used as inputs to models to help improve forecasts. @@ -8,7 +9,7 @@ covariates themselves are not predicted. We distinguish three kinds of covariate * **past covariates** are (by definition) covariates known only into the past (e.g. measurements) * **future covariates** are (by definition) covariates known into the future (e.g., weather forecasts) -* **static covariates** are (by definition) covariates constant over time. They are not yet supported in Darts, but we are working on it! +* **static covariates** are (by definition) covariates constant over time (e.g., product IDs). Check out our static covariates example notebook [here](https://unit8co.github.io/darts/examples/15-static-covariates.html) for more information. Models in Darts accept `past_covariates` and/or `future_covariates` in their `fit()` and `predict()` methods, depending on their capabilities (some models accept no covariates at all). Both target and covariates must be a `TimeSeries` object. The models will raise an error if covariates were used that are not supported. ```python @@ -26,6 +27,7 @@ model.predict(n=12, past_covariates=past_covariates_pred, future_covariates=future_covariates_pred) ``` +Different to past and future covariates, static covariates must be embedded in the **target** series. Because of that, working with static covariates follows a different methodology. You can check out how to use static covariates in [this example](https://unit8co.github.io/darts/examples/15-static-covariates.html). If you have several covariate variables that you want to use as past (or future) covariates, you have to `stack()` all of them into a single `past_covariates` (or `future_covariates`) object. @@ -71,22 +73,35 @@ of the past covariates, and using auto-regression on the target series. If you w ## Introduction - What are covariates (in Darts)? -Covariates provide additional information/context that can be useful to improve the prediction of the `target` series. The `target` series is the variable we wish to predict the future for. We do not predict the covariates themselves, only use them for prediction of the `target`. +Past, future and static covariates provide additional information/context that can be useful to improve the prediction of the `target` series. The `target` series is the variable we wish to predict the future for. We do not predict the covariates themselves, only use them for prediction of the `target`. -Covariates can hold information about the past (upto and including present time) or future. This is always relative to the prediction point (in time) after which we want to forecast the future. -In Darts, we refer to these two types as `past_covariates` and `future_covariates`. Darts' forecasting models have different support modes for `*_covariates`. Some do not support covariates at all, others support either past or future covariates and some support both (more on that in [this subsection](#forecasting-model-covariate-support)). +Past and future covariates hold information about the past (upto and including present time) or future. This is always relative to the prediction point (in time) after which we want to forecast the future. +In Darts, we refer to these two types as `past_covariates` and `future_covariates`. -Let's have a look at some examples of past and future covariates: +Static covariates hold time independent (constant / static) information about the `target` series. We refer to them as `static_covariates`. They must be embedded in the `target` series. Working with static covariates follows a slightly different approach than with past or future covariates. Check out our [notebook on static covariates](https://unit8co.github.io/darts/examples/15-static-covariates.html) to learn more. + +Darts' forecasting models have different support modes for `*_covariates`. Some do not support covariates at all, others support only past or future covariates and some even support all three (more on that in [this subsection](#forecasting-model-covariate-support)). + +Let's have a look at some examples of past, future, and static covariates: - `past_covariates`: typically measurements (past data) or temporal attributes - daily average **measured** temperatures (known only in the past) - day of week, month, year, ... - `future_covariates`: typically forecasts (future known data) or temporal attributes - daily average **forecasted** temperatures (known in the future) - day of week, month, year, ... +- `static_covariates`: time independent/constant/static `target` characterstics + - categorical: + - location of `target` (country, city, .. name) + - `target` identifier: (product ID, store ID, ...) + - numerical: + - population of `target`'s country/market area (assuming it stays constant over the forecasting horizon) + - average temperature of `target`'s region (assuming it stays constant over the forecasting horizon) + Temporal attributes are powerful because they are known in advance and can help models capture trends and / or seasonal patterns of the `target` series. +Static attributes are powerful when working with multiple `targets`. The time independent information can help models identify the nature/environment of the underlying series and improve forecasts across different `targets`. -Here's a simple rule-of-thumb to know if your series are past or future covariates: +In this guide we'll focus on past and future covariates. Here's a simple rule-of-thumb to know if your series are **past** or **future covariates**: *If the values are known in advance, they are future covariates (or can be used as past covariates). If they are not, they **must** be past covariates.* @@ -96,7 +111,7 @@ Side note: if you don't have future values (e.g. of measured temperatures), noth ## Forecasting Model Covariate Support -Darts' forecasting models accept optional `past_covariates` and / or `future_covariates` in their `fit()` and `predict()` methods, depending on their capabilities. Table 1 shows the supported covariate types for each model. The models will raise an error if covariates were used that are not supported. +Darts' forecasting models accept optional `past_covariates` and / or `future_covariates` in their `fit()` and `predict()` methods (and `static_covariates` embedded in the `target` series), depending on their capabilities. Table 1 shows the supported covariate types for each model. The models will raise an error if covariates were used that are not supported. ### Local Forecasting Models (LFMs): LFMs are models that can be trained on a single target series only. In Darts most models in this category tend to be simpler statistical models (such as ETS or ARIMA). LFMs accept only a single `target` (and covariate) time series and usually train on the entire series you supplied when calling `fit()` at once. They can also predict in one go for any number of predictions `n` after the end of the training series. @@ -106,24 +121,24 @@ GFMs are broadly speaking "machine learning based" models, which denote PyTorch- ---- -Model | Past Covariates | Future Covariates ---- | :---: | :---: -**Local Forecasting Models (LFMs)** | | -`ExponentialSmoothing` | | -`Theta` and `FourTheta` | | -`FFT` | | -`ARIMA` | | ✅ -`VARIMA` | | ✅ -`AutoARIMA` | | ✅ -`Prophet` | | ✅ -**Global Forecasting Models (GFMs)** | | -`RegressionModel`* | ✅ | ✅ -`RNNModel`** | | ✅ -`BlockRNNModel`*** | ✅ | -`NBEATSModel` | ✅ | -`TCNModel` | ✅ | -`TransformerModel` | ✅ | -`TFTModel` | ✅ | ✅ +Model | Past Covariates | Future Covariates | Static Covariates +--- | :---: | :---: | :---: +**Local Forecasting Models (LFMs)** | | | +`ExponentialSmoothing` | | | +`Theta` and `FourTheta` | | | +`FFT` | | | +`ARIMA` | | ✅ | +`VARIMA` | | ✅ | +`AutoARIMA` | | ✅ | +`Prophet` | | ✅ | +**Global Forecasting Models (GFMs)** | | | +`RegressionModel`* | ✅ | ✅ | +`RNNModel`** | | ✅ | +`BlockRNNModel`*** | ✅ | | +`NBEATSModel` | ✅ | | +`TCNModel` | ✅ | | +`TransformerModel` | ✅ | | +`TFTModel` | ✅ | ✅ | ✅ **Table 1: Darts' forecasting models and their covariate support** @@ -138,7 +153,7 @@ and past targets to do predictions. ---- -## Quick guide on how to use covariates with Darts' forecasting models +## Quick guide on how to use past and/or future covariates with Darts' forecasting models It is very simple to use covariates with Darts' forecasting models. There are just some requirements they have to fulfill. Just like the `target` series, each of your past and / or future covariates series must be a `TimeSeries` object. When you train your model with `fit()` using past and /or future covariates, you have to supply the same types of covariates to `predict()`. Depending on the choice of your model and how long your forecast horizon `n` is, there might be different time span requirements for your covariates. You can find these requirements in the [next subsection](#covariate-time-span-requirements-for-local-and-global-forecasting-models). @@ -223,6 +238,7 @@ If you want to know more details about how covariates are used behind the scenes ## Examples Here are a few examples showcasing how to use covariates with Darts forecasting models: +- [Static covariates tutorial including TFTModel example](https://unit8co.github.io/darts/examples/15-static-covariates.html) - [Past covariates with GFMs](https://unit8co.github.io/darts/examples/01-multi-time-series-and-covariates.html#Covariates-Series) - [Past and future covariates with TFTModel](https://unit8co.github.io/darts/examples/13-TFT-examples.html#Training) - [Past and future covariates with RegressionModels](https://medium.com/unit8-machine-learning-publication/time-series-forecasting-using-past-and-future-external-data-with-darts-1f0539585993) diff --git a/examples/14-transfer-learning.ipynb b/examples/14-transfer-learning.ipynb index 717f8f28c0..5e223a883d 100644 --- a/examples/14-transfer-learning.ipynb +++ b/examples/14-transfer-learning.ipynb @@ -2257,7 +2257,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.12" } }, "nbformat": 4, diff --git a/examples/15-static-covariates.ipynb b/examples/15-static-covariates.ipynb new file mode 100644 index 0000000000..724fc1192d --- /dev/null +++ b/examples/15-static-covariates.ipynb @@ -0,0 +1,997 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "711bc155-8d7d-46e6-9ca0-b4b859515066", + "metadata": {}, + "outputs": [], + "source": [ + "# fix python path if working locally\n", + "from utils import fix_pythonpath_if_working_locally\n", + "\n", + "fix_pythonpath_if_working_locally()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d3da8657-2bff-43e1-a96f-db149fa9f409", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a46ecf12-0d29-459f-bd2a-90ae4c745df6", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from darts import TimeSeries\n", + "\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "import logging\n", + "\n", + "logging.disable(logging.CRITICAL)" + ] + }, + { + "cell_type": "markdown", + "id": "8f32480c-db7d-4e9f-8ebc-bccf004d817e", + "metadata": {}, + "source": [ + "# Static Covariates\n", + "\n", + "Static covariates are characteristics of a time series / constants which do not change over time. When dealing with multiple time series, static covariates can help specific models improve forecasts. Darts' models will only consider static covariates embedded in the target series (the series for which we want to predict future values) and not past and/or future covariates (external data).\n", + "\n", + "In this tutorial we look at:\n", + "\n", + "1. how to define static covariates (numeric and/or categorical)\n", + "2. how to add static covariates to an existing **target** series\n", + "3. how to add static covariates at TimeSeries creation\n", + "4. how to use TimeSeries.from_group_dataframe() for automatic extraction of TimeSeries with embedded static covariates\n", + "5. how to scale/transform/encode static covariates embedded in your series\n", + "6. how to use static covariates with Darts' models\n", + "\n", + "We start by generating a multivariate time series with three components `[\"comp1\", \"comp2\", \"comp3\"]`" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "13e632a4-ff6a-4d58-be65-b6bebc37ca2b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAEXCAYAAACwHc/gAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABzdElEQVR4nO2dd3gUdf7HX7O76b2HJEBCh0ACZJAOCqKCFQULqKCeWA77YTn1/Hl6FvT0bGc7GxaQpogIioJUAQcIoZdQk5De+5b5/TG7IUDKJpktCfN6Hh42O7Mz72x2PvudTxVkWUZDQ0NDo/2ic7UADQ0NDY22oRlyDQ0NjXaOZsg1NDQ02jmaIdfQ0NBo52iGXENDQ6OdoxlyDQ0NjXaOqwy53Np/2dnZrX6tmv/cQYc7aNB0aDragw530KCCjkZpdytys9nsagmAe+hwBw2g6TgXTcfZuIMOd9AAjtNhsGcnURSDgNVAP2CYJEl76m3TAx8DPYHtkiQ97ACdGhoaGhqNYO+KvBK4EljcwLargCxJkkYDfqIoDldLnIaGhoZG89hlyCVJMkqSlNfI5hHAL9bHq4CRagjT0NDQ0LAPu1wrzRAClFoflwChDe0kiuIsYBbA7NmzmTBhQqtOZjQayczMbNVr1cQddLiDBk2HpqM96HAHDW3VERsb2+g2NQx5MRBofRwEFDa0kyRJHwEfWX9sMgLbFJmZmU3+Qs7CHXS4gwZNh6ajPehwBw2O1KFG1spm4FLr48uBTSocU0NDQ0PDTuw25KIo/gRcBnwsiuJMURQ/tG76EegiiuIGoFqSpD8coFNDQ6MFGIuNyJZW3/hqtDPsdq1IkjTpnKc+tz5vAmaqJ0lDQ6MtnPziFHsf34/vIF86LYtB79XuykU0Woj2F9bQ6CDIssyhlw+z59F9yCaZij8r2Pu3fWjDYzo+miFvx8gm7QLVULAYLex+aC9HXj8KOuj+cAKCt0DGN5kce/+Eq+VpOBjNkLdTMuZnkpayl9M/ZLtaioaLMZWb2H7rTjK+zkTnoyPly0H0frYXXV6MA+DAcwfJ/bWxMhCNjoBmyNshpgoTB/55CGQ49t5xV8vRcCE1eTVsvfZP8n7NxyPUg6HfDSHqikgAgi8LoucT3cECqX9Jo/xguYvVajgKzZC3Q058cora3FoAiqUS7QK9QKk4WsEfE7dSklqKT1cfhq8cSsiQ4LP26fG37kRfG4WpzIQ0fQe1hbWuEavhUDRD3s4wlpo4+vYxALx7eAGQsSDLlZI0XEDx9mL+mLiNymNVBCYHMmLlUPx7+J23n6ATSH53AIHJgVQeq2LnnbuwGC0uUKyRlpbGsWPHHBJ81gx5O+P4B8cxFhkJGR5C7DMxAGR+m4XFpF2cFwq5v+Sx9TqJ2vxawseFMWzZELyivBrdX++rJ+XLgXhGelKwoZB9fz/gRLUaNh5++GFGjx7NihUrVD+2ZsjbEbWFtRz7r5KB0PvvPfAb5Itvd19qcmrI/73Axeo0nMGprzLYfutOzJVmYm+KQfxmMIaA5stBfGJ9SJk3CJ2XjpOfnuLEJyedoFbDhizL7NixA4DBgwerfnzNkLcjjr57HFOZifCLwwgdEYogCMTdrPRtyPjG9Q2BNByHLMscfu0Iux/ai2yW6f5oN5Le64/Ow/5LOGRIMAPeTARg31MHyF+vffk7i/T0dEpKSoiMjCQmJkb142uGvJ1Qk1vDiY+VVVSvp3rUPR97UycQIHdlLrVFF24gq7RCJqugY36cLSYLex7dx+FX0kEHia/1pffTPREEocXHir0phm4PJiCbZXbekUrF0QoHKNY4F9tqfMCAAQ45fsf85HdA0t86hrnSTOQVEQSLwXXP+8T6EH5xGJZamawlF2ZO+cY0mZ7TZMY+FkFBSccqkjJVmNhxeyqn5mWg89Yx+POBdL2zS5uO2fuZnkReEYGx2IQ0bSfGUqNKajUaY/v27YBmyC9oqjKrOfnZKeDs1biNuFsU90rm/AvPvfLxcplxD8vkFkFVjY7tB12tSD1q8mvZNlki9+c8PEI8GPqdSPSVUW0+rqAXGPhBEv59/ak4XEHq3WnI5o71BehuaIZcg/Q3jmKpsdDpumgC+weetz1qUiSGQAMlqaWU7S9zgULnYzTJzH7TwqzXZIwmiAlXnt+V7lpdalF5vJI/Jm2leHsJPp29Gf7TRYRcFKLa8Q0BBsSvB+ER6kHer/kceP6QasfWOJv6gU7NkF+gVJ6o5NRXGaCDno93b3AfvY+eTtdHAxdG0DO/WObyx2Te+w48PeDTJwWem6n4i9PS2//KsiS1hM1XbKUyvZLAAQEMXzUM/17+qp/Ht6svKV8MRDAIHHvvOKe+7vifHVdw7NgxioqKiIyMpFOnTg45h2bI3ZzDr6Ujm2Rip8bg37vxi7mzzb2y6HSHLvjYnS4zZJbM2p0QHQq/vyVwxySBJOt3XFo7X5Hn/ZbHlmv+pDavlrCxYQz94SK8oxvPEW8roSNC6f96PwD2PLaXwq1FDjvXhYptNZ6SktKqALU9aIbcjSk/XEHmt1kIBoEecxpejdsISgnCv5cftXm15P2a7ySFzuW79TLD75c5ng1iH/jzI4Hh/ZULo38CCILM/hNQa2yfq/KM+ZlI03ZirjATM7UTQxYMxiNQjWmMTdP5tjjiZ3VBNsrsuD2VqlNVDj/nhYTNP56SkuKwc2iG3I05PPcIWCBueix+Cb5nbZNlmcOZBkzWVraCIBBrXZVndLCgp8Ui8/xnMtc/I1NRBdMnwPp3BOIiz6xu/H0FukaZMZrgQDurdZFlmSNvHCVt9h5kk0y3BxNI/u8AdJ7Ouzz7vNCb8IvDqM2vRZq+A1O5yWnn7ujYDLkjCoFsaIbcTSndW8bppdnoPAV6PNbtvO3/WQSX/C2Ci+6R2bpPMeaxN8aADnJ/zqMmv2PklJdXytz4nMz/fSYjCDD3PoEvnxHw8Tr/FrVvFyWNrj25V2SzzN45+zn0r8MgQL9X+tDnuV4IOsfcgjeGzqBj0CfJ+HX3pWxvObvu262NilMBWZa1FfmFzOGXjwDQZWZnfGJ9ztomyzIf/qBcZDsPw/D7ZO55zUKlrycR48ORTTJZS047XbPaHD8tM/KvMkvWQaAf/PiKwJxbhEb9jH07K6vIXUfahwEyV5nZMTOVk5+dQuelY9CnycTf3dVlejyCPUj5ZjCGIAM5P+VyyPoZ1Gg9J06coLCwkPDwcDp37uyw82iG3A0p3lFCzspcdD46uj98/mo89TAcPAlhgWaenA4GPXy0HHpPlzncRyn/be/ZK7/vlBFnyaSlQ6/OsPUDgUnDm16l9uvaflbktYW1bJ0skfNTLoYgAxctFel0TbSrZeHfw49BnyQj6AXS3zhK5mKts2ZbcEagEzRD7pYcevkwAPF/6dJgV7tvflVWnFcNq+ble3Ts+lTg4kGQXwIzfo2gytODsj1llO4udaputXj/e5kJj8oUlMAVQxUj3qdr8xdB3y7KitzdDXnlySr+mLiN4j+L8Y71ZvhPQwkdpl6OeFuJuCScvi/2BmD3Q3sp3lHiYkXtF2e4VUAz5G5H4R9F5K8pwOCvp9uDCedtt1hkFvymPL5uhJJd0DdeYM1/BL56RiAsXMev/srKbv6TGZRVtg83AyjZJve+buH+N2RMZphzi+JOCQ6wbyXTOcJMgC9kF0JukXv+3iVppfxxxRYqjlQQkOjPiFVDCeijbo54dY3MHS9b+HGrd6uP0fXuLnSeEYel2sL2W3dSnVWtosILB2cEOkEz5G6FLMscesm6Gr8vHs9Qz/P22ZgGGXnQNRpSep7pkSEIAtMvEzjwpUDIZMW9EvhnNv2nm1n8u+z2k9Rzi2QufUTmwx/AyxO+fEZg7n069Hr7b0d1Ohhg9US546o8b20+W6/eRk1OLWGjQhn240V4x7Te2DbGyq3w+Up46n9B1NS27u8uCAKJr/QldGQINTk1bL9tJ+Yqs8pKOzbOCnSCZsjdioJ1hRRuLsIj2EDC/Q0HvWxulVvGK4brXIIDBP71ahCG7v4EmY3EHctj6j9kJj0ucyTDPY156mGlyGdDmlJqv+EdgVsva50/0VYYtMvN4nSZC7OQbt6BqdxMpxuiERem4BHo4ZBz7Tys/J2LynUsXd/64+g8dQz+bCA+XX0oSS0l7cE9br8gcCdOnTpFfn4+oaGhdO3q2CC2ZsjdhPqr8W4PJDR4kdcaZRb9rjyedmnjhk4QBHreoeSUPxqaRbA/rNoK/Wcq+djVNe5zMS5aKzPifpmTOTAsEaSPBIb0bX1QKLmHe5Xqy7JM+ltHlXQ+k0zC7HgGfpCE3stxl96Oem1TbNlNrcUzzBPxm8Ho/fScXppN+htH26juwsFZgU7QDLnbkPtLHsXbS/CM8KTr3Q23KV0tQWEpJCbAgO5NfzBipnRCMAj47ilgz39qmXEF1NTC/30m03+mzM/bXGvoLBaZZ/9n4cbnZKpqYOZEWPsfgU7hbfvAJ9lcK25gb2SzzL4nD3Dwn0qOeN+X+tD3+d4OzxHfqawH0Otk1qXCgRNt+1sH9PFn4MdJIMChl46Q/WNO20VeADjLrQKaIXcLZItclzfe/eEEDH4Nl2V/s1q5IJtajdvwivAickIEslmm5pfTfP53HeveFugXD+mZcMXfZKb+w0JmnvMNemmFzOSnZV6cp7iH3pwt8OmTAt4NFPm0lAFW18q+40qHRFdhrjKz485UTvzvJDpPgUH/SybhHsfniOcUymTlQ4AvTB2rBMM/Xt729yHq8kh6/6MXALvu291uM6KcibMCnaAZcrcg+8ccSneX4d3Jiy4zGy4aqKiS+X6j8viW8fYdN/YWa075/CxkWWbMQIHUTwXm3ifg6w2Lf4c+t8q88a3sNKOXnikz/D6ZHzZBSACsek3g4RsbL/JpKQG+At1ioNao5Nq7AlOJiW1TJHJ+zMUQaGDIYpFO1zknR9y2Gh/YA24bXwnA56tQxZ3W7YF4Ym+MwVxpRpq+k5rcmjYfs6PizEAnaIbc5cjmeqvxx7qj99Y3uN/yzVBZrfiRE2LsM3qRl0XgGe5J+YFySnYqKygPg1Iduf9LgcmjobwKHntPJuUvMpt2O9aY/yopQc19x6FvV9j2ocCEIeq7GVzZCbEqo4ojM45RtKUY7xilj3jYyFCnnd9myAf1hOTuRgb3UtxxS9a1/diCIND/zX4Ei0FUZ1azY0Yq5pqO22mzLWRmZpKbm0tISAgJCeenEauNZshdTNaS05QfqsCniw+dp8c2ul9L3Co2dB46YqYo/Y/PbaTVJUpg6b90/PiqQEIn2H0URv1V5s5XLOQXq2vQZVnm7cUyV8yRKSqDq0fAlg8EesQ5xld8JnPFua4Vi9HC1mv/pOZoDf59/Rm+aigBfQOcqmHHIeV3HtRLeW9nXa3839agpw29t56UeYPwjvGmaFsxex7bq2WyNIAt0Dl48GCHBzpBM+QuxWK0cPhVZTXec073RrvdFZbKrNqm+JNvvKRl54ibpnw5ZC05jbn6/DzgK4cL7J0n8OwMZUjDZz9B71tlPl4uY1GhaVJNrcxfXpV56G0Zsxn+fht8/5JAoJ/jPtzJ1kCwswOepbvLqDxehUe0B8NXXIRPrPo54s1hW5EPVtzZTJsA/j6wIQ32H1fH4HpFeZHy9SD0vnoy52dx7P0Tqhy3I+FM/zhohtylZMzPpPJ4FX7dfYm5sfHJIUvWgdEE4wdDVGjLDGBgYgCBSQGYSkzkrMxtcB8fL4F/3qUj7TOBS0XlVnzWa0pa4M5Drb/4swtkLnlI5tOfwMcLFjwn8K+7degcnLXhKtdK8fZiAPyH+OER5Jgc8aYoKZdJz1QKqvpa46oBvgLTLlUef6RC0NNGUFIgSe/1B+DAcwfJXZ2n2rE7As70j4NmyF2GudrMkdeUJWPPJ3ugMzT+p7AVAU2b0DoDeGY4c9MNkHp3Efjl3wILnhPoFAZb94E4S+bhty2UVrTMCEgHFH/4H3uhcyRsfFfgpvHOac3aLQb8fCArH9XdRE1RvF3pSeI7wKeZPR1DqrUIakA3JRZi455rlMdfrIIqFWsIOl0TTc8nu4MFUu9Oo+xAuWrHbu+4rSEXRfFVURQ3iKL4pSiKHvWe9xFFcbkoiutEUfxNFMW2j/m+ADg1L4PqrGr8+/o3mdGQmafkAnt5wuTRrTtXzJROCB4CeWvzm+2ZIQiKwT3wlcDDU5Xn3lqsZLcs+M2+Uv9vVsuMni2TkQcjByiTfAb3dl5/bZ1OcEmpvq25lG+SbzN7Ooad1kKgQT3Pfn5wb4GU3lBUpmQqqUmPv3Wn03XRmMpMbL91B7WFHaMPfls4ffo02dnZBAUF0b1705O91MIuQy6KYjIQK0nSaOAAMKXe5onAHkmSxgKfA3epLbKjYa40c+RNZTXe66keTRaIfLsGZBmuHAZB/q0zhp6hnkRdEQkWpVTcHgL9BN58QMf2jwWGJcLpArjleaUr4cGTDRtzs1nmyQ8sTH9BproW7r4a1vxHaLE7SA2SnGzIa4tqqUyvROetw6en833jcKY0f1DP899v26pcTfcKKF/8Se/0JzA5kMpjVey8Y1eHnhlrD/X9484IdIL9K/IRwC/Wx6uAkfW2HQH8rI9DgI45MFJFTnxyktrcWoIGBhI1KbLJfevcKi3IVmkIW9Az45vMFmUZDOwpsOk9gY8fFwgNhN+2w4CZMs98bKGy+sxxSsplrnlK5tVvQK+Hdx8W+PBvAp4ezjficKZUf5eTSvVLrKvxoORABBf9zrbSfFugsz63jFeKhDamwd5j6r4nel89KV8OwivKk4KNhex76gAAlZWVZGa27PPWEXB2oBPsN+QhgK2UqwSonxh7GOgniuJe4F7gG/XkdTyMpSbS3z4GQK+nezb5jX3olMz2g8p0nEnD23be8HFheEV5UpFeSbHUsv7SOp3AX64SOPiVwJ2TlMDrv76ExNtlVvwhc/S0nmH3yvy0BcKCYPW/Bf56vXpFPq3B2QFPm388OCXIOSc8h6oamf0nz+4AWR9/X4HpE5THH6mUimg0GklPT+fXX3/lq5++ZOvwLZh1Zk5+dopbIqbh5+dHXFwcH3/8sSrnay842z8OYO+I7mIg0Po4CCist20GsFGSpP8TRXEK8CzwxLkHEEVxFjALYPbs2UyYMKFVgo1GI5mZrp9+01odOR/mYiw04jfIl5pe1U0e48Ol/kAAl6dUUph/vvFtqYbAiYHkfZ7PwY8P0Tmm8Zz1pvjnbXD1RR489UkQB055cNUTMp6GCGpN0KezkU//VkSXSDOu+BPVfz/CfAQgmr3HZE6czMTQcJ2VamRvUvqPWBIsLvmM7jzigdkcTu84I0UF+RRx/ufjuqEGPlgWwRerLDxwzWl8zu+SfBZms5mcnBxOnjxJRkYGJ0+e5NSpU3X/Z2dnY7Gc7Ua5xHMcf/N/nGnmWzngeYDU2p389NNPzJo1ywG/tf0482+ybds2AOLi4s47Z1t0xMY2fs3aa8g3A48C84DLgU31tgmccafkoxj685Ak6SPgI+uPrV4SZGZmNvkLOYvW6KgtqmXvPOW2s//ziYTFNV7xJ8syP1obW/3lGj9iY88fPtBSDYF3B5H3eT6lv5QR/Z9o9L6ts26xsXDVGJl3lsI/PpEpr1KqROc97Ym/r+vGldV/P2KB+GgLx7MFKiwx9OviuLsDWZbZt/cgAN0mJFCoK3T6Z/RHSQZkhvTzqDv3uZ+P2FgY0sfCnwd0bD4Yw22XQ25uLsePH+fYsWN1/2w/nzhxAqPR2MgZFf94586dSUhIqPsXHx+Px0YDLIVXYudy18mZpKWlERoaio+Pa7J5wHl2Izs7m5ycHAICAhg5ciS6c3pNO0qHXYZckqRUURRzRFHcAJwEXhdF8UNJku5BcaV8a12N69GCnY1y7L0TmMpMhI0Na7Zse8chOHQKIkNgnEqutoA+/gQNDqJkRwnZK3KInRrT6mMZDAKP3Ag3j4M12/K55Ypwh+eHt5Sk7nA8W3Gv9It33Hkqj1ZiLDLiGemJd5w3uGDMZWOBzuLi4rOMdEB5NHAL9z6/g/tuGENlZWWTx42KijrLSNc32p07d8bT8/xlvXybzPbKneSuyuPFsJf5a969bNu2jbFjx6r2+7or9Ss6zzXijsTeFTmSJM0556l7rM+XAFeoKaojUpNXw/GPlAq4Xk/1aHb/+dYg542XKEZTLeJuiaFkRwkZ32S2yZDb6BQucHFyrdsZcVAM+Q+blFL9mx2Yw17nHx8c5LK4QP1AZ2lpKddffz2SJFFSco5LTucLQ6+kymMQkEBISFaDRjo+Pp74+Hh8fVueSinoBAZ+kMTmiVthP9zsPY3169dfEIbcFYFOaIEh12gbR98+hrnCTOTlEYQMCW5yX7NZZr51Lmdbs1XOJeb6Tux/5iAFGwqpOlWFT2fX3e46GiVzRXZ4wPNMoDPYsSdqBKNJrmtHMLAHrPhxBb/9pnyAfH19zzLQCQkJ/JxewM9pgdz9nMRHjzsmVdIQYCDx5T5svU5isEcKyzYsdch53A1XBDpBM+ROoTqrmhOfnAKUKs7m2JCmVCXGRyvdDtXEI9iDqEmRnP4um4xvs+j5N+cULLgCZ2Wu2ErzXZWxcuCEMjQkoZMy6m/Dhg0APPbYY7z22mvn3SWMOyLz850yC3/35D8Pyvh6O+YuIviiEAQvgQQS2LN5D0ajEQ8P57cucCauMuRaib4TOPLGUSw1FqKviSIoKbDZ/W1ulVsuxSG36rac8sz5HTvHt3uM0uMlI09pPOYIzNVmSveUgQBBg1xjyM9tlGUz5CNHjmzw85PcQ2BoPygph4VrHadL76UjdJgSC+pu7MHOnTsddzI3IDc3l4yMDPz9/enVq4FkfgeiGXIHU3miklNfZYAAPZ9ofjVu71zOthA+NgzvTl5UHq+i6I8ih5zDHdDrHV+qX7q7DNko49/bH49A19zg1g90FhUVsWfPHjw9PUlKSmr0NbZKT7Xa2zZG2GjFkCcZkuu+YDoqtkDnoEGDnBroBM2QO5wjrx9FNsrETOlEQJ/zUwjP5edtSk+MAd2gfzfHGHJBLxB705npQR0ZR7tXbP1VXOVWgbMDnZs2KZnBF110Ed7ejfu/b7xEKTTbstexg6rDRimGPNljIOvXr3fYedwBVwU6QTPkDqX8SAUZCzIR9AI9H7fPFz3/N5tbxbHZD7HWjoinl2VjKjc59FyuxNab3FFDJlztH7dY5Lquh4N6nnGrjB7ddIc1Px+B2y5THqtV6dkQQYMCwQfi9HHs3bD3vAKijoSr/OOgGXKHcnjuEbBA3PRY/Lr5Nbt/eaXMMutczpvHOVabfw8/Qi4KxlxhJnt5x52KXrcid9CQCVu7A1cZ8qNZUFoB0aEQHSbYbcjhjHvly1+UmbCOQGfQESAqd6JdKuLZt2+fQ87jDmiGvANSuq+M00uz0XkK9HisgeYXDfDDJmUu54j+9s/lbAt1jbTmu77lgaMYYDXke46CSeUB0zX5tVSdqELvq8ffDreZI6gf6KyqqkKSJARBYPjw5pvzDOguMDxR+SL4do3jNAYMVd6bZI/kDuteyc/P5+TJk/j5+dG7d2+nn18z5A7i8CtHQIbOMzrjE2dfrnadW8VJAxiir41G56OjcFMRlcebrvBrr4QECHSJgupaOKLy95XNrRI0MLDJwSCO5EygU+nxYTQaSUpKIjg42K7Xz3JC0NP/IuVuNNmQzPp1HdOQ2wKdAwcORK93cGOfBtAMuQMoSS0hZ0UuOh8d3R+2bzVeUCKzaqvSAnZqC+dythaPQAPRVylzQDIWdNygp6MCniUu7ngI9QOdLXOr2LjxEgjyh237IfWwY4y5dy9vdIE6IvVRHFh/oEOmvLoy0AmaIXcIh15Wok/xf+mCd7SXXa9Z/DuYzHBpSsvncraFujFwCzKRVRi27I7YDLnavcltFZ1Bg11jyGVZrjPkg3rCxo1KgKUlhtzXW+D2y5XHag+dsCHoBCLGhgMQXdSJo0edPBXbCbjSPw7tzJBbLDIV1e7X06M+hVuLyPs1H72fnm4PJNj9Ome7VWyEjQ7Fp7M3VaeqKdhY2PwL2iG2zBU1V+SyRa5LPQwRg9U7cAvIyoe8Ygj2h86RFjZv3gzAqFGjWnScWVcr789XvygBd0cQPjoMUPLJO6KfXDPkdrI7XWbYvTJPf9p8ZaQrOfySshpPuK8rnmHNNHy2cipHZv0u61zOMY5Udz6CTiD25jPTgzoijnCtVBypwFRqwruTF94xrhrtpvw/qCekpe2irKyMbt26ERPTsmZo/bsJjOgPZZWwwEFBzzP55MlsWN+xCoMKCws5fvw4Pj4+9OnTxyUa2o0h9/WGXemweIMv0gH3dAHkryugYGMhhiADCffH2/0621zOq0coszKdTdzNyoWf/WMOxtKOl1PeIxa8PeFkDhSVqfPZqXOruNA/Xt+Qt8Y/Xp+6mZ4OCnr69fJDFyoQogvl0O+HHHIOV2ELdCYnJ2MwuKa6t90Y8u6xAg/eoDx+7D37prk7E1mWOfSScmV1m52AR5D9zYFc5Vax4RvvS+jIECxVFrKXZbtEgyMxGAT6W2POu1ValZ/JHw9W54CtYMch5XMzuJfQKv94faZeAiEB8OcB2HlI/WtLEAQiL1bm04acDiUrq+ME113tVoF2ZMgBnr5NIMTfwvpd8L2b3Z3lrc6nWCrBM8yD+Fld7H7dgRNKwCrQDyYNc6DAZoi7uWPnlCep3HOleEcx4NqMFduKfGBPuW5F3lL/uA0frzNBT0elItoCnskeAztU3xXNkLeQ4ACBR6eUATDnfZlao3usymWLzKGXravxh7th8Lf/9sq2Gr9hLHh7uS6QG31NFHo/PUVbiyk/UuEyHY5C6U2uTuaKudJM2d5y0EFQsmtiNgUlMieyle6O+tp0cnJyiIyMbFPXPVtO+derocwBQU9bA60BhiTW/95xAp6aIW8Ft46vpHcXSM+E975ztRqF7B9zKE0rwyvai653dLb7dbIsM/9X5bGr3Co2DP4Goq9RcsozO+CqXM2AZ0laKbJZJqCvf4u+tNXE1l8luQds3nRmNd6Wtsf94gVGJUF5FSz4TQ2VZ+Pb1RchUiBAF0D62o6RglhUVMTRo0fx9vamX79+LtPR7gy5hwFev1/5sP7zc5mCEteuymWzrFRxAj0e64bex/6qru0H4XAGRIXCJYMcpdB+Otv6lH+bhWx2j7sdtbAZ8j3HlAlMbcHVE4EAdrYxf7wxHN3eNnqcsljwO+FHYWH7T3e19VhPSkpyWaAT2qEhB7hyOIxPgeJyxZi7kqylpyk/WIFPZ2863xrXotd+Yx0gcZPKczlbS8jwEHzjfag+XUP+ugJXy1GV0ECBuAill016G+Nsru54CLCjXg/ytvrH6zNlrBL03H4Qth9U/9qKvDgCUPLJbV9A7Rl3cKtAOzXkgiDw778KCAL893s4dMo1xtxitHB4rnKv3mNOD3Se9r+dZrNc16jI0S1r7UUQhLr2th0x6KmWe6VuRe6iQiA4syLvElbAkSNH8Pf3Z+DAgW0+rreXwAzrKHVHpCLa8skTPfqzcZ1myNWiXRpyUIJXd05Sytoff981hjzz2ywqj1bi292X2Js6tei163cplXkJnWCo61xr5xF3cwwIkLMiF2Ox0dVyVKWuVL8NvclrcmqozqjG4K/Hv2fzrYkdQUWVzMFTYNBDwcl1AAwfPly1W3tbpec3v6of9PTu5A0x4Cv4kr66/fvJNUOuAi/cJeDnA8s2wtodzjXmlloLh19Tlna9nujR4u53NrfKNAfN5WwtPnE+hI0OxVJjIeu7jpVTbstcacuKvK4QaFAQgt41f7ddR5QCssQE2PqHkv2hhn/cRt94gTHJStDzm9WqHbaOTlY/uWe6F+Xl5eqfwEmUlJRw5MgRPD09SUxUeUp6C2nXhrxTuMCT05SL6dH35DYHsVpC4dIiqjOq8e/rT6fJ0S16bU2tzBJlIeU2bpX61PUp72Al+2q4VoqkYsA98sfrV3Sq4R+vT/2gp9rFd9HjletlgH4AW7ZsUfXYzqR+oNPDw/4CQEfQrg05wKM3QVwEpB5WJp04A3OlmZyP8gDo9WQPBF3LjLFtLmdSd0hMcD9DHn1lFIYAAyU7Sig70H5XTOfSK07pZ3M8G0rKW2ecStwgY8VW0dm3cw27du3Cw8ODoUOHqnqO68dAWJDypbH9oKqHJmyk4ifvZ0hkw5r2WxjkLm4V6ACG3Ndb4OV7FGP4949kh42sqs+JT09iyjcRmBxI1JWRLX79GbeK+xlxAL2vvu4uI3NBx1mVGwwCifHK492tcM/KZpnina7vQW5bkeuqdmGxWEhJScHX11fVc3h7CcxwUKWnZ5gncmcLnoInx345puqxnYlmyFVm2qUg9oHTBfDafMcZclOFibw1+aS/pXz4ev29R4v92+WVMj8og865ebzaCtUj7halkVbmwiwspo4zMLct7pXyQ+WYK8x4x3njFWVfn3m1qTXK7DkGggA56cotqJr+8frYKj3n/walFepeV53GK8kB+kMe1NTUqHpsZ6EZcpXR6QTe+KvyoZs7HzLz1PnQWYwWirYVcfj1dLZcvY3V3dfw59TtGAuN+A7yJWJ8eIuPuWwjVNXAyAHQNdo9V+QAwUOC8evuS01OLflrOk5Oua03eWsyV4rdYCLQ3mNgNEHPONj2h1J+qbZ/3EbvLgIXD4KKKqVsX01iL1MWCokkIkmSugc/h+oaGemQh6oxtNLSUg4dOoSHhwf9+/dX7bitpUMYcoDRyQI3jFWM5NMft+4PJssypfvKOPbBcaRpO1jdfQ1/TNzG4ZePULi5CNkkEzQwkG4PJZDwZpdWZZu4u1vFxlk55R0o6Fm3Im+Fa8UtKjqtbpXk7ha2bt0KwMiRIx12PlsqotpBz9DhoVgEC70Mvdn02ybVjnsuldUyEx6Tue65cC59VCYjV53fITU1FYABAwbg6Wnf3AFH4rqaUgfw6r0CP2yS+WIVPHCDTErv5o1l1akq8tcVkL++gIINhdTm1p613a+HH+FjQwkbE0bYqFA8gpXodGZmy41bfrHML386dy5nW4i7KYZDLx0mZ1UutYW1eIa6/gPbVmyGfPdRZeKUrgWB6rqKTtGVMzoVQxTmfYqamhoSExMJCwtz2PmuHwPhQUrK45/74SKVah48Ag1YulgwnDBwdNUx+Ic6x62P0SRz43MyG9OUn3/fCUl3yHzyBEwe07aFlDu5VaADrcjBvp7ltQW1nF6Wze5H9/K7uJ61A9ez+6G9nF6STW1uLV7RXsTeGEPSe/25JG0sY7eOInFuP6Kviqoz4q1l8TqlgGmCCBHB7r0iB/CO8SbiknBko0zW4tOulqMK4cECMeGKu+BoC0r1TeUmyvaXIxgEgpJcN6XKtiI3FiqrcUf5x214eQrMnKg8Vjvo2cmahijsFzCbzaoe22KRuetVmRV/KNk3i54tYNIwJVvs+mdk7n3dQmV1638fzZA7mGduFwgLgnWpij/aVGEi77c89j93kI0Xb+bXXmvZeecuTn2RQeWxKgyBBqImRdLvlT6M+WMk4/aMJfn9AcTdHItPrLojvL5Z3T7cKvWJtQY9MxZ0nEEArQl4lqSWggUCEgNa1BhNTcxmmV1WzSf3LQMc5x+vz91W98qCNa1P22yI+EldAehj6UtaWppqx5Vlmcfek/nyZ/DzgZ9eFRjer5YfXxV460EBTw/48AdI+Yvc6ipfdzPkHcq1AhDoLfPyqGLWflbAyZmFrK4oQa7Xt1znpSNkaDBhY8IIHxNKYHJgi6syW8OpHJkNacrIsescu4hSlaiJkRiCDJTuKqV0bxmBiQGultRmkrrBqq2Qli5z/Vj7vlTr3CqDXedWOZyh3El0joQ/f1sFOH5FDtCrs8Alg2TW7lSCnvdPVue4oUNDMAtmuut7sPHnjQwapE4L0Je/gv8sUjqlfveiwEX9BDIzlbjPg1Ng7EC45XmZ/Sfgontk5t4LD06xv8K6vLycgwcPYjAY3CLQCR1gRS7LMqV7yzj2/nH+vEUJUMbO/ZNb847So7gYi0kmaFAg3R9O4KKlIhPSxzH0uyH0eKQbwSnBTjHicGao7dUjIcC3/azI9d56Yq5XUsU6SiOtM0Mm7H+NO2Ss2Nwq3aNKKS4upkuXLnTpYv80qrbgiEpPva8ec4IJnaDj2E/q5JN/9IPM0x/LCAJ89YzAhCHnX2vJPQSkjwXuvRZqjfDwOzJXPi6TU2jf75Wamoosy/Tv3x9vb9cM3j4Xu1fkoii+CowAjgN3SpJkrLftZmAWyhfDU5Ik/aGyzrOoPFlFQf0AZd45AcqefpT3CuXFtFBORYWQtsiT0EDXGs/2kq3SEHHTYjn52SmyFp2mz3O90Hm07+//1rhW3MGQ2wKdvhal9aEzVuM2Jo+BiGDlPdu6D4ap1Fqk0/hoCo4WIe8TkGW5TX2Hlvwuc98bynv030cFbhzX+LF8vQXef0zg8osUX/rKrZB8p8znT8EVQ5vW4G5uFbBzRS6KYjIQK0nSaOAAMKXethjgWmC8JEkXO8qIVxytYPcje9k/6SC/D1rP7of3cnppNrV5tXh38iL2JiVAOW73WMZuGcWkL/rid0kkmVUeLu9Zvv+4TOphCPKHiepWUjuFoEGB+Pf2oza/ltxf8lwtp8307gKeHkqw055Cl6rMamqyazAEGvDr4ZqOh3BmRV52WmnU4wz/uA1PD4E7HBD07HFNDwB6Gntx8GDrewGs2S4z7QUZiwX+eZfAvdfa94Vw3WiBXZ8q+fI5hTBxjsyj71qoqW38d2y3hhxlJW7rZLIKqJ+4egVQA6wWRfFLURT9VdRXh2yBU/MyqM0wYggyEHVlJP1e7cuYP0Zyye6xJP9XCVB6xyi3OvV7lr/3net6lkO9uZxjlCyA9oYgCHWNtDLnt/+gp4dBoF+88niPHfnk9f3jLe2roxayLNcZ8kM7FwLOXZHDmaDnt2uguEyd6ylEDMakMxFviGfjitb1J5cOyFz7d5laIzxwAzxze8teHxcp8OsbAi/PEjDo4c2FMOw+mQMnGv4d3dGQ2+taCQFs+WclQGi9bVFAODABuA+YDbxy7gFEUZyF4n5h9uzZTJgwoUVCZW+ZmMej8ervScCAgLoWoiWUUJJV0uBrwn3gpouDWLDWlwffrOKTx4padM6mMBqNduWSyzLMWxUBGLhsUAGZmbXNvkZtDWqgGy2AHnJW53I87QQeYWc+Os7U0RQt0dEjOojUw76s315M19DKJvfNWqe089X31tl1fEe8Hxl5egpLIwn2M5JzUiI4OJjAwMAmz6O2Dh8BRvUPZeMeL95dVMwdlzf9vtmro7JzBYEngjjw/UEyb26Z3iOZeq5/PozyKj3XjaxizvXFZDWw1rDnvbjtEujf2YPZ7wSTetjAoLssPH97CdPGVWHz+FRWVnLgwAH0ej2hoaEtfn/b8jeJjY1tdJu9hrwYsCXPBgGF52xbK0mSLIrib8AzDR1AkqSPgI+sP7bq6zzuiTgyMzOb/IXO5Y0HZJZvkflZ8uZQTgyXDFZnRWWvjj/3y5zIkYkOhSmXhqNXsYd1S9+LNhEL+ZcWkvtzHvImmdj7zpzXqTqaoCU6hg2QWbxB5kRBELGxIU3ue+qQcuF1vrgzkbERquqwlz+PyoBMXHA+xcCYMWPo3LnpQd+O0PHgVJmNe2S+XRfE03cE2+XTbk5H3GVxlH5chschzxbpzciVuW2uTGGZ4rL89p++eHo07Pqy972IjYUxoswD/5GZ97PAE/8LZtvhYD6aIxAaKLB582YsFgtJSUn06NHDbq0t1dFS7HWtbAYutT6+HKhfU7sJGGh9PBBwq7EfruxZDvXmco5DVSPuCmJvtuaUf5Opeo9qZ5NsvQabC3haTBYlhxwIcmHqoS3QqatS8q2d6R+vz7WjIDJEGWL9xx51jtlvSl8AutV058SJE3a9pqBE5rLHZE7lwvBEWPRPAU8Pda6vQD+BL57W8fWzAoF+sGQdJN8hsy5Vdku3CthpyCVJSgVyRFHcACQCS0RR/NC6LQ04JYri78CdwDuOkdp6XNGzHJQCjgVKX6N2ma1yLlFXROIR6kHZvnJK08pcLadNnFuq3xjl+8sxV5rxjffBK9x1LQps/vH8447teNgc9YOeHy1X58s8eGAQNfoaYvWxbFrWfN+V8kqZK59Q8sATE+DHVwX8fNS/vqZNEEj9RGBYImTkwSUPyfxvdRcQDO3TkANIkjRHkqTRkiRNlySpVpKke+pt+7s1Y+VySZLyHSO19biiZzko1aXZhdA9Fob0dcopHYrOU0fsFGtOeTtvpBUZIhAdCmWVyqCJxqgb7ebCtEM4Y8izDq3Ax8eHwYMHu0xL/aBnkQpBT51BR02C0so2fUXTN/S1RpkbnpXZug+6RsPPrwsOTS1OiBFY/47AszOU1sFpxVdD0u9Ex49w2DlbQ/tOCG4BzupZXh+bW+WW8e41l7Mt2DoiZi05jbnGffqUb9myhZUrV7boNfbkk7tDx8PcIpnMPPD2MEHVEYYNG+bSjnvdYwUmiFBdC1/+rM4xo8cpA1osuxu/Ns1mmdv/pTSeiwiG1f8WiI1w/HXlYRD45106Vr5aAzWnIHA4d7yTVNdywx24YAy5o3qWN0b9uZwdwa1iIygpkID+ARiLjOT+nOtqOQCsX7+esWPHcvfdd7N79267X2efIS8GXFuab1uNh3mdBGSX+cfro3alZ9LNSQDEVyaQk5Nz3nZZlnnwLZlv10CAL6x6TaBnZ+deV4HyLtgxiMDa1ZRVCkx/Qeb2f1lUH7rRGi4YQw7q9Cy3l1VbobhcCar1je84hhzOTA9yB/fK3r17ufbaa6mtVdI6FyxYYPdr60r1G2mcZCw1UX6oAsFDIHCA63rM7Dhk1VOsDGBwlX+8PteMgqhQ2HccNqsQ9AwZEEyloZIIfQSblpzvJ3/+M5n/fq/MXP3hZYHBdrSoVpvt27eDqYjres/n48cFfL2VO5JBd8ls2+daY35BGXJQepZ7GOCLVbD9oOPe/PZckt8cMVNiEAwCeb/lU53tujFdmZmZXHHFFRQXF5OUpKzovv32W7tXiM2tyEt2loAMgf0D0Hu7puMhwM7Dyu+Td+xn9Ho9w4cPd5kWGx4GgTsnKY/VqPQUdALVCVUApP94dt+Vd5bIPP856HSw4DmBiwe55pqyZayIYgp/uUpg+8cCA3sqFcIj/yrz8lfOz4qz0W66HxYUFPDqq69SU1NDeHg4np6eDf7z8vJqdtudl4fx4Qp/HnrLyC+vm/Hy8kSvV+9CLauUWb5ZeezOczlbi1e4J5GXRZDzUy6ZC7PwvsH5/tqSkhImTpxIRkYGI0eOZNWqVXTr1o309HR27NhhV1ZBny5Kh7z0LCUTwv+cZmZ1/nEx2BG/gt3stK7I5bIdDBo0CH9/hxRPt5i7rxJ45WuZhWvhPw/IbQ46Rl0cifGwGfPuM73J5/+quFQAPvqbwHWjXbcwOjf1sE9XgS3vKwkUbyxU/l/9J8x7WqkWdSbtxpDn5+fz2muvqXMwfRAMOcSmPeH4dbkRCpah1+vt/mLw8fHh8ssv58EHH2wwiGmbyzkqCbpEdbwVOSiNtHJ+yiVzfibdro936rlramqYPHkyu3fvpk+fPixbtgx/f3+uvPJKPv/8cxYsWGCXIff0EOjbVSYtXcmLPrcRVJ1/3IUZKyXlMkcyQS+YMFfuY9So2S7Tci4JMQKXDZH5eRvMWwUP39i24w2aPpBtH2+nS3lXiouL2XIwiNv/pRjxV+4RuOsq111L1dXV7N27F51OR3Jyct3zXp4C/54tcNlFMjNeUlr9Jt+pTCFy5pdOuzHkYWFhvPLKK+Tn5+Pt7U1tbW3dv5qamrN+tmdbScG/qYh+GaHbXOTCnzCbjVRVVVFVVWWXnpUrV7Jq1So+/PDD81qJtscBEi0l4tJwPCM8KT9UQeXuKohzznktFgt33HEHa9euJTo6mpUrV9aNOrvmmmv4/PPPWbhwIXPnzrUrUyipu+JaSUs/25DLsuwWHQ93HVH+9+UoZbLJLfzj9Zl1tcDP22Q+Wi7z0NS2ZWeF9Q+jzFBKsCmYr97ZzRMbRmIyw99uhsenqSi6FaSlpWE2m+nXrx9+fudXj15+kUDaZzDzJaWT4uSnZe65RuaN2QK+3o63A+3GkIeHh/PEE0+oVuJqNMkMmClz8GQP3lhQzQPXW+z+Ejhw4ABPPfUUq1atIjExkblz53LPPfeg0+nIK5b5RQKDHqZe3Pbf213ReeiIndqJY/89QdGyYpjonPM++eSTzJ8/H39/f1auXEl8fHzdNlEUiY2N5eTJk2zZssUuX3JSdwGwTYo5c8FVnaqmNq8Wj1APfBN81f9F7MQW6KzMVXx17pCxUp+rR0J0KOw/ARvTYHRy869pDEEQqIyvJOBIIGsXRlEZAjMnwtz7BJen79pT0RkZIrBiLryzBOa8L/PhD7B+l8z8584E1h3FBRfstOFhEHj9fuXNfeELKK3U4+vrS3BwMJGRkcTFxdG9e3f69u1LcnIyQ4YMYeTIkVxyySXcd999rFmzhsmTJ1NeXs7999/PuHHjOHLkCIt/B7N1Lmd4O5jL2RZsOeVFq4oxV6k7c7Eh3n77bV577TUMBgNLly5l4MCBZ23X6XTcdNNNgBL0tIdkW8DznDqUszoeutCI2AKd5pI/6d27N5GRkS7T0hAeBoG7rlQeqxH09B6qfKb6lZVzzUj4eI7rjTjY3/FQmUIksO1Dgb5dqZtC9NYi9QZyNMQFa8gBrhwO41OUgawt7VkeFRXFkiVLWLhwIREREaxbt46kpCRe+yIDUMp7OzqB/QIITA7EUmYhZ6Vjc8qXLFnCww8/DMCnn37aaPdMmyFfuHChXQN962eu1L/Q3MGtAmdyyCnf6XarcRt/uUppF714ndIDpbXkFsm8cUJp2D+gPI9P/1aNweAe15HNkNtbUdvYFKL8EseY3AvakLe1Z7kgCEydOpV9+/Yxffp0qizhHCuIRSdX0zu89U3y2xNxNyk55VmLTzezZ+vZsGED06dPR5ZlXnrpJW677bZG9x0yZAgJCQmcPn2ajRub728dHSYQGQKlFXCiXql+iRsY8qoamX0nAMxQkeZ2/nEb8Z0ELr8IamphXisrPUsrZCbOkZGK/Mj2MOAvQ+p3W9QV2kpqamrYs2cPgiC0aK6oMoVIx9IXBUIDYeVWmPBEOBt2qb8yv6ANOSjfnHdOApMZHn+/dW9weHg4X331FTPnKANxLfk/MGp4Mi+++CJGo7GZV7dvOk2OBj3k/ZZPbYF6vdZt7Nu3j2uuuYaamhruv/9+nnzyySb3FwShxe6Vc/PJLUYLJWmu73i456jiptNXHwZLldsacmhbpWd1jcx1f5fZcUjpS1TV+RQAR5a3YBafA9m9ezcmk4nevXu3KvVz8pgzU4jKq5WFg9pc8IYc4IW7BPx8lLTBtTta/225M7MPABOS86mtreXZZ5/loosuYufOnWpJdTu8Ir0IGOaPbJI5/X0T3adaQVZWFhMnTqS4uJjrrruOt99+2y5/qc2QL168GJPJ1Oz+Sd2U/22GvGxvGZZqC37dffEMcX3HQ3PpdmJiYkhISHCZlua4ajjEhMPBk7B+l/2vM5lkpv1TSduLDoVf/i0QO06xdKY0x8dd7EGN1rW2KUTf/18Bvbuo7y7SDDln9yx/7D25ybamjbHvuMyuIxDsD8vn/ZVff/2V+Ph4UlNTGTJkCE8//TTV1dVqS3cLQq4KBiBzkXruFVvBz8mTJxk+fDjffPON3UVbycnJ9OrVi7y8PNauXdv8/rZS/XTl7+4OjbLgTA9yyncwatQotwj6NYahXtDzIzuDnrIsc++/Zb7boMyz/fnfAt1iBMTbFIPZqSSamgrXVQ7bUKsHuV4vkBjf/MKiNWiG3IqtZ/nOw63r6DbfWpJ/w1ilSGD8+PHs3r2bBx98EIvFwksvvcTgwYPZssU9/H5qEnhJIHo/PcV/FlN53L7xX01RW1vLDTfcQFpaGr1792b58uX4+PjY/fqWulfOda24W+taylPd2q1i464rzwQ984ubN+Z//0jmkxXg7Qk/viJYU0Ehrn8c2fpsvAUf/vxWcrTsZmlpoNMVaIbcylk9yz9uWc9yWZb55lflcf0iIH9/f9566y02bNhAr1692L9/PyNGjODRRx+lsrLtBs9d0PvqiJqopMVltjHoabFYuPPOO/ntt9+Iioo6q+CnJdx8880ALF26tK6hVmP07ark/R/OgMpqmWKpGHBtoNNkks/0gKloH4a8a7TAxKFKlsYXq5re998LZF75GvR6WPyCwKiks+82KrtWAHDkB9f6yWtra+s6arYk0OlsNENeD1vP8qx8eN3+Jnr8uV9pnNMpDMYOPH/7yJEjSU1N5YknnkCn0/Hmm28yYMAAu2772wuxNyoDJ7IWZrUpX/app57i66+/xt/fn59++qnVfuF+/frRv39/ioqKWL16dZP7enkK9OmiDMpO22WkIr0SnZeOwETXdTw8cFLp903VUYL8ZPr37+8yLS3BFvT8aHnjQc8vVsr87b/Kts+eFLhy+Pkuo4ix4QAYd7k2WWDPnj0YjUZ69epFYGBg8y9wEZohr8fZPctlu3uW2zOX08fHh1deeYWtW7eSlJTE0aNHGTduHPfeey+lpaXq/AIuJGxsGJ4RnlSkV9bNuGwp7777LnPnzsVgMLBkyZI238q2xr1yeI3iVglMCkTn6brLo86tUpHKiBEjVG3q5kgmDYPYCDh0SpmQdS7LN8ncNVe5Xt6cLXDb5Q1fL+JtIhbZQmRJFMYK1xlzd53ReS6aIT8HW8/yymp45n/NG3KzWWl2D/b1VklJSeHPP//k+eefx8PDgw8//JDExMQWT7dxN3QGHTGTowHIWpTV4tcvXbqUBx98EID//e9/XHbZZW3WZDPk33//fbOBZpt/tvDPYsD1hUBnAp0724VbxYahiUrP9akyNz4nYzbD32+Dh29s/HrpntyNDP0pPAQPUhe1IA1GZTRD3o6p37N8RzM9y39PVeZy9ohV3DL24OnpyT/+8Q927NjBkCFDyMjIYNKkScyYMYPCwsK2/wIuImaqtThoaTYWk/1j4DZu3Mi0adOQZZkXX3yRGTNmqKKnZ8+eDB48mLKysma/KJN7KP/rDit3E6425PUrOtuTIQf4y5UCOp0yfT7PGvRMPSxz9VMy1bUw62p48S/NL3oqupYDcHjZ4Wb2dBztIdAJmiFvkO6xAg/eoPhMH/tv0wUOtk6Ht1za8s5v/fv3Z/PmzcydOxdvb2/mzZtHv379WLp0aZv0u4qgQYH4dfelNq+WgvX2fSHt37+/ruDn3nvv5e9//7uqmux1ryR1B2SZyFzXV3RaLHLditzTuI8hQ4a4TEtr6BwlMGkYGE3w+Uo4nqPnijkypRVKVtd/H7Wvf0rEGMVPXrvLMSl7zWE0GklLSwM0Q95ueeZ2gbAg+H0n/NBIpXdNrcyS9crjW8a3LsfXYDAwZ84cdu3axejRo8nJyeGGG27gxhtvbHB2oTsjCAIxU5WgZ+bC5t0rWVlZXHHFFRQVFXHNNdfw7rvvqp4rfeONSpPs5cuXU1FR0eh+ncKgn3cVASYj+jBPfLrYn+6oNsdOQ1mlALWnuWhgZ7y8vFympbXYgp7vfy8z7aVQcgqVvkZfPys0Gkc6l5TpKZhlM2ElYRhLne8n37t3L7W1tfTo0YOgINfeoTWHZsgbIThA4P9mKh+4Oe/L1BrPX5Wv3Aol5TCoZ9vncvbq1Yvff/+dd999Fz8/PxYtWkS/fv346quvHNo1TW1ipijulZyfcjFVNL6SKi0tZdKkSZw8eZJhw4Yxf/58hwT04uPjGTZsGJWVlaxYsaLR/QRB4GJfZTVu6u7qjofWB+0kf7whrrhIqcs4dhpO5hoQ+8B3/xLw8rT/fe03uB/HhKPo0bP3u30OVNsw7cU/Dpohb5J7roXeXZT84ve/P3+7LVvlFpUGSOh0Ov7617+yZ88eJkyYQGFhIbfddhtXX301GRkZqpzD0fgl+BI8JBhzhbnRjoi2gp9du3bRs2dPli9fjq+v43p+29wrzQ1mHlCrGPLscC3Q2VYMBoG7r1aui+4xJn6aKxDg27LrRBAEyrvY/ORHVNfYHJoh7yDU71n+/OcyhaVnVsZllQLLrcO+bx6n7nnj4+P5+eef+eSTTwgKCmLFihUkJiby8ccft4vVeazVvZLVQMm+LMvcdddd/Prrr0RGRrJq1SrCw8Mdqmfq1KkIgsBPP/3UZKpnpwLFkO/2cG2+sHTAGiiu2MmIESNcqqUtPH4LfPCYwKJnC4hoZW/+sDGhANSmqt+QrTnaS6ATNEPeLPV7lr/wxRkj+vN2b6prYUyyEtxRG0EQuPPOO+u6/5WWljJr1iwmTJjAsWPHmj+AC+l0XTSCQSB/bQE1eWf3yvj73//OV199hZ+fHz/99BPdunVzuJ7Y2FhGjRpFTU0NP/zwQ4P7mGsseJwswwKsq3CdIZdlGWm/4pLqE1ft9r7ZpvD2ErjnWoHIYPszmM5FvDkFo1xLUEkwtYXOM+ZGo5Fdu5S0R82QdwDq9yx/d+mZnuXfb/IG1HOrNEZMTAzff/898+fPJzw8nN9++43+/fvz9ttvY7G0/gJxJJ5hnkSMD0c2y5z+7kxHxPfee49XXnkFvV7P4sWLnXrLaivZbyx7pWx3KRhlMrz8SMvxoKrGNXc+pwugqMITTMWMH+n4Lzl3Z+BFAzkkH0KHjsPLnede2b9/PzU1NXTr1o2QEAf0nVUZzZDbQf2e5U98IJNXLLNhtxcGPUwZ6/jzC4LAzTffzL59+7j55puprKzkoYce4vrrr2fZsmVu2VWxLnvF6l757rvveOCBBwCl4OeKK65wqp4bbrgBnU7Hzz//TFFR0XnbbY2yciKCsFhgr4tueurnj48Z0z7942qi1+sp66y4ww5/77x88vbkHwfNkNuNrWf59xvggf/ImC3KVBRnzuWMiIhg/vz5fP/993Tq1AlJkrjuuuuIiIhg2rRpfPfdd1RVVTlNT1NEXR6J3k9PyY4SNiw+U/DzwgsvMHPmTOfriYrikksuwWg08t133523vXiHYsjNPRW3SpqLejXV+cfLU912tJuzCRujNE2rcaKfXDPkHZT6PcttJfmtzR1vK9deey179+7lySefZPDgwZSXlzN//nyuv/56IiIiuPnmm1m8eLFLOyzqffVEXx0FwBf3zKO6uppZs2bx9NNPu0xTU+4V27DlUDEYgLR017hW1lnvDCJ8M4iJiXGJBncjZcpgquQqAkoDqclxTn/yHTt2AO3DPw6aIW8Rtp7lAN6eMte6cMEUEhLC7Nmz2b59O+np6bz66quIokhFRQXffvstU6dOJSIigqlTp7Jw4cImi2Echfd4pZBlmHk4V191Ne+9955L87Ovv/56DAYDv/32G3l5eXXP1xbUUnmsCp2Pjl6jlFFeu1y0Ik89olySw5NcV5DkbgwZPoQD5v0AHF91wuHnM5lMpKamApoh75D4egvMvU8xRNcOr8K/hXmxjqJbt248/vjj/Pnnnxw7dozXXnuNoUOHUllZyeLFi7npppuIiIjghhtuYMGCBZSVlTlcU1lZGdNeuIUCSwEx+hg+fPxDDAaDw8/bFKGhoUyYMAGz2cySJUvqnre5VYIGBpHUS7kk0tJxeqpnYalMUWUgmCu58pIeTj23O+Pl5UVJXDHgHD/5gQMHqKqqIj4+vlW98BvDVGHCYnRMgoJmyFvILZcKpH0m8OIdJa6W0iDx8fH87W9/Y8uWLZw4cYI33niD4cOHU1VVxdKlS7nllluIiIjguuuu4+uvv3ZIC93a2lqmTJnCjtQdpPmmAlD4Y7Hq52kNDfVeqT9IIjYCQgOhsBQy8xo6guPYedj6xVGRxtgxI517cjcnbLTVT77T8X5yR/nHD75wmENT0+vceGpityEXRfFVURQ3iKL4pSiKHg1sf1IURdfPZXICA7oL+LSD9hddunThkUceYfPmzZw6dYr//Oc/jBo1itraWpYtW8att95KREQE11xzDV9++SXFxcVtPqcsy9x999388ssvREZGMvPjmQCc/u60w1YjLeG6667D09OTdevWcfq0klFzZkanUpp/7ug3Z7F6cz4APpaD9OrVy7knd3MGTR5IuaUcnzJfKk86NqDviEKgkrRSTnxykprjNei81F8/23VEURSTgVhJkkYDB4Ap52wPAAaork5DNeLi4njooYfYsGEDGRkZvPPOO4wZMwaj0cjy5cu5/fbbiYyM5KqrruLzzz9vMEXPHp555hnmzZuHn58fK1asoN/Evvj38qO2wEj+2gKVf6uWExQUxMSJE5FlmUWLFiFb5DrXiq3jYZI1fdvZhvx36xdKn7gqtx607ApGjBzBHrMyci3r15b3u28JtkCnWity2SKz9/H9YIHwm8MI7K9+wZm9Xw0jgF+sj1cB5973PQS8q5YoDccSExPD7NmzWbduHZmZmbz33ntcfPHFmM1mVqxYwR133EFUVBSTJk3i008/tbtH+vvvv89LL72EXq9n0aJFiKJo7YioZF9kLnbsBWgv9d0rFemVmEpMeEV54R2jFHkl91CM6C4nZ64cOKX0m7l4SLBTz9se8Pf3p6iT8jl0ZN8Vs9nMzp07AfUMecb8TIr/LMYrypPov0aqcsxzsTf6FALYGmeUAKG2DaIoBgEDJEl6URTFRg8giuIsYBbA7NmzmTBhQqsEG41GMjMzW/VaNXEHHWppuPbaa7n22mvJy8tj1apVrFixgs2bN7Ny5UpWrlzJPffcw8iRI7nyyiu54oorCA0NPev1RqORTz/9lNmzZwPw6quvkpSUVKdNP0oxjNkrcjh56CR6P8eMLbP3/UhJScHb25vNmzezY5Gy+vJK9CQrS/miiQ7wAMLZccBIZma+w3TUp7JaoKQ2CmQjY1JCVfm7usNnVE0d3oO94Beo2lFNRkZGi+5a7NVw6NAhKisriY2Npaamps26TSUmDjynBGijHo7E4m1p9TFjY2Mb3yjLcrP/UlJS7k9JSbnd+jglJSXl3XrbnktJSRllfSzZczy5DWRkZLTl5arhDjocqSE3N1f+8MMP5QkTJsh6vV4GZEDW6/XypZdeKn/wwQdyTk6OLMuyvGzZMtnb21sG5Oeff77B422etEVeEbpKPjXfcZpb8n5MnTpVBuQvL/1aXhG6Sj7yZnrdtspqi6wba5b1F5vlqmqLQ3XYWL4uT2a0WdaJqbLRaGzx69XS4QjU0rHs+2Xy18EL5BWhq+Syw+UO0TBv3jwZkCdPntwaieex+9G98orQVfIfV2+VLRZLW9+LRm2qva6VzcCl1seXA5vqbesBPCOK4iqgpyiKrqv40FCNiIgIZs2axS+//EJ2dnZdWb0gCPz666/ce++9dOrUifHjxzNjxgyqq6u5++67efbZZxs8XqxtDNzi8zsiugKbe6Vqj9LeINhaCATg4yXQKw7MZth33Dl6vlut5EdH+WW7PE3TXRk5aiRpRqWRVc5axwxdUTPQWbyjhJNfnEIwCCTO7efQuIddhlySpFQgRxTFDUAisEQUxQ+t226TJOkKSZKuAA5LkvQvh6nVcAnh4eHcddddrFy5kpycHD799FMmTZqEXq9nzZo1FBcXc+WVV/Lf//630Q9r9LVRCB4C+esKqM52TnVeU0yaNIkQ/1CiaqJAB0EDzw5A1WWuHHWOni17lPdkUE/XZ/a4K2FhYeRHKjmh6csd84dRK9Apm2X2ztkHMsTf25WAPv5qyGsUu7/6JUmac85T9zSwT+NOco0OQWhoKHfccQd33HEHRUVF/PDDD+zdu5fnnnuuyZWkZ4gnkZdGkLMyl9PfnSbhvnjniW4AHx8fbh9zO4YtBqpCqzD4n609qbvAwrWytVTf8Rkkx3KDQA+XjYxy+LnaMyGjQmAFVO+oRrbICDr1/jYWi0W1QOepLzMoSS3Fu5MXPf/WXQ15TaIVBGm0mpCQEGbMmMFDDz2En59fs/vH3Hh2R0RXc2m84i3cXZV23rZka2HlLid0Ts0vKKVKUC72m67s4/gTtmNSJg4mz5yHocqDsv3lqh770KFDlJeXExcXR2Rk67NLavJrOfjCIQD6vtgHQ4DjXWWaIddwGpGXRWAIMFC6q5Tyg+pehK0hukL5Yvkj9w8OHDhw1jaba2WXE0r1v/1xN+g88ZZPEh3huJF3HYHRY0azy5QKQN66lmcUNYVaFZ0H/3kIY7GJsLFhRF/rnDsszZBrOA29t57oa5QPdqYbBD3LUpWeMwdNB87riNg5EoL9oaAEsh1cx7RygzJ8o2tY64qwLiRiY2PJDlE+O8dXHFf12GoEOou2FZHxdSaCh0Diq32dVtilGXINp1I3z3PxaZfOH63JraHqVDV4wynzSRYsWHCWnvql+o7uhLjTOmx5aKKnY0/UQQgeEQxA5c4qLCb1gsNtDXRaTBalghPoNjsB/57NuxvVQjPkGk4ldGQo3jHeVJ2somhbsct02PqrhAwOJiQshAMHDrB79+6z9nFGz5WamhpOl0UDcPW4OMedqAMx5AqRTHMm+ho9pWnqdPK0WCxtNuQnPz1F6e4yvOO86f5Igiq67EUz5BpORdAJxNygGK6sha5zr9QZcjGEG264ATh/4ERdqf4Rx905bN0mIfsmATA2xXVDn9sTY8aMqcsnz9+gjt/ryJEjlJWVERMTQ3R0dItfX5NTw6GXlMh4v5f6YPBzbi2AZsg1nI6t98rpZdlYal2TN21rJRosBp01Oai+e8UZzbN++GUv6P3xMxQR4cSxge2Zbt26cdJfKaA6teqUKsdsa6DzwP8dwlRmImJCOFGTHNNPpSk0Q67hdAITAwjo54+xyEjeb+pmHtiDbJYpqet4GMyYMWOIjo4mPT297oIGSEwAQYADJ6Gm1jGr8t+s7qVesc6f4NReEQSBEKufvGJnpSqLgbYEOgs3F5K5MAudl45+LzsvwFkfzZBruIS6joiLnN8RsfxwBaZyM96x3nhHe6HX65kyRenMXN+94ucj0DMOTGbY74AJYxaLhf0nlQDn2MGaW6UlXDThIo6bjqMz6urcZG2htf5xi9HCnjnWAOdDCfgluCZ9VDPkGi4h5oZoECD35zyMpUannrvOrWLtPw5nBjMvXLjwbPeKAwOee/bsocbQD4CLxaBm9taoz+jRo0kzKX7ygjb6yWVZbrUhP/7xScoPlOMb70P3B50b4KyPZsg1XIJPrA+hI0OwVFvIXp7r1HPXnwhkY/jw4cTFxXHy5Em2bNlS93xSd+U2Oc0Bvck3bNgI/oMAGKQNBGoRiYmJpHspwcXM1W27q0tPT6ekpITo6GhiYmLsfl11VjWHX7EGOF/ui97HMe2Z7UEz5Bouo64jopPdK2cMeXDdczqdjhtvvBGABQsW1D2f7MBc8p9/3wceYfh5VdPZ+fGxdo1OpyN4eDAW2ULFrkrMleZWH6u1/vH9/ziIucJM1MRIIi+LaPX51UAz5BouI/rqKHSeAgUbC6nOqnbKOU0VJsr2lSHoBYKSz/ZL21rbLlq0CLNZMQyOcq3IssymXZUADEgwaaPdWsHQSy4i3XwEwSy0qSahNRkr+esKOP1dNjofHf1edn1/HM2Qa7gMjyAPIi+PBBmyljonp7x0VylYIKCfP3rfs2+FhwwZQkJCAqdPn2bjxo0AdI2GQD/ILYKcQvXcK8ePH6ewpgsAowc5tsVpR2XMmDHsMrbdT95S/7il9kwFZ49Hu+PT2afV51YLzZBruJSYqc7tiGhzqwSlnB9cFAShblVuc6+cVaqvYifEjRvP+McH99JW461h0KBBHDIcBCB7TesGTbQm0Hnsv8epOFKBX3dfEv4a36rzqo1myDVcSsSlERiCDJTtKaNsvzrl1k1RV9FZzz9eH5shX7JkCSaTCXBMYdCGDRvOBDp7qnfcCwkPDw8CxQBMsony3RWtyn46duwYRUVFREZGNj0T00pVRhVH/q0Mtej3al/0Xu5hQt1DhcYFi95LR6frlJJoZ6zKi6Vi4OyMlfokJyfTu3dv8vLyWLt2rfKctVQ/7ah6rpW1G/eCVxy+nmZ6ai1WWs3wS4ZzyHQQQRYo/KPl3SPrBzrtiVPse/oA5koz0ddGEXFJeIvP5yg0Q67hcurP85QtjutrUp1VTfXpGgyBBvwa6UxX371iKw5S27WSl5fHkdOKX3xgTx06FafcXGiMHj2aXXX55IUtfn1LAp15v+WR82Muej89fV9wfYCzPpoh13A5IUOD8Y7zpjqzmqItjuvJXecfHxTU5Iiw+u6V2tpa+ltL9fefgFpj279oNm3aBP4DARjcWzPibWHo0KHslfcAkPt7Xotfb69/3FxtZu8TSoCz55zu+MR6t/hcjkQz5BouR9AJxE5xfNCzoYrOhujXrx/9+/enuLiY1atX4+8r0D0GjCal70pbqe8f1wKdbcPHx4eAQf7UyrVU7q+ktqDW7tfKsmz3ivzou8epPFaFf28/4u/t2ibNjkAz5BpuQf2OiOYax3RELN5xfkVnY9TviAjq5pNrgU51GTF2BPtMewEo2GS/e+XEiRMUFhYSHh5O586dG92v8kQl6W8qAc7Euf3Qebif2XQ/RRoXJAF9/AkcEICpxETe6pbfIjeHbJYpSS0F7DPkNvfK999/T3V1tWql+hUVFWzfdRh8euJpkOkX36bDaXB2f/KW+MntDXTue+oAlmoLMVM6ETYqtG1iHYRmyDXcBkd2RCw7UI65woxPFx+8Irya3b9Hjx4MHjyYsrIyVq5cSXIP5fm2Bjy3bNmCxXsAAP27CXh6aK6VtjJixAjSzGkA5K+3vy2yPW6VnJW55P6chyHAQJ/ne7dNqAPRDLmG2xBzvdIRMe+XPIzF6nZErD9Iwl7qu1fUcq0obpWBgOZWUYugoCB8E32okquoPFJF9Wn72j00F+g0V5rZ95Q1wPlUD7yjm18AuArNkGu4Dd6dvAkbHYqlViZ7eesq9RqjoY6HzWFrorV8+XIiAirx94HsQsgtar17RQt0OoZRY0exx6jMXC3Y2Lx7xZ5AZ/p/jlJ1qpqARH+63tW4D90d0Ay5hlsR6yD3SrF0fsfD5ujatSvDhg2jsrKSn376sc2rcqPRqLTI1QKdqjNmzJh6/cmbN+SnTp0iPz+f0NBQunY9PwulIr2Co+8cAyDxtX7oDO5tKt1bncYFR9RVUei8dRRuKqIqo0qVYxpLTZQfLEfwEAgcENCi19YvDmqrId+5cyeVVWbw7YdOdyYTRqPtjBo1qq6BVv765htoNRXolGWZvU/sx1IrE3tLDKFDQ9QXrDKaIddwKzwCDURdoTTnzlqiTk55SWoJyMqsUL13y5r/T506FUEQ+Omnn+gVq/heW5u5smHDBvDrD4KB3p2VUXIa6hAZGYlnDw/KLGVUn6qm8kRlk/s35VbJXp5D/toCDEEG+jzXPiZ+aIZcw+2o64i48PRZY9daS2v84zZiY2MZPXo0NTU1FGWsB1o/ZELxjyvDCzS3ivqMHjua3SYle6U590pjgU5TuYn9TysdFXs/09OuDCd3QDPkGm5HxLhwPEI9KD9QTtnetndELGlgIlBLsLlXtq39BIB9x8FoatkXjCzL1ta1AwEt0OkIRo8ezS5jKtC0IW8q0Hnk30epzqomMDmQLjPcO8BZH82Qa7gdOk8dna5VOiJmLW6be0WWZYp3FAMN9yC3hylTpqDT6Vizehnx0WZqjXCwhaX6Bw4coKCgAI+QoYC2IncE5w6aaOxuLjMzk9zcXIKDg0lIODMwufxgOcf+exwE6P9aXwR9+/my1Qy5hlsSe6PiXslafBrZ3Hr3SnVmNTU5tXgEG/Dr7tuqY0RGRjJu3DiMRiMhnhlAywOeGzZsAPRYvBMBbdiyI+jSpQu6OIEiSyE1ObVUHK5ocL+GAp2yLLP38f3IJpnOt8e1+u7NVWiGXMMtCR4SjE9XH6pP17Sof8a51B+03Ja5mDb3SknmBqDlAc8NGzaAb2/MeBIfDSEB7We1154YPWY0acam/eQNuVVOL82mYGMhHqEe9H6m/d0u2W3IRVF8VRTFDaIofimKoke9568WRXGrKIobRVF8yzEyNS40BEEgduqZVXlrsQ2SaK1bxcb111+PwWDg+N4fgJYHPBX/uBbodDRjxoxhlykVaNyQnxvoNJaa2P+sEuDs849eeIZ6Ol6oythlyEVRTAZiJUkaDRwAptTbvAsYKUnSKCBSFEVRfZkaFyIxU5TioOwfcjBXmVt1jLZkrNQnNDSUyy67DEtZKtAy10pGRgbHjx/HM1Txj2uBTsehBDytfvKNhQ0OKjl3RX547hFqcmoIFoOIm978uDd3xN4V+QjgF+vjVcBI2wZJkk5KkmSy/lgLOKYHaQdi0aJFJCYmotPpkCTJ1XLcFv+efgQNDMRUZiL3l5Z3RLQYLZTssnY8HNw2Qw5W90r1UXRUkZUP+cX2uVcU/zj4R40GNP+4I+nduzfmUBO55hyMRcbzsp6ysrLIzs4mMDCQbt26UbqvjBMfnQSd0qK2qYEj7ozBzv1CANv9bQlwXi9HURSHAJGSJO1o6ACiKM4CZgHMnj2bCRMmtFwtSplzZmZmq16rJm3RERERwfvvv88TTzxBbm5uq4/TEd6L5vC7zJeS1FLSvzyKRWx6VX6ujsp9VViqLXh28SSvKg/aKHHIkCF4enpQW7oLAoexZms+I/ufP8jgXB2rVq0CoEJQWih2CsgmM9Px650L4fPREEMuGsKudbuYoL+Moz8eIyI0vE7D6tWrAUhMTCQrK4v0B48hm2XCbg6lPLyM8kzHDgBvy3vR1HBoew15MRBofRwEnOV8EkUxDvgPMLmxA0iS9BHwkfXHVqchZGZm2jXtujXMmzeP119/HUEQSEpK4oUXXuDOO+8kPz+fiIgIPvvsM7p06cLMmTOxWCwcOnSI3NxcPv30U+bNm8cff/zB0KFD+fzzzwHw9/fn7rvv5pdffiE6OpoFCxYQERFRp9/Ly8vu6d0N4cj3wl10hM8MJ+v1bMo2lhPhE9Gk//JcHSd+VnIEwy8KU0VfbGwskyZN4vs9aRA4jKyScGJjz1/Bnatj586d4J1AjdmHqFAY3L9Tm7XYw4Xw+WiIyy67jGW/LmeC12WYdpuJjY2t03D8+HFAaX3LRoGKnZV4Rngy+KWBeAR5NH1gFXDUe2Gva2UzcKn18eXAJtsGURQDgAXAPZIk5aohShCERv/FxcU1ub2pf02xd+9eXnzxRdasWcOuXbt46623eOCBB5gxYwZpaWlMnz6dBx98sG7/kpIS/vjjD958802uueYaHnnkEfbu3cvu3btJTU0FlCECoiiyd+9exo4dy/PPP6/G23NB4RXlRfjFYchGmdPLWtYRUS3/eH1uuukmqFB8sGlHm1+PFBUVsWfPHgzBFwFaoNMZ1B80UbipEIvpzN1PXaCzr8iB/zsEQJ//6+UUI+5I7DLkkiSlAjmiKG4AEoEloih+aN38MJAAvCuK4u+iKI51hFBHs2bNGqZOnUp4eDigBLf++OMPpk2bBsBtt92mZB5YufTSSxEEgQEDBhAVFcWAAQPQ6XQkJibWfevrdLq6tLVbb731rNdr2I+tI2LW4pZ1RHSEIb/qqqvwNCkG4M99zc+H3LRpE7IsE91jIgCDNf+4w0lKSsIYUEuGOQNTuZlS62QoOBPo7LKtK7V5tYQMCyb2phhXSVUNe10rSJI055yn7rE+/wLwgpqimuqv4S63i56eyi2+TqfDy+tMPwadTofJZGrwNW3JY76QiZoUid5XT9GWYipPVOLbtfnCHmOJkYrDFeg8BQL6Bza7v734+/szaUwc3+fCgZM6TCYZg6Hxv6st0OkZOhSKYFBP7TPgaPR6PSNHjiRt3S7i9HHkbyjE50YvsrOzycrKYkBAEiVLSxH0Aomv9esQ16VWEGRl3LhxLFq0iIICpQVmYWEhI0aMYMGCBQB8/fXXjB49ukXHtFgsLF68GIBvvvmGUaNGqSv6AsHgbyBqkrUjop055bZBy4FJgei91P2Y3zbtWqg+hsli4HBG0/va7sKKapWe15prxTk0lE++fft2BAQeDHwILNB1VhcC+7WsrbG7ohlyK4mJiTz99NOMHTuW5ORkHn30Ud555x0+++wzkpKS+PLLL3nrrZbVO/n5+bFt2zb69+/PmjVr+Mc//gHAd999R1xcHH/88QdXXnkll19+uSN+pQ5FXUfERfZ1RHSEW8XGxIkT0VfvA+CXzY377auqqvjzzz/BsxNFFV4E+UO39n8X3y4YM2YMu60VnkVbi7DUWti+fTsTvC4npioWrygvej7ew8Uq1cNu18qFwIwZM5gxY8ZZz61Zs+a8/T7//PO6FKL4+Hj27Nlz1rb6vPHGG+e9fvLkyUye3GiCj0YDhF8chme4JxWHKyhNKyMouWl3SXEbOx42hY+PD327mNhTCt/9coSHpkU3uN+2bdswGo0kpFzPMWBgD8295ixEUaTGq4ZjpmMkVCdQmVbF3q17melzBwB9X+iNR2DHMX/ailyjXaAz6Og0WTGYzY2Bk2WZEtuwZQesyAGuukRpcbrjYOO57Tb/eGQ35Y5LC3Q6D09PT4YNG1bnXinfWk6C1J0gXRDeg73odH3DX77tFc2QO5Dy8nJXS+hQ2HqvnF7adEfEqhNV1BYY8QzzwKerj0O0TJ+cBECZJZ79+/c3uI/NP27xHQhogU5nUz8NMf+7AkYZR2OSjaS8ff54t/aOZsg12g1Bg4Pw7eZLTU5tk3MZbW6VoDZ2PGyKvl090As14NWZz79adt52s9nM5s2bAcguiwK0QKezGT16NHtMu7FgwZxrQSfo+DN8G0F91ctichc0Q67RbjirI+KixrNXih3sVgHQ6wV6dFJmeM5fvu+8AOyuXbsoKyuja/eBnMoz4O0Jfbo4TI5GAwwfPpwafQ1HTIcByDPnUTmh4R7l7R3NkGu0K2KmKIY8+8cczJUN+6cdmbFSn5EDldS1UwVB7N69+6xtNv947xSlICypO03mm2uoj5+fHykpKayp+Q2jbOS9yncYOGygq2U5BM2Qa7Qr/Lr5EZwShLnCTM6q8ztCmGsslO5WGh+p0fGwKQb21FtFJfHtt9+etc3mHw+KvRjQAp2uYvTo0Syv+YEpRZP507jtvBmdHQXNkLuAOXPm0KdPH5KSkpg8eTLFxcWultSuqJ9Tfi5le0qx1Fjw6+nn8P4Zyd2tD/ySWLBgQZ17RZbluhV5lb4voAU6XcWYMWMAMGHC19eX3r17u1iRY9AMuQuYMGECe/bsIS0tjV69evHyyy+7WlK7otPkTgh6gfw1+dTkn93vxFluFYABdYa8P0ePHq/r43Hs2DFycnKIjIwkPVtxv2iBTtcwatSouoD3wIED0ev1LlbkGDRDXo958+aRlJREcnIyt912G8ePH2fcuHEkJSUxfvx4Tp5U2qLOnDmTp556imHDhtGtWzd+//137rzzTvr27cvMmTPrjufv788jjzxCYmIi48ePJy9PGY5w2WWXYTAoxQjDhg0jI6OZOm+Ns/AK9yR8XBiySeb099lnbXOmIQ8JEOgSBeh8wKdHnXtl27ZtAAwbOZ6Dp0CvhwHdHC5HowFCQkLo378/QId1q4CbGnJhjKXRf3G3dGpye1P/msJVbWw//fRTJk6cqOr7dyFQ1xHxnOIgW48VZ01BT6pblSezcOFCLBaLUpYPJCReg8UC/bqCt5fmWnEV1113HUCHboXhlobcFbiije2//vUvDAYD06dPd8Jv2LGImhiJ3k9PsVRCxbFKAEzFJiqPVqLz1hHQz98pOmyGPKDTSE6ePMmWLVvYunUrAN7hwwEt0Olqnn32WX7//XeuvPJKV0txGG7ZbEBe3/j3S0dpY/v555/z448/8ttvv3W4KjNnoPfVE31VFJnfZpG1OIuec3pQubsKgKDkQHQezlmjJHcXAJmI+PGU7Ya33nqL48eP4+/vT15VHKAFOl2Nh4cHPXp0nAZZDaGtyK04s43tqlWrmDt3Lj/88AO+vs331tZomHM7IlbuVlbmzvCP27CtyCsF5cHChQsBpRgl9YhiwLVAp4ajccsVuSuo38ZWr9czaNAg3nnnHe644w5ee+21upmdLcHWxvbFF18kMjKyLhg2e/Zsampq6gZQDxs2jA8++ED136mjEzY6FK8oTyrTKynZWVq3IneWfxygRyx4e0J2kRdduydzIl3p7TF85FheWa/sM1Az5BoORjPk9XBWG9sjR46ooFZDZ9DR6fpOHH//BJkLs84YctF5K3KDQaB/NxnpAIy6/H5O/PceADr3uoza3xRDH+inuVY0HIvmWtFo18RaS/ZPfZWBudSMV5Qn3rHeTtWQZE0t7Nx7EqD4ZE0+SnfEQVqgU8MJaCtyB6K1sXU8gcmB+PX0o+Kw0gwpeLDjOh42RpI14JlfHcPrr7+OTqdj3wmlqnSwFujUcALailyjXVO/IyJAkBMDnTaSrQkRaenw2GOPceONN7LjkPKctiLXcAaaIddo99g6IoJzM1Zs2DJX9hwDs1nGYoFUaxhEy1jRcAaaIddo9/h29SX25hh8+noTMiTY6ecPDRSIi4DKajiaBcey9VRUQWwERIZorhUNx6P5yDU6BMnvDSAzMxO9j2uaIiV1h4w82JUOuXmKf1xbjWs4C21F7gKeffZZkpKSGDhwIJdddhlZWU0PE9Zwf2zulbR0mT3HrYFOzT+u4SQ0Q+4C5syZQ1paGqmpqVx11VX885//dLUkjTaS3ENxoaSlw55jthW55lbRcA6aIa+Hs9rYBgaeGf5aUVGh9VrpANhW5LuOoK3INZyOW/rIfwr7ucntu9jT5PbGmFTQeBtLWxvbzZs3Ex4eTmFhYV2l54wZM/j000958MEH+f7774EzbWx/+OEHrrnmGjZt2sT//vc/hgwZQmpqKgMHDqxrY/vmm2/yz3/+k+eff553330XgKeffpp58+YRFBTE2rVrW/X7aLgPveLAyxOOZwPoCA2EzpGuVqVxoaCtyK04u43tv/71L06dOsX06dPrjLtG+8VgEEiMP/PzoJ5od1oaTsMtV+RNrZw7ShtbG9OnT2fSpEkNDp3QaF8kdaeuEEhzq2g4E21FbsWZbWwPHz5ct8+yZcvo06ePGr+ChotRSvUVtECnhjNxyxW5K3BmG9snn3ySgwcPotPp6Nq1q9bCtoOQ3P3MY21FruFMBFmWXXHeVp/UXVwr9ujw9/d3aOOs9vReXAg6CkpkOk2W8fO2kL9cj17v2lW5q98Pd9LhDhpU0NHoB8ruFbkoiq8CI4DjwJ2SJBmtz+uBj4GewHZJkh5urUoNjfZMWJDAqtegsrwQvV5LWdFwHnb5yEVRTAZiJUkaDRwAptTbfBWQZd3mJ4ricPVltk+0NrYXHuNSBAb1MLpahsYFhr3BzhHAL9bHq4CRdm7T0NDQ0HAw9hryEKDU+rgECLVzm4aGhoaGg7HXR14M2OrKg4BCO7fVIYriLGAWKMOHbYOHW4rRaKybl+lK3EGHO2jQdGg62oMOd9DQVh1NBUntNeSbgUeBecDlwKZztl0KrLduazBHT5Kkj4CPrD9eEFkrF4IGTYemoz3ocAcNjtRhl2tFkqRUIEcUxQ1AIrBEFMUPrZt/BLpYt1VLkvSH6io1NDQ0NBrF7vRDSZLmnPPUPdbnTcBMFTVpaGhoaLQArURfQ0NDo53jqspODQ0NDQ2V0FbkGhoaGu0czZBraGhotHM0Q66hoaHRztEMuYaGhkY7RzPkGhoaGu0czZBraGhotHM0Q66h0c4RRdGr+b0criFIFEVtvp2LcLtRb6IoGoDbgP2SJG1xoQ4PoLskSQdEUdRJkmRxkYZpwCFXtj6w/k0eBo4C6yVJynehlgeBAkmSvnbx3+V54A9JkpY7+/z1dBiAl4FsURQ/kCSpwkUangGSgAeBDGdrqKfjKeAksFGSpHQX6rgFOChJ0jZnndetVuSiKEYCS4CBwL2iKN4iimKIC3SIKF0cl7nQWIwFtgFDgYdEUbxUFEWn/71EURwGbAW6WbVcaf2wOh1RFKOBycB9oih6u+jvMgrYB+QDa5x9/no6PIBHgDzgPRcZ8UuA34A4IAJwugarjs7Az0AYEADcLYpisAt0dAF+BQZZNdzkLB1uYcjr3RpWAZmSJD0EvApEc/Y0Ikfr8LQ+zAOGowzK+It1m1NuG+tp8ALulSTpfmAFMF6SJIsLdBSgjPa7H1gORFj76ziFejqQJCkbWAxkoayIcdaXWz0dBmAdcBCYKYriVGe6Nmznso5a7ArsB14XRfFxURTHOEmDZ70fH5Ak6S8oXVD/4ozz19Nhe9+NwGHrmMkvUJr3FbtAhw7lLu1R4L/ARcAkZ2hwaYm+KIrhKLeGBSjf7NuBh1D+GCdQjOnlwCeSJB13ko61kiT9bH2+H/AucLMkSbmOOv85GgqBXyRJ+q3ets7AG8A026xUJ+lYU++98AE+QZnN+iGwTpKkw07QUQD8LknSKlEUo1Catb2D0nVzqiRJWY7ScI6OQpRJWGuBJ4A7gBeBq63Pfe9ILee8H+slSfpJFMUHUCZyfYsymHcSyrXiEDfcOe/Fr5Ikra63bTLQCfjA0XdK57wXv6Lcud6OcmfQGeWO/k2UGcI7naTjZyAXpYHgW5IkZYii+KX1udclSTrtKB3g+hX5FJTBFAtQDHgCyq3REBRtR1A+oJ6NvF5tHfNR3BgXiaLoKUnSPpSVxiMOPn99Dd8Aj1k12P4+w4ETjjbiDeh4qJ6OTih3BndZHw9zko75wIOiKF4kSVIOyu1zMfApsFAUxRlO1PEokGJ9PECSpHnA/wFdgGAn6vir1f1XhfJ+HJIkaSnKTIChTtDwDfCI7TqxbgsCelrvGh1tV+rbjUeA3sD7gNn6/DCUmQc3OVHHo4A3UI3iFp4L+KPYsUoH63CdIbf+saOABdZ+54tRDLiEcrGMt95KdwZ8naRjF8qFMh7lSwVJkp4BkkVRfFIUxfsd4fNqQkNP6y4G4HtRFK8VRfFeURT91NbQhI5LgXhJko5KkvS1JElpgA+Q7QgNTei4TBTF/sBxYAbK6rM/4LDVXwOf0W+BcYCnJEk1AJIk7UWJH3g4SccuYBHKSvw4yt2A7fa9G8qdrDM02D6j8dZdFgHDRFHs4sgVeSN/k8uB7sBpYKskSWUoU8uOOFHHEpT5xd8DXwFpwPUobp8AR+mw4RJDLoqiYP1jl6Ks8ACWoqxsjgDLgFGiKP6EcoE4xGg0omMZyhvfybqPL9ALmIji/yp2ogbbKJEbgK9Rbh+3OSKw1YQOfxRfLKIoDhBFcTHQAzimtoZmdPiirHi8UAzIoyirzz8dETdoRMf3KCvPWOs+Q0RRXIayMnbIrXMT10oUykrvUyBAFMVFgIgSiHWGhnM/ozKwsd7PqtPE38QXZVbwcaCnKIqrgYsBh2S9NfE36QQES5J0AOXu6CuUz2yxI3TUx+GGXBTFEFEUvxBFcYL1Z71tmyRJbwL9RVEcJklSKbAXuEOSpE3AP4EXJEmaZl2ZO1NHGjDdujkQeEKSpLFt9be1QsM06+a9wBxJkm6QJGlHWzS0Usct1s19gEWSJN0oSVKbVzst1LEPuFWSpFckSbpNkqQTKAGuA5IktSnQ04r342bbS4GFkiTNUCOG0kIde4CZ1mvjeeD/JEm6SpKkg07UUP8zWgt8rpZ/vhV241ZJkn5EiSX9W5KkayVJ2uNkHWmcced0ApZLkvSgJEnlbdXRHA4NdloDZI+jrKBMkiSNq7dNL0mSWRTFKSi+phex3rYCb0uSVOtiHV7Am2plaLRSg7ckSXPVOH8bdXhJkvSaG+jwRAkkGa2rojZ/eNv536Xu/XCxhv+odZ20QYeXVYdqcaQ26Pi3I91LDeGQFbmoEC1JUhVKjusY4ISoRNnr3gQASZIWo9yC3AKMAr5Wy4i3UcdXanw426jhy7aeXyUdX7mLDtuFqsIqvCP8Xb5Ww3CpoEGtxY4qnw030OH0+gZVV+TW2473gL4oPtRNkiR9bN3WE8XPO1GSpIJzV1T135yOoMMdNGg6NB3urkHToQ5qr8h9gUJJksai+LgniqLYR1SqIw+jFNg8aN03sv4LVX4T3EGHO2jQdGg63F2DpkMF1DbkOmCMKIpdJUk6ihK5vdF2qyFJ0j+AB0RR3AUkqnxud9PhDho0HZoOd9eg6VCBVhtyURSDzvnZQ5KkEpQ0nOesT38KjBVFsa91n/tQUpRukSRJlT4V7qDDHTRoOjQd7q5B0+FAZFlu0b+UlJTolJSUZSkpKe+mpKTEW5/TnbPP6pSUlEusj59MSUm52vo4uKXnc2cd7qBB06HpcHcNmg7H/2txsFMUxetRKql2AyWSJH1pfV4P/BvlG6sKJb80GyWSe4v1VkU13EGHO2jQdGg63F2DpsPx2GXIRVG8EQgBvpQkqdL63BXAYGCVJEk7RFEcilJ5+KwkSYWiKHZCKVldL0lSnhpi3UGHO2jQdGg63F2DpsO5NGnIRaX0+WGUJjQHUZrSbJAkaY31F70eQJKk9855nao9vN1Bhzto0HRoOtxdg6bDNTQZ7JSUPMkAlG+yf6D0LrhdFEVfSWnLuBOQRVGcIoriJFDePLXfBHfQ4Q4aNB2aDnfXoOlwDfZkrRwAIkSledSvKInytl4TO1G6472MtVmOpELZtBvrcAcNmg5Nh7tr0HQ4GXsM+V6UBjC9JCXpfTtKRy+ACSjdCi+WrBVQDsQddLiDBk2HpsPdNWg6nIw9sxf3AaOBy0VRPIbSQN/WD/snSZJ+cJA2d9ThDho0HZoOd9eg6XAyza7Irbca81DmJH6C0n83zbrNabMb3UGHO2jQdGg63F2DpsP5tCiPXBTFRJSxUs4YOebWOtxBg6ZD0+HuGjQdzsGlw5c1NDQ0NNqOQ/qRa2hoaGg4D82Qa2hoaLRzNEOuoaGh0c7RDLmGhoZGO0cz5BoaGhrtHHsKgjQ02iXWsuzHgeOSJH0uiuJM4DNgjiRJr7tUnIaGimgrco2OjC/KtJeZ1p/XoUw7X+4qQRoajkBbkWt0ZCTr/2NFUZSBE0BXYA5wUBTF40A48AVwK8pQgXeBj1CujTskSVoliqIn8BLKl4AfsBq4X2oHfao1Lgy0FblGR+bv1v/3oxjhhtwptr4bfwCTgPeB11CmpL9i3fYU8BjKSv4/wETgA4co1tBoBZoh1+jI/GL9P1eSpAVAeQP7WIBHgCXWn7+UJOltlN4cCdbnrrL+fw+Kq8YPpXOehoZboLlWNDoy9vSfqJIkqVYURVv/jRLr/2ZAX28/E4pBN1t/1hZBGm6D9mHU6MiUoqy4e4iiOB3FP94afkRZ9MwAugBXoKzONTTcAs2Qa3RYrF3uXkPpQf0VZ1bTLeVl63FGowRDJ6JkwGhouAVa90MNDQ2Ndo62ItfQ0NBo52iGXENDQ6OdoxlyDQ0NjXaOZsg1NDQ02jmaIdfQ0NBo52iGXENDQ6OdoxlyDQ0NjXaOZsg1NDQ02jn/DzkpoWVIjcn3AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "np.random.seed(0)\n", + "series = TimeSeries.from_times_and_values(\n", + " times=pd.date_range(start=\"2020-01-01\", periods=10, freq=\"h\"),\n", + " values=np.random.random((10, 3)),\n", + " columns=[\"comp1\", \"comp2\", \"comp3\"],\n", + ")\n", + "series.plot()" + ] + }, + { + "cell_type": "markdown", + "id": "bd068f2c-bc9c-45c7-b757-a7feae6449d5", + "metadata": {}, + "source": [ + "### 1. Defining static covariates\n", + "Define your static covariates as a pd.DataFrame where the columns represent the static variables and rows stand for the components of the uni/multivariate `TimeSeries` they will be added to.\n", + "\n", + "- The number of rows must either be 1 or equal to the number of components from `series`.\n", + "- Using a single row static covariate DataFrame with a multivariate (multi component) `series`, the static covariates will be mapped globally to all components." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "90431347-dca7-4dee-b7e4-ef4911da605c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " cont cat\n", + "0 0 a\n", + " cont cat\n", + "0 0 a\n", + "1 2 c\n", + "2 1 b\n" + ] + } + ], + "source": [ + "# arbitrary continuous and categorical static covariates (single row)\n", + "static_covs_single = pd.DataFrame(data={\"cont\": [0], \"cat\": [\"a\"]})\n", + "print(static_covs_single)\n", + "\n", + "# multivariate static covariates (multiple components). note that the number of rows matches the number of components of `series`\n", + "static_covs_multi = pd.DataFrame(data={\"cont\": [0, 2, 1], \"cat\": [\"a\", \"c\", \"b\"]})\n", + "print(static_covs_multi)" + ] + }, + { + "cell_type": "markdown", + "id": "127de718-dda2-4423-a02a-b354ef43ba08", + "metadata": {}, + "source": [ + "### 2. Add static covariates to an existing TimeSeries\n", + "Create a new series from an existing TimeSeries with added static covariates using method `with_static_covariates()` (see docs [here](https://unit8co.github.io/darts/generated_api/darts.timeseries.html#darts.timeseries.TimeSeries.with_static_covariates))\n", + "\n", + "- Single row static covarites with multivariate `series` creates \"global_components\" which are mapped to all components\n", + "- Multi row static covarites with multivariate `series` will be mapped to the component names of `series` (see static covariate index/row names)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "80e40a2e-508d-4494-a066-fa70c1cd520f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Single row static covarites with multivariate `series`\n", + "static_covariates cont cat\n", + "global_components 0.0 a\n", + "\n", + "Multi row static covarites with multivariate `series`\n", + "static_covariates cont cat\n", + "component \n", + "comp1 0.0 a\n", + "comp2 2.0 c\n", + "comp3 1.0 b\n" + ] + } + ], + "source": [ + "assert series.static_covariates is None\n", + "\n", + "series_single = series.with_static_covariates(static_covs_single)\n", + "print(\"Single row static covarites with multivariate `series`\")\n", + "print(series_single.static_covariates)\n", + "\n", + "series_multi = series.with_static_covariates(static_covs_multi)\n", + "print(\"\\nMulti row static covarites with multivariate `series`\")\n", + "print(series_multi.static_covariates)" + ] + }, + { + "cell_type": "markdown", + "id": "638f47fb-282c-45d5-8708-42af483912a3", + "metadata": {}, + "source": [ + "### 3. Adding static covariates at TimeSeries construction\n", + "Static covariates can also directly be added when creating a time series with parameter `static_covariates` in most of `TimeSeries.from_*()` methods." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2106e10b-8a32-4e48-9d3c-2c77bfab4473", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "static_covariates cont cat\n", + "component \n", + "comp1 0.0 a\n", + "comp2 2.0 c\n", + "comp3 1.0 b\n" + ] + } + ], + "source": [ + "# add arbitrary continuous and categorical static covariates\n", + "series = TimeSeries.from_values(\n", + " values=np.random.random((10, 3)),\n", + " columns=[\"comp1\", \"comp2\", \"comp3\"],\n", + " static_covariates=static_covs_multi,\n", + ")\n", + "print(series.static_covariates)" + ] + }, + { + "cell_type": "markdown", + "id": "9caa4638-35d0-4397-98eb-672b18d94115", + "metadata": {}, + "source": [ + "### Using static covariates with multiple TimeSeries\n", + "Static covariates are only really useful if we use them across multiple TimeSeries. By convention, the static covariates layout (pd.DataFrame columns, index) has to be the same for all series." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b6af377e-e0b8-4e36-9df7-d625789b95ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Valid static covariates for multiple series\n", + "static_covariates ID var1\n", + "global_components SERIES1 0.5\n", + "static_covariates ID var1\n", + "global_components SERIES2 0.75\n" + ] + } + ], + "source": [ + "first_series = series.with_static_covariates(\n", + " pd.DataFrame(data={\"ID\": [\"SERIES1\"], \"var1\": [0.5]})\n", + ")\n", + "second_series = series.with_static_covariates(\n", + " pd.DataFrame(data={\"ID\": [\"SERIES2\"], \"var1\": [0.75]})\n", + ")\n", + "\n", + "print(\"Valid static covariates for multiple series\")\n", + "print(first_series.static_covariates)\n", + "print(second_series.static_covariates)\n", + "\n", + "series_multi = [first_series, second_series]" + ] + }, + { + "cell_type": "markdown", + "id": "18b91481-969b-449c-9f90-3ee749a93f43", + "metadata": {}, + "source": [ + "### 4. Extract a list of time series by groups from a DataFrame using `from_group_dataframe()`\n", + "If your DataFrame contains multiple time series which are stacked vertically, you can use `TimeSeries.from_group_dataframe()` (see the docs [here](https://unit8co.github.io/darts/generated_api/darts.timeseries.html#darts.timeseries.TimeSeries.from_group_dataframe)) to extract them as a list of TimeSeries instances. This requires a column or list of columns for which the DataFrame should grouped by (parameter `group_cols`).\n", + "`group_cols` will automatically be added as static covariates to the individual series. Additional columns can be used as static covariates with parameter `static_cols`. \n", + "\n", + "In the example below, we generate a DataFrame which contains data of two distinct time series (overlapping/repeating dates) \"SERIES1\" and \"SERIES2\" and extract the TimeSeries with `from_group_dataframe()`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "643fe7fd-0442-42ef-9109-0b58b720edd4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input DataFrame\n", + " dates comp1 comp2 comp3 ID var1\n", + "0 2020-01-01 0.158970 0.820993 0.976761 SERIES1 0.50\n", + "1 2020-01-02 0.110375 0.097101 0.604846 SERIES1 0.50\n", + "2 2020-01-03 0.656330 0.837945 0.739264 SERIES1 0.50\n", + "3 2020-01-01 0.138183 0.096098 0.039188 SERIES2 0.75\n", + "4 2020-01-02 0.196582 0.976459 0.282807 SERIES2 0.75\n", + "5 2020-01-03 0.368725 0.468651 0.120197 SERIES2 0.75\n", + "\n", + "2 series were extracted from the input DataFrame\n", + "Static covariates of series 0\n", + "static_covariates ID var1\n", + "global_components SERIES1 0.5\n", + "Static covariates of series 1\n", + "static_covariates ID var1\n", + "global_components SERIES2 0.75\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAEXCAYAAACwHc/gAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA7DklEQVR4nO3dd3gU1f7H8femEUoICSWEhCpFitSjCFIURIooKgFEBaQIonC9AqKC5YKoqFxFKQJKkSItUbEjgv7wIihHmoCFXpbQAgQCJKTM74/ZhABJ3A27md3k+3qePOzO7M5+djN8c/bMmTM2wzAQQgjhu/ysDiCEEOL6SCEXQggfJ4VcCCF8nBRyIYTwcVLIhRDCx0khF0IIH2dVITfy+3P06NF8P9eTP5JLckku7/kppLly5XMt8vT0dKsj5EhyuUZyuUZyuaao5fK5Qi6EEOJKUsiFEMLHSSEXQggfJ4VcCCF8nBRyIYTwcVLIhRDCx0khF0KIAnDo0CF+/fVXj2xbCrkQQnjYpUuX6NGjBz169OCTTz5x+/alkFtg+fLl1K9fHz8/P7TWHn2tUaNGsXPnTrdsKyUlhV69elGzZk2aN2/O/v373bJdIQq7kSNH8ssvvxAREUGbNm3cvn0p5BZo0KABn3zyiUd+odmlp6czadIk6tWr55btzZ49m7CwMHbv3s3TTz/Ns88+65btClGYLVmyhKlTpxIYGMjMmTMpV66c21/DKwu5zWbL9Sc6OjrP9Xn9/JP58+fTsGFDGjVqRJ8+fdi/fz/t2rWjYcOGtG/fnoMHDwLw6KOPMnToUG699VZq1KjBjz/+yMiRI6lbty6PPvpo1vZKlSrF008/Tf369Wnfvj0nTpwAoG7dutSpU8epz2LHjh3ccsstNG7cmIYNG7Jr1y4AFi5cmLV8yJAhWaf+lipVipEjR9KoUSPWr19PTExMVqv/u+++o0WLFjRt2pQePXqQlJQEwHPPPUe9evVo2LAho0aNyjXLihUr6NevHwAxMTGsXr0auVSgELnbuXMngwYNAmDy5Mk0adLEMy9kGIYVP3nCQxPW5GX79u1GrVq1jBMnThiGYRgJCQlG165djXnz5hmGYRizZ882unXrZhiGYfTr18/o1auXkZGRYXz22WdGSEiIsWrVKiM9Pd1o2rSpsXnz5qz3sXDhQsMwDGPcuHHGk08+ecVrtm3b1ti4cWOeuYYNG5a1jZSUFOPChQvGzp07ja5duxqXLl0yDMMwhg4danz00UdZr7l06dKs5996663Gxo0bjRMnThitW7c2kpKSDMMwjIkTJxrjxo0zTp48adSuXdvIyMgwDMMwTp8+nWuW+vXrG4cOHcq6X6NGjazPy1WHDx/O1/M8TXK5RnLl7ty5c0bdunUNwHjooYeMjIyM682Va00N8Myfh+tj5NHKs9vtREVFuf0116xZQ48ePbK+9oSHh7N+/fqsAxN9+vRh9OjRWY+/5557sNls3HTTTURERFC3bl38/PyoX78++/fvp3Hjxvj5+dGrVy8AHnnkER544AGXc7Vo0YJXX32Vw4cP88ADD1CrVi1Wr17Nb7/9xs033wzAxYsXqVChAgD+/v507979mu1s2LCBnTt3cttttwHmwZcWLVoQGhpKcHAwAwcOpGvXrnTt2tXljEKIKxmGwWOPPcYff/xBvXr1mDVrllO9AvnllYXcFxQrVgwAPz+/rNuZ99PS0nJ8Tn5+kQ899BDNmzfnq6++okuXLsycORPDMOjXrx+vv/76NY8PDg7G39//muWGYdChQwcWL158zbpff/2V1atXExsby9SpU1mzZk2OWaKiojh06BDR0dGkpaWRmJhI2bJlXX5PQhR206ZNY8mSJZQqVYq4uDhKlizp0dfzyj5yK7Rr147ly5eTkJAAwKlTp2jZsiVLliwBYNGiRbRu3dqlbWZkZBAbGwvAxx9/TKtWrVzOtXfvXmrUqMG//vUvunXrxrZt22jfvj2xsbEcP348K+uBAwfy3M6tt97KunXr2L17NwDnz5/n77//JikpicTERLp06cI777zD1q1bc93Gvffey0cffQRAbGws7dq182grQwhftGHDBkaMGAGYAwRuvPFGj7+mtMgd6tevz9ixY2nbti3+/v40adKEKVOm0L9/f9566y3Kly/P3LlzXdpmyZIl+fXXX5kwYQIVKlRg6dKlAHz66acMHz6cEydOcPfdd9O4cWNWrlyZ4zaWLVvGggULCAwMpGLFiowZM4bw8HAmTJjAXXfdRUZGBoGBgUybNo2qVavmmqV8+fLMmzeP3r17k5KSAsCECRMICQmhW7duJCcnYxgGb7/9dq7bGDhwIH369KFmzZqEh4dn/ZETQphOnjxJz549SU1N5V//+hc9e/YskNe15dUf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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# generate an DataFrame example\n", + "df = pd.DataFrame(\n", + " data={\n", + " \"dates\": [\n", + " \"2020-01-01\",\n", + " \"2020-01-02\",\n", + " \"2020-01-03\",\n", + " \"2020-01-01\",\n", + " \"2020-01-02\",\n", + " \"2020-01-03\",\n", + " ],\n", + " \"comp1\": np.random.random((6,)),\n", + " \"comp2\": np.random.random((6,)),\n", + " \"comp3\": np.random.random((6,)),\n", + " \"ID\": [\"SERIES1\", \"SERIES1\", \"SERIES1\", \"SERIES2\", \"SERIES2\", \"SERIES2\"],\n", + " \"var1\": [0.5, 0.5, 0.5, 0.75, 0.75, 0.75],\n", + " }\n", + ")\n", + "print(\"Input DataFrame\")\n", + "print(df)\n", + "\n", + "series_multi = TimeSeries.from_group_dataframe(\n", + " df,\n", + " time_col=\"dates\",\n", + " group_cols=\"ID\", # individual time series are extracted by grouping `df` by `group_cols`\n", + " static_cols=[\n", + " \"var1\"\n", + " ], # also extract these additional columns as static covariates (without grouping)\n", + " value_cols=[\n", + " \"comp1\",\n", + " \"comp2\",\n", + " \"comp3\",\n", + " ], # optionally, specify the time varying columns\n", + ")\n", + "\n", + "print(f\"\\n{len(series_multi)} series were extracted from the input DataFrame\")\n", + "for i, ts in enumerate(series_multi):\n", + " print(f\"Static covariates of series {i}\")\n", + " print(ts.static_covariates)\n", + " ts[\"comp1\"].plot(label=f\"comp1_series_{i}\")" + ] + }, + { + "cell_type": "markdown", + "id": "71c466ae-b136-4307-a9b5-550554ffc467", + "metadata": {}, + "source": [ + "### 5. Scaling/Encoding static covariate data\n", + "There might be the need to scale numeric static covariates or encode categorical static covariates as not all models can handle non numeric static covariates. \n", + "\n", + "Use `StaticCovariatesTransformer` (see the docs [here](https://unit8co.github.io/darts/generated_api/darts.dataprocessing.transformers.static_covariates_transformer.html#staticcovariatestransformer)) to scale/transform static covariates. By default it uses a `MinMaxScaler` to scale numeric data, and a `OrdinalEncoder` to encode categorical data.\n", + "Both the numeric scaler and categorical encoder will be fit globally on static covariate data of all time series passed to `StaticCovariatesTransformer.fit()`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "32455f97-f147-4b02-9838-11632488b25f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original series 0\n", + "static_covariates ID var1\n", + "global_components SERIES1 0.5\n", + "Transformed series 0\n", + "static_covariates ID var1\n", + "global_components 0.0 0.0\n", + "\n", + "Original series 1\n", + "static_covariates ID var1\n", + "global_components SERIES2 0.75\n", + "Transformed series 1\n", + "static_covariates ID var1\n", + "global_components 1.0 1.0\n", + "\n" + ] + } + ], + "source": [ + "from darts.dataprocessing.transformers import StaticCovariatesTransformer\n", + "\n", + "transformer = StaticCovariatesTransformer()\n", + "series_transformed = transformer.fit_transform(series_multi)\n", + "\n", + "for i, (ts, ts_scaled) in enumerate(zip(series_multi, series_transformed)):\n", + " print(f\"Original series {i}\")\n", + " print(ts.static_covariates)\n", + " print(f\"Transformed series {i}\")\n", + " print(ts_scaled.static_covariates)\n", + " print(\"\")" + ] + }, + { + "cell_type": "markdown", + "id": "e6db96f9-4b1f-4f7f-9897-144b8e8b270d", + "metadata": {}, + "source": [ + "### 6. Forecasting example with `TFTModel` and static covariates\n", + "Now let's find out if adding static covariates to a forecasting problem can improve predictive accuracy.\n", + "We'll use `TFTModel` which supports numeric and categorical static covariates." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "a0e90425-1196-4a84-8f5e-b5a1597bcda5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "from pytorch_lightning.callbacks import TQDMProgressBar\n", + "\n", + "from darts import TimeSeries\n", + "from darts.models import TFTModel\n", + "from darts.utils import timeseries_generation as tg\n", + "from darts.dataprocessing.transformers import StaticCovariatesTransformer\n", + "from darts.metrics import rmse" + ] + }, + { + "cell_type": "markdown", + "id": "58c2fd83-6a2d-4b44-a88a-a5579695011e", + "metadata": {}, + "source": [ + "#### 6.1 Experiment setup\n", + "For our experiment, we generate two time series: a fully sine wave series (label = smooth) and sine wave series with some irregularities every other period (label = irregular, see the ramps at periods 2 and 4)." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1f8af919-e0cb-4d4c-9577-04954421e187", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "period = 20\n", + "sine_series = tg.sine_timeseries(\n", + " length=4 * period, value_frequency=1 / period, column_name=\"smooth\", freq=\"h\"\n", + ")\n", + "\n", + "sine_vals = sine_series.values()\n", + "linear_vals = np.expand_dims(np.linspace(1, -1, num=19), -1)\n", + "\n", + "sine_vals[21:40] = linear_vals\n", + "sine_vals[61:80] = linear_vals\n", + "irregular_series = TimeSeries.from_times_and_values(\n", + " values=sine_vals, times=sine_series.time_index, columns=[\"irregular\"]\n", + ")\n", + "sine_series.plot()\n", + "irregular_series.plot()" + ] + }, + { + "cell_type": "markdown", + "id": "783504a3-65f3-40bc-8a1f-ed5a090b98e7", + "metadata": {}, + "source": [ + "We will use three different setups for training and evaluation:\n", + "\n", + "1. fit/predict without static covariates\n", + "2. fit/predict with binary (numeric) static covariates\n", + "3. fit/predict with categorical static covariates\n", + "\n", + "For each setup we'll train the model on both series and then only use only the 3rd period (sine wave for both series) to predict the 4th period (sine for \"smooth\" and ramp for \"irregular\").\n", + "\n", + "What we hope for is that the model without static covariates performs worse than the other ones. The non-static model should not be able to recognize whether the underlying series used in `predict()` is the *smooth* or the *irregular* series as it only gets a sine wave curve as input (3rd period). This should result in a forecast somewhere inbetween the smooth and irregular series (learned by minimizing the global loss during training).\n", + "\n", + "Now this is where static covariates can really help. For example, we can embed data about the curve type into the **target** series through static covariates. With this information, we'd expect the models to generate improved forecasts.\n", + "\n", + "First we create some helper functions to apply the same experiment conditions to all models." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "76279b10-1c94-46c7-9b91-6b59398a6c8b", + "metadata": {}, + "outputs": [], + "source": [ + "def test_case(model, train_series, predict_series):\n", + " \"\"\"helper function which performs model training, prediction and plotting\"\"\"\n", + " model.fit(train_series)\n", + " preds = model.predict(\n", + " n=int(period / 2), num_samples=250, series=predict_series, verbose=False\n", + " )\n", + " for ts, ps in zip(train_series, preds):\n", + " ts.plot()\n", + " ps.plot()\n", + " plt.show()\n", + " return preds\n", + "\n", + "\n", + "def get_model_params():\n", + " \"\"\"helper function that generates model parameters with a new Progress Bar object\"\"\"\n", + " return {\n", + " \"input_chunk_length\": int(period / 2),\n", + " \"output_chunk_length\": int(period / 2),\n", + " \"add_encoders\": {\n", + " \"datetime_attribute\": {\"future\": [\"hour\"]}\n", + " }, # TFTModel requires future input, with this we won't have to supply any future_covariates\n", + " \"random_state\": 42,\n", + " \"n_epochs\": 150,\n", + " \"pl_trainer_kwargs\": {\n", + " \"callbacks\": [TQDMProgressBar(refresh_rate=4)],\n", + " },\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "99faf9bc-305a-4897-8f10-5f8ac3777601", + "metadata": {}, + "source": [ + "### 6.2 Forecasting without static covariates\n", + "Let's train the first model without any static covariates" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7a8411c3-b02a-4b19-97bb-46a253f43338", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6bdf2f0868df4253b621aa3236eee2ab", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Training: 0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d52eb2242b3c4f25bfc82fe27f631419", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Predicting: 4it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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WsrKysLWj508eLCentnZgwe488476du3L3v27GHXrl1mi9MrpIE3EMt5VjZHHz67XC5ef/31sPUjPXj/6ErP7FIquCucTicPPPAAYL/JVmngDcQMz6q+vp6SkhLi4uLaV/eLEIyoMCk9eP/obqRoZw8e2vTstddeo7W11WRp/EcaeAMxw7PyzaCJi7OxG9UFd911F5mZmezatStsw2e7efC6gT1y5Ihhe9hWVFRw8eJFUlJS6N+/f7tjdvfgAa6//nquuuoqzp07x9q1a80Wx2+kgTeQoUOHAprRNcoLiNT4u05CQgL3338/EL7SBXZKkwTIysoiMzOT2trasM5N+OIbnum4raHDYZ9Kkl1h1+38pIE3kJSUFPLz82lpaTGsnGukxt99Cffw2U4LnXR8vXgj6E7PbG7bveiL61auXElVVZW5wviJNPAGY3R8NNI9eIDx48czbNgwysrKWLduXcjbt5sHD22G1gp6ZuF9yntFYWEht956K01NTaxYscJscfxCGniDMToOHw0evMPh8OYqh2P4LD34nokGDx7sV7pAGniDkR58eNCHz3/729+orq4OWbtut9tbCiEpKSlk7YYbq3jwacngtGElya6YN28eSUlJbN682fTNzf1BGniDMdKDv3TpEufPnycxMZH8/Pyw92cmRUVFTJkyhcbGxpBultzY2Ahoxj0mxj6Pi5EevKqqXXrwzniH7VMkfUlLS2Pu3LmAPbbzs4/GRghGevC+D52djFOg+G6zFirsliKpoxvao0ePhr243blz56ipqSEzM5OsrKyw9mUFfPXMqDTUQIn8p95iDB48mJiYGE6ePElzc3NY+4qG+Lsv+vB506ZNIdv71m6LnHTS0tLIy8ujqamJkpKSsPalOyudpUhGIrfffjv9+/fn2LFjfP7552aL0y1BGXhFUX6rKMpmRVGWKooS3+HYFEVRShRF2agoyqfBiRk5OJ1OBg0ahNvtDnsMTzfwkR5/10lPT+fee+8FQufF29WDB+PCgdGmZ0bvRxAMARt4RVHGAPlCiEnAQeC+Tk57UwgxRQhxe6D9RCJGTYD5elbRgm+WQyiGz3b14MG4cGA069mbb77pnaexIsF48DcB+prdNcDETs6Z5/Hw/zGIfiIOoybAos2zgrbh89GjR9m+fXvQ7UkPvmeiUc9GjRrFddddR1VVFe+//77Z4nRJ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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "train_series = [sine_series, irregular_series]\n", + "for series in train_series:\n", + " assert not series.has_static_covariates\n", + "\n", + "model = TFTModel(**get_model_params())\n", + "preds = test_case(\n", + " model,\n", + " train_series,\n", + " predict_series=[series[:60] for series in train_series],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "d0c99b81-c62a-4712-8b53-57adfaae4b10", + "metadata": {}, + "source": [ + "From the plot you can see that the forecast began after period 3 (~01-03-2022 - 12:00). The prediciton input were the last `input_chunk_length=10` values - which are identical for both series (sine wave).\n", + "\n", + "As expected, the model was not able to determine the type of the underlying prediciton series (smooth or irregular) and generate a sine-wave like forecast for both." + ] + }, + { + "cell_type": "markdown", + "id": "fb22bf06-9803-40f6-9d37-25e65840a4ad", + "metadata": {}, + "source": [ + "### 6.3 Forecasting with 0/1 binary static covariates (numeric)\n", + "\n", + "Now let's repeat the experiment but this time we add information about the curve type as a binary (numeric) static covariate named `\"curve_type\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "44b34b11-d5de-4d6d-a817-b584b12fb12a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "static_covariates curve_type\n", + "component \n", + "smooth 1.0\n", + "static_covariates curve_type\n", + "component \n", + "irregular 0.0\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9a5bf463e99b4c0baecc2944a40a0f65", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Training: 0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "259476d0a70f41eca31008b1ecdaa42e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Predicting: 4it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "sine_series_st_bin = sine_series.with_static_covariates(\n", + " pd.DataFrame(data={\"curve_type\": [1]})\n", + ")\n", + "irregular_series_st_bin = irregular_series.with_static_covariates(\n", + " pd.DataFrame(data={\"curve_type\": [0]})\n", + ")\n", + "\n", + "train_series = [sine_series_st_bin, irregular_series_st_bin]\n", + "for series in train_series:\n", + " print(series.static_covariates)\n", + "\n", + "model = TFTModel(**get_model_params())\n", + "preds_st_bin = test_case(\n", + " model,\n", + " train_series,\n", + " predict_series=[series[:60] for series in train_series],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "dbfe3526-d346-4eda-91c3-3e61cc9de0be", + "metadata": {}, + "source": [ + "That already looks much better! The model was able to identify the curve type/category from the binary static covariate feature." + ] + }, + { + "cell_type": "markdown", + "id": "9298d11f-3864-461d-a364-6b2e5e45d2f6", + "metadata": {}, + "source": [ + "### 6.4 Forecasting with categorical static covariates\n", + "The last experiment already showed promising results. So why not only use binary features for categorical data?\n", + "While it might have worked well for our two time series, if we had more curve types we'd need to one hot encode \n", + "the feature into a binary variable for each category. With a lot of categories, this lead to a large number of \n", + "features/predictors and multicollinearity which can lower the model's predictive accuracy.\n", + "\n", + "As a last experiment, let's use the curve type as a categorical feature. `TFTModel` learns an embedding for categorical features.\n", + "Darts' `TorchForecastingModels` (such as `TFTModel`) only support numeric data. Before training we need to transform the `\"curve_type\"` into a integer-valued feature with `StaticCovariatesTransformer` (see section 5.). " + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "05d6e39c-ff99-4354-96be-756062ab3941", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Static covariates before encoding:\n", + "static_covariates curve_type\n", + "component \n", + "smooth smooth\n", + "\n", + "Static covariates after encoding:\n", + "static_covariates curve_type\n", + "component \n", + "smooth 1.0\n" + ] + } + ], + "source": [ + "sine_series_st_cat = sine_series.with_static_covariates(\n", + " pd.DataFrame(data={\"curve_type\": [\"smooth\"]})\n", + ")\n", + "irregular_series_st_cat = irregular_series.with_static_covariates(\n", + " pd.DataFrame(data={\"curve_type\": [\"non_smooth\"]})\n", + ")\n", + "\n", + "train_series = [sine_series_st_cat, irregular_series_st_cat]\n", + "print(\"Static covariates before encoding:\")\n", + "print(train_series[0].static_covariates)\n", + "\n", + "# use StaticCovariatesTransformer to encode categorical static covariates into numeric data\n", + "scaler = StaticCovariatesTransformer()\n", + "train_series = scaler.fit_transform(train_series)\n", + "print(\"\\nStatic covariates after encoding:\")\n", + "print(train_series[0].static_covariates)" + ] + }, + { + "cell_type": "markdown", + "id": "638b7f2b-203d-44cc-a5b2-ef3b46c0d4e3", + "metadata": {}, + "source": [ + "No all we need to do is tell `TFTModel` that `\"curve_type\"` is a categorical variable that requires embedding.\n", + "We can do so with model parameter `categorical_embedding_sizes` which is a dictionary of: {feature name: (number of categories, embedding size)}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "dc7eb492-692d-41ec-958d-6bddbdaed937", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8cac6c0f143d45bca19d71ce1afdc34a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Training: 0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3200df5b97714f6081ef94a257fc125a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Predicting: 4it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXgAAAEXCAYAAACnP18pAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABOFElEQVR4nO29d3gc13nv/9mCRScKARCdYBW7KGEkqpu2RImWFVlyFNuJbUX+3XtdFN3rKlvxTbEdx7YcO05ynSJZ7klsJpZlR46sRlKFqhxRFKtIECBAVAIEQbRF2XJ+f8zOYgGibZu25/M8eLCYcubdL2beOfPOe97jEkIgkUgkEufhNtsAiUQikaQH6eAlEonEoUgHL5FIJA5FOniJRCJxKNLBSyQSiUORDl4ikUgcipUcvEjmp6enJ6n9zfqRdku7nWy3nW23kd1zYiUHnxShUMhsExJC2m0s0m7jsavtdrU7Fsc4eIlEIpFMRzp4iUQicSjSwUskEolDkQ5eIpFIHIp08BKJROJQpIOXSCQShyIdvEQikTgU6eAlEollCQYFcs6KxPGabYBEIpHMxrBf8Mx+QZYXVtcIqstcLMkHl8tltmm2QTp4iURiOSYDghcPCdxuyPLC4RY42CQoLoStq6FyqXT0i0E6eIlEYimEEKhvC4ZHYVmp5sRzfNq60XHBnjcFFcWwdQ2UF0snPx8yBi+RSCzF8TbB6R6oKLl4XX6Oi5oyF2MT8PR+wWvHwsYbaCOkg5dIJJah+1yYAyehsmT+EMySfBfVS+FUB/QOyJewcyEdvEQisQyHWqC4ADyehUMvLpeLonx486QgHJZOfjaSisErilIEPANsAK5SVfVIzDoP8H1gDfCGqqqfTuZYEonE2QSCgvNDUFm6+H0K8lx0nRN09gnqlsl4/EyS7cH7gfcAv5xl3W1Al6qq1wP5iqJcneSxDKGzs5OJiQmzzbAV3d3djI2NmW2Grejt7WVkZMRsMyzF0Ci4mDs0M3ihn5HhwYuWlxTCGye1nHnJdJJy8KqqBlRV7Ztj9TXA05HPTwLXJnMsI/jtb3/L8uXLufzyy7lw4YLZ5tiCvXv30tDQwKZNm+jrm+tUkMTy2muvsWLFCtatW0dHR4fZ5liGgWGhefhZaD55iA/dvooPv3cNnWdOTVuXm629dG3ulA5+JulMkywBhiKfB4GLHrwURfkY8DGA++67jx07diR8sEAgQGdnZ8L7Hz9+nA9+8IOEQiGOHTvGHXfcwU9+8hO83vRmkiZrt1kEAgFefPFF7rzzTiYnJ2lpaeG2227j5z//OdnZ2WabNydm693V1cXtt9+O3+/H7/dz66238uijj5KXlzfvfmbbnQyLtT00BptrwTOj29nff44//+x7GB3R3Mlffu49/Pw/fkNh4ZLoNsvyITAGHR2QqvR4u2heU1Mz90ohRNI/jY2NP25sbNw0Y9m3Ghsbb4h8/v3GxsbPL9BOUnR0dCS879mzZ0VDQ4MAxO/93u+JsrIyAYhPfepTyZq1IMnYbSZHjx4Vl1xyiQDEzTffLGpqagQgPvrRj4pwOGy2eXNipt4jIyNi69atAhA33HCDWLlypQDEXXfdJUKh0Lz72vU8EWJxtodCYfGfe0Pi2f0hsfdAOPrz1KtjYuOl1whArNt0pWhYtVEA4oqrbxHPvj45bdtfPBsSh5vn1zHVdluEOf1qOrNoXgZuiny+BXgpjcdKmImJCd73vvfR2trKlVdeya5du3jsscfIysri7//+73nooYfMNtFyBINB7r33Xk6cOMGmTZv45S9/yX/913+Rm5vLj370I77zne+YbaLlCIfDfOQjH+HgwYOsXr2aX/3qVzz++OMsWbKEX/7yl3z5y18220RTGRmDYHB69owQgu987eMcfetlypfV8rXvPMZff/c3FBWXsf+Vp/jn735+WhsFeXB2wGjLrU3SDl5RlCeAm4HvK4pyj6Ioukf8LVCvKMqLwLiqqq8ke6x0cO+99/LSSy9RW1vLr3/9a3Jzc7nuuut4+OGHAS109Pzzz5tspbW4//77ef755ykvL+fxxx+nsLCQyy+/nJ/97GcAfOELX+CJJ54w2Upr8ZWvfIXHHnuMoqIiHn/8cZYuXcqGDRvYtWsXbrebv/qrv+I//uM/zDbTNAZHYGYE/f4HX+Hp1g/hrflf/MW3/oul5VVU167kq99+FK83i0d//g/892OPRLfPzoILw8babXnm694b/JMUiTxOtbS0CEDk5OSIAwcOXLT+85//vADEu971rmTNmxMbPQYKIYTo7e0VLpdLeL1esW/fvovWf+UrXxGAUBTFBOsWxgy9h4eHhc/nEy6XSzz11FMXrf/ud78rALF27do527DbeRLLYmx//VhIPPrcVHjme/85Ibg+FP3Jvzkk7vhSSPz0SW2b+//iEQGIsvJqseeNqf3+/ZmQmJhMTYjQRpqbEqKxPL/73e8AuO2227jssssuWv/AAw/gdrt58cUXGR6WXQOAp556CiEE1157Lddee3Fi1Oc//3mys7NRVZXe3l4TLLQee/bsYXJykm3btnHzzTdftP5P/uRPKCoq4uTJk7S0tJhgofl090NB7tTf//Cf2gvV3LHn2NAAo2Pw6xfhE9+Gth7Bu9/7UZaWVXGur4vmpkPR/dwu8I8bbLyFkQ4euPXWW2ddv3TpUq666ioCgQB79uwx0jTLomv2zne+c9b1eXl5bN++HdBuBpKFz7OsrKyo49e3zSTGJgSj4+DL0uLvbzYJTnaXQnCQW9Y+xz9+xsUjX4Ar14N/Av7sEc3hb7vu3QC8tm8qHBgWMCaHsUTJWAc/Pj7O7t27Adi5c+ec27373dpJJGPKEAqFePLJJwF417veNed2uiOTmmkhUF0H/VyajUw+zwZjxnsJIfj+45E/Or7N9Te8A4BVNS6+/FFYWQUdffC1n4FyjabZ6y89Gd3f7dLqyEs0MtbBP//884yNjXHZZZdRVVU153axzkpk+Mwyr7/+OufPn2fVqlWsWLFizu10Z/XUU08RDAaNMs+SHDt2jDNnzlBRUcHll18+53Z6J2PPnj0ZNyr43KDA69E+v3QEjrcBk2fJGfgBmy+7LrpdbraLv/qfsCQPXjsGbw+9B4/Xy5FDLzM8pKXPZPvgghwgHCVjHbz+KDxfrwpg69atLFu2jI6ODo4ePWqEaZYlVrP5Kv2tWbOG1atXMzAwwOuvv26UeZZE12znzp243XNfblVVVVx22WWMj49nXNZWdz/k50AoLPjBbyML27+OcsXVZGX5pm1bXebiL+7Reur/8Xw2tZc/QDgUQn31GUDLpBmQDj5Kxjp4/VF4rriojtvtzujH51gWqxlkdsghlng0y8TQVjAo6B+C3GzY8wa09kA2vdD9MNuunb3z1XiJi4/frn0+V/A5wB0N02RnRVIuM/xpWycjHfypU6doamqiuLiYbdu2Lbi97qwy8QWYztmzZ3njjTfIycmJvkSdD91ZZbJmQ0ND7Nu3D7fbvagyHJl4ng35QYS1AmO/jYyUCZ3+MojJOR08wF3boWopjAaWQPF2Xnv5d4TDYdxuF2EBE5OGmG95MtLB6xfQLbfcsqhaMzt27MDj8bBv3z6GhoYW3N6J6C9X3/nOd5Kbm7vA1vCOd7yDnJwcDhw4QE9PT7rNsyS7d+8mEAhw9dVXU1q6cA3cbdu2UVJSEu2AZAIDw9q8q0IImru0ZcGzv2Hl6s2UL6udcz+328UORfucXf9xBvrPcurEQW2B0LJtJBnu4Bfz2AxQUlLCNddcQzAY5Nlnn02naZYlXs1yc3OjmTb6zSHTiFczr9fLLbfcMm1fp9PZp4Vnege01EefexQCPdEUyPnQHXyw6DZw5/PaS1poSyBTJXUyzsH7/X727t0LEL2YFkMmx5SDwWA0p32hl9KxZLJmi02PnEkmaRYOC84OQH4utHRry1x+LZHhynnCMzq1FS42NkCIHCi7k9f2aTdFrwcGR2UMHjLQwT/33HOMj4+jKArLli1b9H6xMeVMe4Hz6quvcuHCBdauXcuqVasWvZ+u2dNPP51x6ZKHDx+ms7OTqqoqtm7duuj99HTJ5557Dr/fnybrrMHQKITC4HG7OB0Jz0z0v0p+wRI2bblmUW3cfIX227XsIxw/8hqDF/rxZclUSZ2Mc/CLTY+cyZYtW6iurqarq4tDhw4tvIODSFSzlStXsnbtWgYHB3nlFUvWmksbsemR86WUzqSiogJFUZiYmIg+aTqVgWERrTCm9+AZPULjth14s7IW1cb2yyDLA6LoXYS9laivPk1OFgxk5quyi8g4B6+HGhYbF9VxuVxRB5dpMeVENYvdR2q2eDJFs65+yM3RPus9ePyH582emcmSfBdXbwJcbqj4I/a/8jS+LK38sJyIO8Mc/ODgIE1NTWRnZ6MoStz7X3edNqrujTfeSLVplmVycpJDhw7hcrm45prFPTbHkomaCSGi31f//vGQCZqFw0IrMJYDwZDgjF6XbvTotNGri0EP01BxNyeOv4HL5ULImjRAhjl4PbSyadOmhKbi02Opb731VirNsjTHjx8nEAiwevVqCgoK4t4/EzVrbW1laGiIZcuWUVlZGff+l156KaCdr+FwONXmWYJhPwQiE3y0n4VgCBhrJicbqmsX/54HtCJkS/IF5G/kTF8Ok5OaZ5cOPsMcvO5k9AsoXtavX4/X66WpqYnR0dFUmmZZktVsxYoVFBQU0NPTkzHlg5PVrKKigqqqKkZHRx1bPvjCiIjOnarnvzN6hJWrN+PxeOJqK8vr4sZGrbFw6QdpazkGyFx4kA4+LrKzs1m/fj1CCI4cOZJK0yxLspq53W62bNkyrS2nk6xmsfs6VbOuc5AbKTNzWn/B6j/EqrVbEmrven23JdfQfPItvF4YHJExeOng48TpF95MUqFZpoVp5Hk2P0IIus5p+e8ALTE9+FVrE9NsdU3kQ/4WTr59iJwsOC/n6MkcBx8MBjl8+DCQGmd18ODBFFhlbYQQ0e8ZTy73THS9M0EzICWaOfk8GxnT4u/eyATb0R786GFWr92aUJuFeS6K8/zgyeV483l8smwwkEEOvqmpifHxcZYvX05xcXHC7Ti5ZzWTrq4u+vv7KSkpobZ27rogC5FJmg0NDXH69Gmys7O55JJLEm7HyZpdGJ6aYHtkTBvNSngcxptZuWZzwu2urtFuGK1nvWR5tJeswWBmh2kyxsGn4rE5dn8nZzjoxGoWz2CdmWzatAmXy8Xbb7/NxISz33zpmVobN25MKFNLZ82aNeTk5HDmzBkGBgZSZZ4l6DonyLko/n6MmtoGcvPiz9TS2bBSi/mMu1fT29MOAsYyvKqkdPBxUl5eTlVVFSMjI5w+fToVplmWVGmWn5/P2rVrCQaDHDt2LBWmWZZUaeb1etm0aROAo0ZOCyHoiplgeyr+fjjh+LvOVBx+K80ntf9Dpk/AnTEOXo9lJnvhxbbhxPhoLFKz+JGazc/omFarPcurPRFOd/CJZdDorNajiAWX0tz0FrjAPy5DNBmB3rNK5sWXTqZkhaRSMyfHlGOR59n8DI5Oxd8h9gXrkYRfsOpUlkK2dxJ8lRw/0YbPKzNpMsLB9/X10d3dTUFBwbyTRS+WTHBWfr+fpqYmvF4vGzZsSLq9TNAsFApFx0fouf/J4ETNhv0CT8TrCCFSGqJxuVzUl2tB95NnQuT4ZNGxjHDw+gWyZcuWeSc+XixOvPBmcuTIEcLhMOvWrSM7Ozvp9mJ7o04tt9zU1MTY2Bj19fWUlJQk3Z5+kzh69CiBQCDp9qzAkB98kUKRfRdgdBwInCM/Z5yKyrqk29+wSgvu949VEA6OMJDh87NmhINPZVwUpjIc2traHJfhoJNqzaqrq1m6dCkDAwO0t7enpE2rkWrNioqKaGhoYGJigubm5pS0aTZDo+CLJBfF9t5Xr40/UysQFJwfmu6819ZFyhzkX0pb8xGCocyenzUjHHwq46KgZThs3qzl6zopwyGWVGvmcrkc/+STas1i23JK9tFwTA9++gCn+G6KQgh6B2AyoDl6nVV6Jk3BFk6dPIiLzK5Jk1EOPlU9q9i2nO6sUqmZE18axpLO88wJDj4YFEwEpkawtupzsfuPxR1/7x2ANXWwbvn0EasrKsFFGHIv4cSJY5FMmhR9ARvieAc/MTHB8ePHcblc0bziVOBkBx8Oh6NPJvKmuHjS6eCPHz+esjbNYmLGa4Tu/siHsVNxOfgLI4KifLhsjYv6ZS4CMbNB+rJcLCvyg8vD2y1+PG5t+0zF8Q7+2LFjBINB1qxZQ35+fsradWKOss7p06cZHh6msrKSioqKlLXrZM36+vro6uqioKCAlStXpqxdXbOjR4+mrE2zGJ8RC+/s0xyvK9BKw8rFZWqNTwomg3DtFhdZXhclhVCYpy3XuaReiwG19+eR5QlldCaN4x18OuKiMD3DwWkTSqdLs/Xr15OVlUVzczMjI86qBJXqTC2dhoYGlixZwrlz5+jp6Vl4BwszPgl6Qsv4pOD8sAvCkyyvLsSXnTPrPpNBwdCo4NygoOe8oH8QrtusFRcD7d3O2joYjDmd1q/Usr6CvvWc723mvLNOtbjIGAefysdm0DIcVqxYwcTEBCdOnEhp22aTLs18Ph8bNmxACBGt7OkU0qWZk+rpj44J3JFEmWh4ZryV1XMUGJsMaA69rBguqYOrNsAtV7qoLpvutmrKXITFVDpkbOngMy2H8I9nbtExxzv4dMSSdWILjzkJqVn8SM0WZnB0KoOm61xk4fjpOUsUnBuEbevhmk1uNq9y01Dlpqz44lTKgjwXZUVaGQSIzaS5lOaTh3G5MjeTxvEOXu9dr1+/PuVt6206rQcvNYsfqdnCxA5ymurBN7N8xcWaDfsFJYXQULW43Pi1dTAccfDFBS4Ksv3gKeBE6yCIzM2kcbSD9/v9dHZ2kpWVRX19fcrbX7NmDaCNYHQKoVAoOqhm9erVKW/fiZrB1PfRv18qcYpmw/6pQU5TPfgWauqnayaEYHAUrljnwu1enIOvLHXhAsJhLRSzvFzrsrf3ZeNya3XnMxFHO/hTp04BsGrVqqRqc8/F2rVrATh58mTK2zaL9vZ2Jicnqa6upqAg8drcc+FEzfr7+zl//jwFBQVUVlamvH0naBYKCcZjqkh29oUAcE22UlUzvT7UuUFYU8us4Zi5yPa5qK/UnhIA1q/QXtr2jS7F5xWcz9BMGkc7eP2CSEevKrbdpqYmx9S7SLdmq1atAqClpcUx2UexvfdkJkaZi7q6Onw+Hz09PQwP27M84swUyTNntf99WcE4WVm+6PJAUCAEbF4Zv44rKl2MRWLtq+u1mjShrOWMDnZnbFVJRzt4/cLTe0Cppry8nKKiIgYHBzl37tzCO9iAdGuWl5dHXV0dwWCQtra2tBzDaPSbYro083g8NDQ0AFNPpXZjfBJ0lx0KC/oGtSfq5dXT0yPPDcLW1ZCXE7+DLy4kWou4pjyyMGclfd1NDGZo0bGkHbyiKA8qivKioig/UxQlK2b5dkVR2hVFeU5RlN3JHicR0t0bdblc0bbt/PgcS7o1i23bKZqlM/6uo5e5tqtmE4GpHPj+QQiFPTDZQ3399Ll+XUBNeWJPQbnZLvJytNz56qWRhTmr6WxvQkC0d59JJOXgFUW5FKhRVfV64G3grhmb7FJVdbuqqjcmc5xESXdvNLZtu78A05GaxU+6e/Aw5eDtqpl/XKBHr2JfsNbWT2kmhAAX0flaE6GyVMuYKSkEr3sCskpoPt2RsZk0yfbgrwGejnx+Erh2xvrfj/TuP5XkcRJC9kbjR2oWP7IHvzCDo5ClZ9BEa9C0UBuTQRMIQV42eDyJv8dYVgrjE9rT9dJ87Y1rS4f2OxNz4ZNNLSkB9KKfg0BpzDoVuCTy+TeKouxTVfWN2J0VRfkY8DGA++67jx07diRsSCAQoLOzM/r34OAgfX195ObmIoSYti6VlJWVAdoglESOMdNuM5mcnKS1tRWXy0VOTs68diVj99Kl2vPzkSNHDP/uqdZbCBHNTy8oKEjb99HTfI8ePWqZ82WxBAIB8t3drKsEtxuGhgqBAhhv4fL1a6ks0ArDh8PgcUMyX88dho012s1kRaWbs8PQ0x9mU203k6PxtW2la3M+ampq5lyXrIO/ACyJfC4CzusrVFWNVoBQFOVx4FJgmoNXVfVh4OHIn0m9Aens7Jz2Rbu6tJNmzZo11NUlP1PMXGzbtg3Q0gvnE3ouZtptJidOnCAUCtHQ0LBgwaxk7L7qqqsAaGtrM/y7p1rv7u5u/H4/paWlKa1WOhP96aC1tdUy58ti6ezs5K2OKnKzwed18Xa7VlbSE2iDQoWeEW2Sjv5BweoauKQm8cBCMCh45XlBRQnUVE1CE5wbKaS5r4KiAg/bL1t821a6NhMl2RDNy8BNkc+3AC/pKxRFWRKz3XWAoa//jYglw9SFd+rUKcLhcFqPlW6M0mzFihV4PB7a2tqYmLD3c7MR8XeAZcuWkZ+fz/nz5+nv7194B4sxNgFZkcmW2nq0/3lZUQCPxxPdJhiCJUkWfPV6XZQu0Y7XUKUF88O+FQwPtDOQgamSSTl4VVUPAmcVRXkR2Ag8qijKQ5HV71cU5XVFUV4GOlVVfSE5U+PDiFgyQHFxMeXl5fj9/uhTg10xSjOfz0dDQwNCCNtPRWdE/B2mZ2zZ7UVrZHBpdIzA2QEtcFC/LGvadkIklh45k8ql2lyv1WWRBTkr6e5oYmJSK2CWSSQ9vFNV1ftnLPp4ZPkjwCPJtp8oRl14+jH6+vpoamqitrZ24R0sitGaNTc309TUxIYNi6sFbkWMuimC9pRw8OBBmpqaomEuOyDCRAOwI2OCsUA2hPysaCibtp3LBbnJz+9O2RIXoZCgRm8+dzUdZ35F/SU78I9P1cPJBBw70MmoR+fYY9g1w0FHahY/RoW1wL7ZR2FBdJRTbIpk3fI1F22XCge/JF87XnkxeFxB8FXS2tqK4OIRtU7HkQ5eCGF4bxTs9+g8E6lZ/Bjdgwf7aRaOiYpMVZFsoXb51E0xGBL4vFO1apIhL0eL94cFlORrJSZbOsaSbteOONLB9/X1MTg4SFFREeXl5QvvkCRO6I36/X7a29vxer3RYfHpxAmaxVbeNNLB202zUGj2KpJ1MYOcJgNQlKLadi6XKxqH10sW6NMDZhqOdPDpLv40Eyf0RnVHtXLlyrRU3pyJEzRrb29nYmKCyspKCgsL0348uxa3C4Wn4t5t3VoGTVaog9KyqcqbEwEoTmHx0srIgKcVNVrRsQtjhQQDGRafwaEO3shYMkzVTW9ubrZthUSjNauvr8fn89HV1WXb+VmNjL+DNkCstLSUkZERW83PGo5x8K1dWr2A8qLAtM5Xqh18cYELAdQv0zorInsFvT2nU3cAm+BIB29kLBkgPz+fmpoaAoEAZ86cMeSYqcZozTweT7R0sF0rJBoZf9ex25NPOCwIi6kc+LMDmsupLfdM204IyE9BiqROYZ7WZpVedCx3FT2d9tAslTjSwRvdG409lt3iozpSs/gxugcfeyy7aKZXkXS5XARDgsGxPBBhVi8vmrad2wU5Kcig0fFluSjK1zJpAMhZRXeHdPCOwOjeaOyx7NKzmonULH5kD35hxieJVpE8ex4EHpjooL5heikMAeQmUUVyNiqXQkEuuAhDdj1d7TJEY3vC4bApzspuPauZyB58/Mge/MLEVu/oikmRjM2gCYcFHjdkp9jBlxW5EAJK8sfB5eZMtz3f9SSD4xx8V1cXY2NjlJeXU1xcbNhx7dazimVwcJDe3l5ycnIMLa5kZ80mJyc5ffo0Lpcr+i7BCOysWWdf5MP46WkTbU8EtJh5qjPefF7ANVWy4Ox5+2QepQrHOXgzeqKxx7NLzyqW2Ccet9u4U8LOmp0+fZpQKER9fT05OTkL75Ai7Fzc7nTHKADZdFNUvDS6fDIIRUkWGZsNXxYgoCEyLeDwZBF+vz/1B7IwjnPwZoRnQMsfd7vdtqyQaJZmVVVV5Ofn09/fz/nz5xfewUKYpVlhYSGVlZVMTEzQ3t5u6LGTpbVLG026tGD61EqTKU6R1PF5tfh/XUXEzeWsoq3V3sXt4sVxDt6sHrxeITEcDtPS0mLosZPFLM3sXCHRLM1ij2m3J5+e81oIpnbZ9BTJUBgK81I/IDHLq728rY4pOtbcbC/NksVxDl53rkbGRXX0AU92c/BSs/iRmsWHEIIBvxaHWVmTd9H6VBQZm4keooktG3ym1T6apYL0j0k3GDMvPH0WJDtdeCA1SwSpWXwM+SEQzoHgECuWV1+0PpmJtufC5XKRky0o0O8nOSs43ZpZqZKO6sELIaIn/UJTzqUDO154gNQsAaRm8RFbZKy6bkozIQQu0tODJ9Kuxw1LcsbB7aOpLbNSJR3l4M+dO8fIyAhFRUWUlJQYfvwVK1YA9rrwxsbG6Orqwuv1mjJZiR01C4fDnD6t9QR1+43EjprFlgmuqp7SLBCE/Bxwu9NTFDDPp00FWLU0BEBbT/qLD1oJRzl4M3tVsce104XX2toKwPLly6fNj2kUdtSsq6uLyclJysvLKShIQ/rHAthRs7YuLbPMHThzURXJdKRI6uTlag6+vlJ7ROgdLiAUCqXvgBZDOvgUEnvh2aWcq9ma1dfX43a7aW9vZ3LSHuVc9d67WZqVl5eTn5/PhQsXGBgYMMWGeGmJ5MAX5w5fVEUyVXXgZyM/R3Pwt17tJbfzs4T7n6KzszN9B7QYjnLwZl94xcXFlJSU4Pf76evrW3gHC2C2Zj6fj7q6OoQQtqnEafZN0eVyRY+t//+sTmef1mteVjy99xwMQlFB+sImudkuQiHYusZFfa4K4y22evJJFkc5eLMvvNhj2+UkkprFj9Qsfs4NaWkydcsuTtzLS+NAYL1cAUBFlf3eXSSLdPApxm4vwHQ7zXhZqCM1ix87aRYIwvBkIYgQK+uKLlqfn04HnxX175RLB29vrHDh2a1nZYWbotQsfuykWVe/B3DDRDu19Q3R5aGwwOtNX4okaKNZdWQP3sYEAgHa29txuVwsX77cNDvsdOGZPW5Ax06agfnvLWKPbQfNOs5FsrPGW6iumdJsfBJKl6S+imQsPi+EI/kOy6rs9d4iFTjGwXd2dhIOh6mrq8PnS8OwuEVipwuvr6+P0dHR6Mths7CTZn6/n+7ubtPGDejYSbP2Xt3Bn6ayZurpenwCypak99i+rKkJRyqq7KNZqnCMg9czMMzsVcUe3w4nkRV677HHt0PPSh830NDQYMq4AZ2GhgYA2traLJ/XffKMVj0yx91Lbu5U0vtkEEoK0zvwyO12keWBUEhQXFpFdnY2vb29pk/03trayr//+79H//7xj3/Mfffdl/LjSAefYvS87o6ODsvndVsh1ABQVlZGQUGBLfK6rXJTzM3Npbq6mmAwSEdHh6m2LERLp3YDKisYm7bcBVN1YtJIXg4EQuB2u6mr154gzO5MzHTw6cIxDr6trQ0w/8LLysqK5nXrNlkVK7yUBi0Ga5esEKtoFmuD1TXr7M8CoGrp9OWC9GbQ6ORFBjsB1C+fX7PR0VHe8573cOmll3LjjTeya9cuGhoa+NM//VO2bt2KoigcOHCAW265hVWrVvEv//Iv2ncRgvvvv59NmzaxefNmdu3aNe/yBx54gBdffJGtW7fy3e9+F9BGSO/cuZM1a9bwhS98ISXf3TEOXu/BW+HCs0uYxiq90VgbrK6ZVZ56Ym2wsmZCCPpHtUD7iprc6PJAUJCXDb6s9NeGyc2ecvDLF3DwTz75JNXV1bz11lvs3r2bnTt3AtqT+cGDB7n++uu55557+OUvf8mrr77KX/7lXwLwq1/9ioMHD/LWW2/x7LPPcv/999Pd3T3n8m9+85tcf/31HDx4kM985jMAHDx4kF27dnH48GF27dqVkgldHOfg5YW3eKSDjx+pWXycH9LLBA+yvK4qunxsQsugmQuXy5Wyn20bPNx5g4c/vMlD/fL53/ds3ryZZ555hi9+8Yu89tprFBVpefu33357dP22bdsoLCykvLyc7OxsLly4wL59+/jDP/xDPB4Py5Yt4x3veAf79++fc/ls3HjjjRQVFZGTk8OGDRtSEgFwTD146eDjRzqr+JGaxUfb2ciHGWWCxyeh7OIxT2lnecP8mq1du5YDBw7wxBNP8K1vfYsjR44AkJ2tJeu73e7oZ/3vYDCYEtti2/V4PClp1xE9+IGBAQYHB8nPz6e8vNxsc2xx4U1OTtLe3o7b7aa+vt5sc2yRSWOVcQM6dtCstSfyYUYOfCgMxfPUoBFCpOynozfMz58J8fNnQyxfPv+12dXVRV5eHh/+8If55Cc/yYEDBxb1Pa+//np27dpFKBSir6+PF154gSuvvHLO5YWFhQwPDy9OxCRwRA8+Ni6azkETi0V/D2DlC+/MmTMIIUwfN6BjhxeGvb29+P1+SkpKKC4uNtscW3QkTrUHgCxcE60sLb8zutztgvzcufdLJb6sqc/1MVk04XAYt3t6H/fw4cPcf//90eWPPPIId91114LHuPPOO3nllVe49NJLcblcfOtb36KysnLO5UuXLsXj8XDppZdyzz33pG8cSirvlEn+JMx//ud/CkC8973vTaaZlNHb2ysAsWTJEhEOh+fdtqOjwyCrpvPUU08JQGzfvj2h/VNtt9/vF4Dwer0iEAiktO1YkrH7lVdeEYC4/PLLU2jR4pjN7lAoJLKzswUghoaGDLdpMdz2uX7B9SFRvOHPxN4DYbH3QFjseSMkfvFsSASD818bqeLCcFj84tmQ+PmzIdF9LizKysoEIDo7O+fdz6xrMwHm9KuOCNFYKXUNpvK6h4aGLJvXbaVQA9gjr9tqmrndbss/LbZ0anHkiuKpePJEAArzwOMx5mk7y6ulZOrYIbSVKhzh4K2UugbT63Vb9fHZas4KrB9ykJrFT9d5LfxXu2wqTjI+CUvTXKIgFp8XYuffsbpmqcQRDl5eePEjNYsfqVl8TAYEF8YKQIRYFVMmeHwSyoqNs8PrdeH1TDl5K2uWaqSDTxNWP4msrJlVH52lZvHR1gNameAz1NY1TFtXmGdsMkSuT6tLD9a/NlOJ7R18KBSaVgDKKlg9NqrbZZX3FmD9TBqpWXy0dEc+jLdQVTNdMyNKFMSSG6lHA9bWLNUknSapKMqDwDVAK/D/qaoaiCz3AN8H1gBvqKr66WSPNRsdHR0Eg0GWLVtGbq5BeVeLwMq9hIGBAS5cuGCZcQM6VtbMauMGdKysWX6OwHPhvwkNvk5VzecACIcFbld6p+mb3ZapcgVWfupJNUn14BVFuRSoUVX1euBtIDZh9DagK7IuX1GUq5M51lzoJ7aVLjqw9oUXG2qwwrgBHStr1tbWZqlxAzqxT4rhcNhka6azZlkfocO3UzT4PQoKtRj8+CSUFqZ3ko/ZyM8BvapybW0tXq+Xzs5OxsfHDbVDxy7lgq8Bno58fhK4dpHrUoZ+F7aag4+t152qocypwoqxZIDKykpycnI4d+4cQ0NDZpszDatqptdEmZiYoKury2xzpqFrVls3NcPa+CQsNaFEQey0gF6vNzrrm1m9eKPKBScboikB9EjbIFA6Y93QHOsAUBTlY8DHAO677z527NgRtwFvvfUWoN2VOzs7494/nVRWVtLT08P+/fvnvAEFAgHD7X7zzTcBKC8vT/jY6bK7rq6OpqYmXnvtNTZs2JDy9hO1Wx+yXlFRYcp5Np/dtbW19PX18frrr1vqiUxVVQDq62qoLNBuPktzoCAbjJbQG4Jr1sL4CHSOQ01NDc3Nzezfv58lS6ZyNv1+P5/4xCfo7u4mFArx6U9/mq9//eu8973vZe/evXi9Xh588EG++c1v0trayic+8Qk+8pGPIITga1/7Gs899xwAn/rUp7j99tvnXP7Zz36WU6dOsXHjRv7gD/6AoqIimpub2b59O21tbezcuZM/+7M/W9R3q6mpmft7J6yYxgVAV6cIOL/IdQCoqvow8HDkTzFz/WL4zne+w2c+8xkGBgbm/aJmsHr1anp6evD7/XPa1tnZabjd/f39AGzZsiXhY6fL7jVr1tDU1MTo6Gha2k/Ubn3AWjKaJcN8dq9bt44333yTkZERS10Dg4ODAFTVNNAzUg3A2fOCGxtdlBUbeyPqHRDsPiJ4z1UuKpe6WLduHS+88AJDQ0PTNHv00UdZuXIlu3fvprOzk4KCAh588EE2btzI9773PT7zmc/whS98gZdeeonx8XE2bdrEAw88wKOPPkpzczNHjx7l3LlzXHHFFdxxxx28/PLLsy7/27/9W7797W/z29/+FtBCNCdOnODNN98kOzubSy65hC996UvU1dUl9b2TdfAvA58FfgrcArw0Y91NwAuRdT9K8liz4vV6aWhoICsra+GNDWblypXs27fPci9zrDYwLBarxuGtNlo6FqtmhUyFaKaeXoVYXA0a1w3peZ/Q/Zj2e64XrZs3b+Zzn/scX/ziF9m2bRvve9/7gOnlgkdGRigsLKSwsDDhcsGxTw06erlgIFouOFkHn1QMXlXVg8BZRVFeBDYCjyqK8lBk9W+B+si6cVVVX0nKUhtidWclHfzikZrFT9TB12rx7lBY4HZDjgXeUc+lmV4uePPmzXzrW9/iq1/9KmDfcsFJp0mqqnr/jEUfjywPAvck276dseKFFwwGoxMJWGncgI4VNRNC0NzcDEgHHw8ze/CTAShZZAaNeCG1Q3QCQcFjL0xFgefSrKuri9LSUj784Q8TCoV47LHHFtX+9ddfz0MPPcQf//Efc/78eV544QX+5m/+hmAwOOvyzs5OWS7Y7ljxwtPHDVRXV1tq3ICOFTUbGBhgaGiIgoICysrKzDbnIqyo2cTEBB0dHXg8HiorqxkMarM4La80xx6vR/vRidVMCBG96chywRYsFyyENUt7dnV1CUAsXbp0zm2Mtnv37t0CENddd11S7aTL7uHhYQEIn88nQqFQyttPxO79+/cLQGzZsiXl9iyW+ewOBoPC6/UKQPj9fgOtmpsTJ04IQNTXN4jDxzvE3gNayd6WztT/TxfL717VygXrFBUVCUD09vbOur0VfcocOLtcsFXR87r7+/stk9dtxeH2sRQUFFBeXs7k5CTd3d0L72AAVn4pDVq8Vs/r1st2mI2umT5FHmgvWAsMrkETS1729L8zYUSrdPBpxOVyWa4mjZVfFupYLeRg5QwaHatqtrxhumYznayR5M7h4K2iWTqQDj7NWO0kkg4+fqRm8aPb0RDpwYfDApfrYidrJIV5EDtDn9U0SwfyJWuasdpJJJ1V/EjN4meqB6/ZNRGAogJwu80L0aytcxGbwGM1zdKB7MGnGaudRNJZxY/ULH6iDn65FqKZCECpgbM4zYbb7ZqWomnVAWKpRDr4NGOlC294eJhz586Rk5NDZaVJ+WqLwEqaBYNBzpw5A1hz3ICOlZyVEOKiHrzR0/QtBiudZ+lCOvg0Y6U39bEZNG63df/1VtJMHzdQU1NDTo7BRczjIFYzIRIq65Qy9HEDhYWFlJYu1RYKKMi1TiE0gOXLl+NyuWhvbycQCJhtTlqw7lXuEPRenxXqddshGwS06nhZWVl0d3fj9/tNtcUumpWUlFBcXMzo6Ch9fX2m2jLXfANGz+K0ED6fj7q6OsLhcPQpzWlIB59mCgoKqKioYHJy0vR63XaIJYO18rrtohlYJ+QwUzMBYHIGzVxYRbN0IR28AVjlJJLOKn6kZvEz86lHhGFJHng81grRgLXeXaQD6eANwGoXnnRWi8dOmlnFWV3UgxfmZ9DMhVXOs3QhHbwBWOWlodWH3MciNYsfK2tWWmiWNfNjFc3ShXTwBmCFXkI4HLZ8HZpYrKBZ7PHt5OCtppnLBUvyrReeAetoli6kgzcAKzw6d3d3MzExQXl5OQUFBabZsVisoNnQ0JAtxg3oWMFZ6fMNuFyu6ItylwvyLJZBo2MFzdKJdPAGYIWTyE49Ubi4XrcZxD7xWGky67mor6/H7XbT3t7O5OSkKTa0t7cTCoWorq6Ojhtwu80tMjYf5eXl5OXlMTAwEJ1310lIB28Ael63PgG3GdjNwRcXF1NSUoLf76e3t9cUG+ymmc/no7a2FiFEdNYuo5lNM48bvF5r3iBdLpej4/DSwRuAx+OZNuDJDOzmrMD8Jx+pWfzM1Mzjnj6TkhUxW7N0Ih28QZjdS7BTNoiO1Cx+rKZZcaHLkgOcYjFbs3QiHbxBmN1LkL3R+JGaxY/UzFpIB28QZmeF2KWmSixSs/gx21nZUTOzz7N0Ih28QZh54fn9frq7u/F6vdTW1hp+/EQxUzO7jRvQMdtZyR68tZAO3iDMPIn0gl0NDQ14PBZ/4xWDmZp1dXUxOTlJRUWFLcYN6Jip2dDQEP39/bYZN6CjJ0C0tbURCoUMPXYwGOSaa67h7rvvTku1WengDSL2wjO6bHBzc/M0G+xCfX09Ho+Hjo4OxsfHDT22XTXTB7INDg7S399v6LFjNbPDuAGdvLw8qqqqCAQCtLe3G3rstrY2XnnlFfbu3ZuWORqkgzeIoqIiKioqGBsbM7xscFNTEwBr16419LjJkpWVxYoVK6bNEGQUdtXM5XKxZs0aYOo7GIVdNYMpm43W7OTJkwDR/1mqkQ7eQPR/ov5PNYp0n0TpRGoWP1Kz+DFLs3TfFKWDNxCze1Z2vvCkZovHrN6onTUz6zyTPXgHoV94ZvWs7PzoLDVbPGb34O2omVnnmezBOwgzegl+v5+Ojg68Xm+0up+dMEOzUCgUfWG4evVqw46bKmQPPn5kD16SNGb0Ek6dOgXAqlWr8Hq9hh03VZihWXt7OxMTE1RVVVFYaNGZKuYhtgdvVCXO/v5++vv7yc/Pp6qqypBjppJVq1bhcrk4ffq0YZU4JyYmaGtrw+12py1bSzp4A9F7gy0tLQSDQUOOaedeFUBdXR0+n4/u7m5GRkYMOabdNVu6dCmlpaWMjo7S09NjyDFjNbNTiqROTk4O9fX1hEIhw2rSNDc3I4RgxYoV+Hy+tBxDOngDycvLo7a2lkAgYFg5VzvHRUGrxKnfGI16fLa7ZmB8HN7OKZI6Roe2jOhISAdvMEbH+uzeGwWpWSIY7azsnCKpY/RN0YiOhHTwBmN0TNkJvVGpWfzIHnz8yB68JGlkbzR+pGbxI3vw8SN78JKkMbI3Ojg4SG9vL7m5udTU1KT9eOnCSM0CgQCnT5/G5XKxatWqtB8vXRjprIQQsgefALIH70CM7I3qx1i9enVaChkZhZGanT59mlAoRH19fXTSaDuia9bc3Jz2Cok9PT2MjIxQUlLC0qVL03qsdNLQ0IDX66W9vT3tcyePjIzQ1dWFz+ejvr4+bcex71VvU1auXInb7aatrY2JiYm0HssJsWSAqqoq8vPz6e/v5/z582k9llM0KywspLKykomJibRXSHRC7x3A6/VG89H1gW7pInZ8SjpLeCfl4BVFeVBRlBcVRfmZoihZM9ZtVxSlXVGU5xRF2Z2cmc7B5/PR0NBAOBxOe4VEJ8SSwdgKiU7RDIwLOTgh/q5jVGjLqI5Ewg5eUZRLgRpVVa8H3gbummWzXaqqbldV9cZEj+NEjIopO6U3ClKzRDDKWTmlBw/G3RSN6kgk04O/Bng68vlJ4NpZtvn9SA//U0kcx3HI3mj8SM3iR/bg48dpPfhkipOUAN2Rz4NA6Yz1KnBJ5PNvFEXZp6rqG7EbKIryMeBjAPfddx87duxI2JhAIEBnZ2fC+xtJRUUFAAcPHuSuu+5Ki91CiOhJVFhYmPJjGK13eXk5AIcOHUrquAvZ/fbbbwOwZMkSS51PiehdWqpdkocPH07rdzl+/DgAxcXFsx7HTtemrtnRo0fTavfRo0eBuTWLh/ky5BZ08IqiVAK/mGXV08CSyOciYNrbL1VVo4VDFEV5HLgUeGPGNg8DD0f+TKoqUmdnp21SAa+44gpAszkrKystdvf19TE4OMiSJUvYsmVLyuuDGK33lVdeCWiFwJI57nx2j42N0dnZidfr5aqrrrJUcbZE9L766qsBOHPmTNr+V+FwODrn77XXXjtrcTY7XZu6Zq2trWm7NvX2QdOsuro6LceARTh4VVV7gO0zlyuKshX4LPBT4BbgpRnrl6iqOhT58zrgX5K01TEY8egcGxe1Y/GnmcRqJoRIy3eKnVPUSs49UWIrJAYCAbKyshbeKU70ypuVlZW2rLw5k9raWnJycujt7WVoaCgtDv78+fOGVd5MOAavqupB4KyiKC8CG4FHARRFeSiyyfsVRXldUZSXgU5VVV9I1linUF9fj8/no7OzM235tk6Ki4JWIbGkpITh4WHOnj2blmM4TTMjKiQ6TTO32x0tbpcuzYysvJlUN0VV1ftnWfbxyO9HgEeSad+peDweVq1axfHjx2ltbU3LxeGkzAadtWvX8tprr9HU1ERlZWXK23eqZm1tbTQ1NaXlezlVsyNHjhji4NONHOhkEvo/N1258E7rWUH6MxykZvHjZM3SfW0acVOUDt4k9H9uunsJTutZQfreXUjN4sfJmjnh2pQO3iTS2UuILf7kxJ6V7I0uHqlZ/Djp6Vo6eJPQ797pqHnR0dGB3++nrKyM4uLilLdvFrpmJ06cSHnbg4ODnD17lpycHGpra1PevlmkU7OJiQlHVN6cia5ZS0tLyue0DYfD0sFnAps2bQK0gTXhcDilbR86dAiAzZs3p7Rds1m/fj1ut5sTJ04wPj6e0rZ1zTZu3GjrypszWbFiBbm5uXR0dKS8UNvx48cJhUKsXr2a3NzclLZtJhUVFZSVlTE8PMyZM2dS2nZraysjIyNUVlZSVlaW0rZnwzlnss0oKyujuroav9+f8kfBt956C4BLL700pe2aTW5uLmvXriUUCnHs2LGUtn3w4EHAeZp5PJ7ojV6/iaUK/TzbunVrSts1G5fLFf1O+ndMFUZfm9LBm4h+EunOJVXo7TntwoP0aeZUZwXpP8+cdlOEqe9k92tTOngT0U8iu/cSjERqFj9Ss/hximbSwZtIOk6i0dFRmpqa8Hq9rF+/PmXtWoV0aBYMBjly5AgAW7ZsSVm7ViEdmgkhpINPAOngM4h0nERHjhxBCMH69evJzs5OWbtWIVazVGU4nDx5kvHxcZYvX+6orCMd/aalV0hMBZ2dnZw/f57S0lJHZR3prFu3Dp/PR3NzM8PDwylp88KFC7S2tpKdnW3YuAHp4E1kzZo15OTkcObMmZRlODg5/g7a9H3l5eVcuHAhZRkOTo6/g1YuetWqVUxOTkbLISdLbPzdCcXsZuLz+aJpjKl6OR2b3WZUMTvp4E3E4/Gwbt06IHUnkZMfm0HLcEj1k4/TNYPUPy1mgmYbNmwA7K2ZdPAm44STyGhS7aycnA2iIx18/Djh2pQO3mRSeRKFw+Hok4CTLzzprOInXZo5NawF0sFLUoB+EqUi37alpYWRkZFonNqppDJHube3l56eHgoKClixYkXS7VmVWM2SfTnt9EwtHf3aPHToEKFQKKm2gsEghw8fBozN1JIO3mT0CyQVGQ6Z0KuC1GY4xPaqnFSiYCbLly+nqKiIvr4+enp6kmrr8OHDjs7U0ikpKaG2tpaxsTFOnTqVVFsnT55kYmKChoYGQzO1nHtG24TCwkJWrlzJ5ORk0gWhMiHUAFqGg9670ntFiZIJ8XdI7cvpTOlIACkrWWDWtSkdvAVI9YXndGcFUrNEkJrFj901kw7eAtj9JDIDqVn8SM3ix+6aSQdvAVJRDGpgYIC2trZoxUWnkwrNxsfHefvtt3G73Y4rrTwbqdAsHA5npINP9oW+WQMQpYO3AKnoJejpkZs2bcLj8aTELiuja3b48OGEMxyOHTtGMBhkzZo15OXlpdI8S7Jx40Y8Hg8nTpxgbGwsoTZaWloYHR2lurra0ZlaOqtWrSI/P5/Ozk76+/sTakPP1CosLKShoSG1Bi6AdPAWQM9w0E+ERMikXhUQrYHi9/sTnhUr0zTLycnhkksuIRwOc/To0YTayDTNYuvpJ9oB0/fbsmWL4Zla0sFbAJfLFc2NTfYkypQLD5J/8pGaxY/ULH7M1Ew6eIuQbHzU6UXGZkNqFj+p0iwTHbwdzzPp4C2C/s9/7bXX4t53dHSUI0eO4HK5MuJloU4ymgUCAQ4cODCtnUwgGc2EEOzfv39aO5nAZZddBiSmGcDrr78OSAef0dx4440APPvss3GPaN27dy+Tk5NcccUVFBUVpcM8S7J9+3ZcLhcvvvgiIyMjce378ssvMzw8zLp166iurk6Thdbj2muvxefzoaoqfX19ce178OBBenp6qKmp4ZJLLkmThdajsbGRJUuWcOLECU6fPh3XvqdOnaKpqYni4uLojcJIpIO3CMuXL2fDhg0MDw/z0ksvxbXvE088AcCtt96aDtMsS1lZGdu2bWNycpI9e/bEtW+mapafn8/27dsRQvDUU0/Fta+u2bvf/W5H1oCfi6ysLHbs2AHA7373u7j21be/+eabDasBH4t08BZCdzbxnERCiOj2measIDHNYreXmi0eqZkNNRNCWOUnKTo6OpJtwhRi7d69e7cAxKZNmxa9/7FjxwQgysvLRSgUSoeJs2IVvffv3y8AUV9fL8Lh8ILbd3R0iDNnzghA5Ofni/HxcQOsTJ5U6n3ixAkBiNLSUhEMBhe1T39/v3C73cLr9YrBwcG4jmeVcyVeYu3u7OwUgMjNzRVjY2OL2t/v94ucnBwBiJ6ennSZKcQ8flX24C3EddddR0FBAUeOHKG9vX1R++g9hJ07dzq6GuJcXH755VRUVHDmzBmOHz++qH2efPJJAG666SZHV0OcizVr1rBq1SrOnz8ffWm6EM888wzhcJjrr7+eJUuWpNlC61FdXc3WrVsZGxvjhRdeWNQ+zz33HOPj4zQ2NrJs2bI0Wzg7mecRLIzP5+Omm24CFv8oGBsXzUTcbjc7d+4EprRYiEzXzOVyRb97vJplYnhGR//udtJMOniLEc9JNDw8zAsvvIDb7ebmm29Ot2mWJR7NJicnefbZZ4HMdfAQn2bhcDja4chkzeK5KQohLNGRkA7eYugnw+7du5mYmJh32z179hAIBNi2bRtLly41wjxLsmPHDtxuN/v27WNoaGjebffv38/IyAgbN26kvr7eIAutx/bt28nJyeGNN97g7Nmz82574MAB+vr6qK+vj9bhz0SuuuoqiouLaWpqWnACkKamJlpaWigtLeXKK680yMKLkQ7eYtTW1rJ582ZGRkbYt2/fvNta4RHQCpSWlnL11VcTCATYvXv3vNvq6ZSZrllubi7vfOc7gal3EnMRe55lUnrkTLxeL7fccguwcAhV12znzp2mFv+TDt6C6L34+U4iEZMemcmPzTqL0Qy0QWGx22cyi9VMnmdT2E6z+VJsDP5JCiekYuk899xzAhDr16+fc7/Dhw8LQCxbtszQ9Egdq+l94MABAYiampo50yVPnz4tAFFYWCgmJiYMtjA50qF3U1OTAERxcbEIBAKzbtPX1ydcLpfw+XxieHg4oeNY7VxZLLPZ3dPTIwCRk5MjRkdHZ91vZGRE+Hw+4XK5RG9vb7rNFEKmSdqLa665hiVLlnD8+HFaW1tn3Sb2ETAT0yNnsnXrViorK+ns7Jxznla9V3XTTTfh8/mMNM+SrF69mjVr1nDhwgVeffXVWbd56qmnEEJwww03UFBQYLCF1mPZsmU0NjYyPj7Oc889N+s2e/bsiZYOMbtmvvQMFiR2aPQPfvCDi9aPj4/zb//2b4CMJevEpv7NplkgEOCnP/0pIDWLRddiNs3C4TA/+tGPpm0nma6ZEGLaOiEEP/zhD6dtZyrzde8N/kkKJz0GCiHE008/LQDhcrnEr3/96+jycDgsPvzhD0dHbyb62JwsVtT7pZdeEh6PRwDiZz/72bR1n/zkJwUgysrKxLlz50yyMHHSpffBgweFz+cTgPinf/qnaeu++MUvRkM4yRzfiufKYpjL7pMnT4q8vDwBiG9+85vT1v31X/91dJR0U1OTEWYKMY9fTdghNzY2FjU2Nr7e2Ng40tjYuGmW9Z7GxsYfNjY2vtjY2Ph3i2gzKZx2EgkhxDe+8Y3oyXLw4EEhhBBf//rXL1pmBlbV+3vf+54ARHZ2tnj55ZcvWvab3/zGZAsTI516/+QnPxGA8Hg84plnnrlo2bPPPptU+1Y9VxZiPrt/9atfXdQBi11m8HmWFgef1djYWN7Y2PjjORz8exsbG78W+fz9xsbGqxdoMymceBKFw2HxkY98RACirq5OPPTQQ7P26s3Aynrfe++9AhAVFRXihz/8YbRX/6//+q+Wtns+0m33Aw88EO2t//jHP56zV58ITtU8trf+k5/8ZM5evQGk3sGLKUc+l4N/sLGx8YbI599vbGz8/AJtJYVTT6KxsTFx9dVXCyD6Y8IJdBFW1ntyclLcdNNN0zT70pe+JISwtt3zkW67Q6GQuOOOO6Zpdt9996WkbadqHg6HxYc+9KFpmt19992LKnqXYub0qy4hpr8kiBdFUX4MfFtV1SMzlj8M/JOqqgcVRbkJeJeqql+asc3HgI8B3HfffY36i8VECAQCZGVlJby/WSzG7r6+Pm677TY6Ozu56667+O53v2v6gBOr633hwgVuv/12Wlpa2LlzJw8//DBut9vyds+FEXaPjo5y5513cuzYMW644QZ++tOfpqSGuZM1Hx8f5/3vfz8HDhxAURR27dpleAG7mpqaOZ3Bgv89RVEqgV/MsuqDqqr2zLPrBUAvO1cEnJ+5gaqqDwMPR/5M6k7T2dlJTU1NMk2YwmLsrqmp4ZVXXmHv3r184AMfsEQFRKvrXVNTw0svvcSTTz7JBz7wAXJzcwHr2z0XRtn9/PPP8/jjj3PXXXdRWFiYkjadrvnu3bt57LHHuPPOOykuLk6/YXGwoIOPOPHtCbT9MnAT8AJwC/CjBNqQRKirq+Puu+822wxbUVlZyT333GO2GbairKyMj370o2abYSuKi4stq1lSefCKojwB3Ax8X1GUeyLLHoqs/i1QryjKi8C4qqqvJHMsiUQikcRHUgE2VVUvyuRXVfXjkd9B4J5k2pdIJBJJ4siRrBKJROJQpIOXSCQShyIdvEQikTgU6eAlEonEoUgHL5FIJA4l6ZGsEolEIrEmsgcvkUgkDkU6eIlEInEo0sFLJBKJQ5EOXiKRSByKdPASiUTiUKSDl0gkEociHbxEIpE4lOSnazEARVG8wEeA46qqvmq2PfGgKEoW8EfASTuVTLar5jbX+0+AY8DLqqqOmmzSoojo/QmgCdinquqIySYtGrue4/Fg+YFOiqJUAN8HWtFmhnoKeFJV1QEz7VoMiqK8A/g74BWgFHgE2KOqathMuxbCrprbWO9i4G+BYcAf+f0jVVW7zbRrIRRFuQr4J7TJfQLAceBfVVX1m2rYIrDrOR4vlg3RKIqiz0s3BnSqqvop4EGgErjLNMMWiaIoHiAH+LiqqvcC/w3cqKpqWFEUcydUnQM7a25TvfUpLYcBH/DnwFcjf99rll2LIaLpJHCvqqr3Ab8GVqiq6req3mDvczwRLNeDVxSlDPgG0A/sBt4APgX8BGgDrkabAvAHqqq2mmTmrCiKUg08APwwMtl4CXBBVVWhKEodWi/tj1RVDZhq6AzsqrmN9S4Fvo4WIv0d8CTwP4Gjqqo+G/leXwb+UVXVt0wzdAYRu74N/L2qqq8pirJEVdWhyLpCNCf/e1bswdv1HE8WK/bg70KbsPsXaP+AFUAhcAWavacAF1qPxzJEHvm+CmwAvgagquqAqqr6HfQqoM1qziaC7TS3ud470UIa3wDeHfk7C2hQFKVOVdUutHBHpXkmTkdRlALg/wDLgT8D0J17hCvRYtmWc+4RbHeOpwJLOXhFUdzAMuAXqqoeBH6J9g9QgUa0R+4eoA7IM8vOOTgPfFlV1ZuACUVRPgzRl1CgXcC/VhTlvYqifEJRlHyzDI3FxprbUu8I2cBzqqo2A/+G5shHgQLgfZFtVkSWmUpMuMUPfEdV1WuByRi9dYfoA3YrinKHoihfUhSl3ARzZ8XG53jSWMbBK4riirwMGwL+R2Txr4B6tLvrb4DrIhN9ZwE9phg6B5E5aHWbvgHcqyhKdkwP8g60i/lu4HWzsiQij9L6Z9toHms32Erv2XrhE8C1kc+vosWyzwKPA2WKovwazdm0G2HjbOh2609EkfNEfwEZq/dkZNkdaC8tPwg8papqn7EWT6EoSm3MZ7ddzvF0YEoMPhIr/Tu0N+7PRF6QhfWTSVGUF4AvqKr6qqIoHwKuU1X1k5HeQqOZ6W+z2a6qaihmvSsSA/574LSqqn+nKEoR8DngiKqq/2GS3cVocd8cYK+qqj+LOfktq/lsds9Yb2W9vwJsQnMiv1BVtTdm/W+A/xeJub8T+KCqqh+PXAsbVFU9bFG7Z9O7EngPMKaq6r+bYXeMfTejnS9fVVX1vxRF8UY6A5Y9x9OJ4T14RVFy0WJ5K4A/BVBVNRQ5aTyRzf4B+LSiKJuApUCzoig+VVUnTXbus9o+YzP9kfbPga8qinIQqFBV9S9MdDbLgG+i9RIfROt9rYpkmOghDctpPpfdkXW6zlbUuwi4Hy3m+1FgM5qmsSGkfwTuUBTldrT3CCOKouRFrgWznPt8ds+m918pivIGUKOq6g/Mdu4RBoBx4A8URSlQVTVo5XM83RjWg1cURQE6VFXtURSlTFXVc4qi/Ag4oKrq/5ulJ3wb2pvtNcCnzMwJTsD2YrQXOeuBL6mq2mKi3WdUVe1VFGWjqqpHI8u/ipZt8rcztreE5gnYXYx19NbPk0JVVYcjy/8ROAT8WFXViZjtL0fL3LgE+L+qqnbaxO4c4H+jxbG/qKrqaTPsjtjSCHRHXkyjKMoVwFqgCshSVfUbsdenVc5xo0i7g4/0yv8R7eI7Dbykqur3I+vWoMVJ362qar/++Be77yw9ZMNI1PZIj6HMRAcZa3cr8KKqqo9E1rnQwhdvqKq6V18WY7tpmsdrd8x+VtL7NNpI1Icjj/53A/8LOAqcBH5iFaeSjN2KopSoJg4KmmF7C/Caqqr/omiDrz4G/F+0p79/RsvuGYzd10y/YiRGhGjygPOqqr4DLa3t3YqirIvEf5vQcoD/T2TbitgdLfBPSMh2VVUDJl/EsXZ/BbhVUZR1EdsE0ACcUBQlT1GUotibqsmax2W3vpPF9P4qsFNRlHWRF5DPqKq6DS2kV4L2gtUqJGy3mc49QqztfwXcoiiK7uyfRMuyWgo8hGZ/FAv4FcMwwsG7gRsURVkeeXR+AXi//nJPVdW/AP63oihvARsNsCce7Gr7rHZDNBtlPXAb2oCPrWYZOQtOsvsDAKqqtkW2uRVYh7XqP9nVbrjY9ueB24Fy4NNoqZBH0WrkWHEshCGk1MHH9qoif2dFHo1+BfxlZPEPgXdE7rYoivJJYB/wh6qq7kmlPfFgV9vjtHsD4EF7SXw18D9UVX3eSHtj7HS63TcoirI+8rTxT2ix9vvVmIwUI7Gr3RFbF2v7LcA54G+Az6uq+kW0a9OUdxtWICUxeEVLk3oILW/326qqtioxKXiRbZ4Bvq6q6l5FUR5AG5b9uKIoxaqqXkjaiASxq+0J2P2naMOzXwc2qqr6krQ77XYfBPYCDaqqvi3tjo8EbP+/wJuqqj4RWTftnV4mkioH/z60u+dhYFCN5CpHXoR8B62XO4ZWxrUHuA7tzmpKtkMsdrU9Qbv/SNVGT5pGhtlt1/PEdLshYds/qJqY1WM1EnbwiqK8H+3lxc/USP0JRVF2Apejld08oCjKNrS38X+uqup5RVGqgGuAF1RzR7rZ0nZpt7TbyXbb3XYrEreDV7R0tU+jFXM6AYTQ0tn2RIR+H4Cqqv84Y79pj1ZmYFfbpd3GIu02HjvbbmXifskaiWkVot1h/wKtlsbdijYKrxt4ExCKotylKMqtEI2Fmf5PsKvt0m5jkXYbj51ttzKJZtG8DZQripIHPIs2SOKDkXVvAjehFSSqgamCRRbBrrZLu41F2m08drbdkiTq4I+iDQVeq2qDBt5AKwYFsAOtStt2NTLq02LY1XZpt7FIu43HzrZbkkQHLxwDrkcbPXYaKAb0ettPqKr6XymwLV3Y1XZpt7FIu43HzrZbkoR68JFHo58CXcAP0OosH4qsC6bMujRgV9ul3cYi7TYeO9tuVZLOg1cUZSNwUrXm1GjzYlfbpd3GIu02HjvbbiUsN+m2RCKRSFKDZabsk0gkEklqkQ5eIpFIHIp08BKJROJQpIOXSCQShyIdvEQikTgUq83SIpGknchQ+C8Araqq/lhRlHuAH6FNbPFtU42TSFKI7MFLMpE8tJmA7on8/Tzwh8DjZhkkkaQD2YOXZCJq5Pc7FEURQBuwHLgfbVLvVqAM+AnwYbSJJb4HPIx2zXxUVdUnFUXxAV9HuznkA88A98qa5BKrIHvwkkzkS5Hfx9Gc82xhGb0GyitoE0//M9pcnxXANyPr/hT4HFrP/++AdwP/khaLJZIEkA5ekok8Hfndq6rqL4CRWbYJA58BHo38/TNVVf8BrU7Kisiy2yK/P44W8slHq3ookVgCGaKRZCKLqc8xpqrqpKIoei2UwcjvEOCJ2S6I5uhDkb9lp0liGeTJKMlEhtB66KsVRfkQWvw9EX6L1kn6Y6Ae2InWm5dILIF08JKMI1Kh8G/Q6o3/K1O973j5RqSd69Fewr4bLSNHIrEEspqkRCKROBTZg5dIJBKHIh28RCKROBTp4CUSicShSAcvkUgkDkU6eIlEInEo0sFLJBKJQ5EOXiKRSByKdPASiUTiUP5/apYdpVmcheUAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "n_categories = 2 # \"smooth\" and \"non_smooth\"\n", + "embedding_size = 2 # embed the categorical variable into a numeric vector of size 2\n", + "categorical_embedding_sizes = {\"curve_type\": (n_categories, embedding_size)}\n", + "\n", + "model = TFTModel(\n", + " categorical_embedding_sizes=categorical_embedding_sizes, **get_model_params()\n", + ")\n", + "preds_st_cat = test_case(\n", + " model,\n", + " train_series,\n", + " predict_series=[series[:60] for series in train_series],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5ebcfe69-b5e3-40a6-971d-ddbc48d032a9", + "metadata": {}, + "source": [ + "Nice, that seems to have worked as well! As a last step, let's look at how the models performed compared to each other." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "dd7784f1-a7b4-4f37-9898-66120a421a97", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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k6lVq5aer8GFRERE0LBhQw4fPsz+/fvNNqfaop2+pkJq1qxJTEwMSilWrlxptjlegz38EBsbS2BgoMnWVB/8/PwYNGgQoO8u3Yl2+h5gxYoVJCYmekRr1KhR5OTkXPZ+n3zyCVOmTDlveXmlbw8ePFhu3v65nLudlJL77rvvsm3zNvT8y+5Dx/Xdj3b6HsATTl8pRWlpKb/++iuhoaEuO255X8LKOn0hBLNmzXKZbWahH+K6Dx3Xdz/a6VeSzz77jO7duxMZGcltt90GnF0yecKECWRkZHDw4EHeeecdXn31VaKioli1ahWZmZnccMMN9OrVi169erFmjTGBWGZmJvHx8XTt2pW//e1vtG7dmuPHjwPw3//+l27dutGtWzdee+01wHCqHTt2dJRAPnz4MG3atCErK+uSbCxb1rkiioqKsFgsbN++nYiICHJzc5k+fTqrVq0iKiqKV199lYMHDxIbG0vPnj3p2bOno7T0udutWLHCUYAuLy+PO+64g4iICLp378633357nvbGjRvp168fkZGR9O7dm9zcXAoKChz79ejRw+GAY2Ji+P333x372ktcr1y5kqioKKKioujRo4fTYw4OHTrE/v37CQ0NpUePHk4dS3M+nTp1omnTpmRkZLBr1y6zzameVDRqy4telcZdo+V27Nih2rdv75j0wV52uGzJ5Jdeeumsksn20stKKTVx4kS1atUqpZRShw4dUp06dVJKKXXPPfeo5557Timl1IIFCxwTS0gpVbdu3VReXp7Kzc1VXbp0UZs3b1YHDhxQFotFrV271nHs1q1bq23btl2SjWXLOn/88cfqnnvuOe9cr7nmGhUdHa0A9dlnnymr1aqWL1+urr76asc2p0+fVvn5+UoppXbv3q26d++ulFLnbVf28yOPPOIoG223qyyFhYUqPDxcbdiwQSmlVE5OjrJarerll19Wd9xxh1JKqZ07d6pWrVqp/Px89d///lc98cQTKjU1VaWnp6sOHTo47LeXwM7NzXVqchr7dQLUtddee946Lx+dWWW0J0yYoAD15ptvmqJ/OXi5tutLKwshXgT6AQeBO6WU1jLrrgX+DViBTVLK+23Lc4FNts3ulVJud8aGBnOXXGSLyk1dmHVTxZOxL1u2jHHjxjnq5tgLkZUtmXzmzJkKywT/9ttvZ02peOrUKfLy8li9ejXff/89ACNG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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metric\n", + " no st bin st cat st\n", + "RMSE 0.133118 0.027695 0.047532\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX0AAAEXCAYAAABBFpRtAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAABlI0lEQVR4nO2dd3wUxfvH33epJEDoAULvJSGRDDWEIlUEBATpiA1RmvIFRRFF/SkoIoodUJEqiF2QJkhHGCD0GmkJJISShJCElNvfH3t3Bkgouctdkpv363Wv3O3M7ufZzd5zs8/MPGPQNA2FQqFQuAZGZxugUCgUCsehnL5CoVC4EMrpKxQKhQuhnL5CoVC4EMrpKxQKhQuhnL5CoVC4EAXB6Wu5fcXExOR6X1tfrqrtbH2l7VraztbP59rZUhCcfq7JzMxU2i6mr7RdS9vZ+gVRu1A7fYVCoVDcjHL6CoVC4UIop69QKBQuhHL6CoVC4UIop69QKBQuhLstOwsh/IC1QAOguZTyYJYyN2AOUBvYLaV8wbx9LPAYcBkYLKVMtMUGhUKhUNw7trb0k4GHgeXZlHUDzkspwwFfIUQLIUQZoAfQClgKjLRRv0ASFRVFXFycs81QOJCoqChSUlKcbYZCYZvTl1KmSylz8l4tgTXm96uAMKAJsFFKqWXZ5jIkJyfzyiuvUL16dapXr86MGTPIyMhwtlmKPOb8+fPUqFGDsLAwUlNTnW2OwsWxKbxzF0oCltBNAlAqh223IYQYDgwHGDVqFB07dsyVAenp6URHR+dqX1u5VXv9+vW89tprnD17FoCMjAzGjx/PN998w9SpUwkNDc0zbUeTn657ftDeunUr6enp7N27lzFjxvDGG284TNsRqPstf2oHBARkuz0vnX48UNz83g+4Yt5W65ZttyGlnA3MNn/McTrx3YiOjs7xxPMai/b58+cZO3Ysy5frEbDg4GC+/PJLrly5wsiRIzl8+DA9e/bk2WefZerUqZQoUcJu2s4iP1z3/Ko9Z84c+vTpQ6dOnRyunVeo+61gaefl6J1tQAfz+87AVmAX0PqWbYWSzMxMPvnkE+rVq8fy5cvx9fXlgw8+QEpJ8+bN6dq1K4cOHWLixIm4ubnx5ZdfUq9ePZYsWYJawrJwYem/sfygP/7441y6dMmJFilcGZudvhBiJdAJmCOEGCaE+Mpc9AdQRQixGUiVUm43x/9XCCG2AgOBz23Vz4/s37+f7t27M2bMGK5du8YjjzzC4cOH+d///oe7+38PVz4+PkydOpW9e/cSFhZGbGwsAwcOpHPnzpw5c8aJZ6CwJxYH/9xzzxEeHk5MTAxPP/20+nFXOAdN0/L7K9dERUXZsnuuSExM1Pz9/TVAq1y5svbLL7/c036ZmZna3LlztVKlSmmAFhQUpGVkZOTKBmecd37Rz4/ao0aN0gDto48+0k6fPq35+flpgPbVV1/lubYjUPdbvtXO1qeqyVl25v333yc2NpaQkBAOHz7MI488ck/7GY1GnnrqKY4ePUrVqlU5cOAA8+bNy1tjFQ7B0tIvU6YMVatW5csvvwTghRde4OjRo840TeGCKKdvR6KiopgxYwYAU6ZMoWjRovd9jLJlyzJ16lQAJk+eTFJSkl1tVDierE4foH///gwZMoSUlBQGDRpEWlqaM81TuBjK6duR1157jZSUFPr27YsQItfH6d+/P02bNuXChQt88MEHdrRQ4QwsHblly5a1bvv000+pXr06e/bs4fXXX3eWaQoXRDl9O7F3717mz5+Ph4eHtaWeWwwGg9XZT58+nfPnz9vDRIWTuLWlD1C8eHEWLlyI0Wjk/fffZ8OGDc4yT+FiKKdvBzRNY/z48WiaxqhRo6hZs6bNxwwPD6dXr14kJyerlmABRtO0bJ0+QMuWLZk8eTKapjFkyBCuXMl22opCYVeU07cDK1euZP369ZQsWZLXXnvNbsd97733cHd355tvvmH//v12O67CcVy/fp0bN25QpEgRfHx8bit/7bXXaN68OdHR0Tz77LNqGKciz1FO30YyMjKYMGECoHe8liqVbWaJXFG7dm2ef/55NE2zaigKFpZWftZ4flbc3d1ZtGgRRYsWZfny5Xz33Xc3lWsmjSs7rnJuURRpV1WHr8J2lNO3ka+//pojR45Qo0YNnn/+ebsff/Lkyfj5+bFmzRpWr15t9+Mr8hZLJ+6toZ2sVKhSlce++Ba3Og0YPXo0J06cIGFfIkfeOMaGkE3seHgnB8YcYlOLrZz/+YJ6GlDYhHL6NpCYmGiNt0+bNg0vLy+7a5QpU4ZJkyYBMH78eDIzM+2uocg7cornZ+W1fcf52dOP4m9/ROnJc5g5YT8bOm/n1KenSY1OxTvAG7+Q4qTFpRHx9H52D95LSvSd0zSbTBq7j2m8u0DjwbEmek0ysfe4+rFQ5G3CtULP+++/z8WLF2nRogV9+vTJM53Ro0fz+eefc/DgQb799luefvrpPNNS2Je7Of0dcVf5NjIKNxP4XNdIrFaa76vB749oPJJYjOGh1Qlq6Q/AuYVRHH3jOBdXxXFlyxXqvl6HKk9UxmA0AHDhksaaXbB6l8baXXAp4WatX7doDOmk8X9PG6jsb8izc1bkb1RLP5dknYg1Y8YMDIa8+xJ5e3urCVsFlDs5/RuZJl7YfQSAbqs0Pptm5Lmd4PnvOa77GlhcIYl25w8weNs+NsVdocrQyrTeFob/w+XISMrk0EtH2NZ1J198fI0OL5WhYm+NYVM1lqzTHX7V8jC8O/zwloFxj4G7G8xfDXUGaUyaYyLxumr5uyLK6eeSSZMmkZqaSt++fWnRokWe6/Xr14+mTZsSExPD9OnT81xPYR+ym5hl4eOjpzieeJ0KcfDInxrhPzThnQ86MizhBImTxuCxZwfuBgN/no+j18Y9TDsYiXcFb0LnP0DjeSFQyouEXfFUfHs7D+w+S1EvjYdbwKyxBo4uNHBqqYGvJhjp09bAjFFGji408Fg7SE2DdxdArQEaX/yikZ6hnL8roZx+LtizZw8LFizAw8ODadOmOUTTYDBYnyymT5/u1EUrFPdOTi39Y4lJfHjkFABPLjBRqlZRigfry0+89eabhPj5cvG912mzaikvNaiB0QDvH/6XeZFRnInRGPlPWR4r14JVJQPw0DQGx/3LOr+9/PxqJqMfNVC3iuG2p88aFQ0sfdPI9i8MhAVBXDw8/6FG0DCNrQeU43cVlNPPBe+88w6apjF69Ghq1KjhMN1WrVrRu3dvUlJSrD8AivxNdk7fpGmMk0dIM2l0OetF/RMQ0K+i1Ul7enqyaNEifHx8+OGbuVTZ/w8fNK4PwP/kERpMushPm0Dz9aDClIY8sCwUt5JuXN5wmW0dd5B04vodbWre0MDmTw38+LaBWgFw7Cz0maxa/K6Ccvr3yZUrV/jjjz8wGo2MHz/e4fovv/wyAEuWLFHr6xYAsnP68/+NZvuleMp6etD7s1QwQkDfCjftV7duXWbOnAnAiOee48ZeD7wOV0czgGf7g3TuFs+xhQZeGWygQvsy1Flck2INi3I9MpltHXdwcV1OS1frGAwGercxcGi+gQbVIOYK/LLZvueuyJ8op3+fLF++nLS0NNq3b0+FChXuvoOdadKkCbVr1yYmJob169c7XF9xf9zq9GNSbjBl/wkA/pdQBp9EjTJtS+Ndwfu2fZ955hkeeeQRrpUcz4hZZYlZXx3fsxUxuJs4Xmcf173/a9F7BnjSYmUzynf3J+NaBrL/Hv795NRdx/R7ehh4vqf+hPH5L6ql7wrYY+Ws94QQm4UQC4QQHlm29xJC/G1+nRVCjDVvP5Fle+5WPHciixYtAmDQoEFO0TcYDFZtiy2K/MutHbkT9x4lMT2DThXK0GDhNUAP7WSHwWBg8Kh5UGUSaJk8XG8tJ8bUo0vFMsSnZdB3014upKRa67sXdeeBb4Kp/XJN0ODolOPse+4Amal3ntsxpDP4FoG/98KR08rxF3ZscvpCiGAgQEoZDhwFrIPVpZQ/SynbSinbApHAL+aiBMt2KeVaW/QdzZkzZ9i0aRPe3t706tXLaXZYnP5PP/1EcnKy0+xQ3JnMzExrErVSpUrxZ/RFfou6iK+7G2+WrELinkTci7pRvqt/tvtfvKox6lO9c5czb7Lqm27sj9jN3OaNaFLaj6jkVB7btJfEtHTrPgajgdov1aLxvBDcfN04/8MFdnTfReqF1Gw1AIr7Ghhsbn598aty+oUdW1v6LYE15vergLBbKwghygNeUkrLoq9FhRAbhRCLhRD2S1TjAJYsWQLAI488QvHixZ1mR61atWjWrBlJSUn89ttvTrNDcWfi4+MxmUyUKFGCFAxM2KOvkjUpsBb8fBmA8j3L4+bjdtu+JpPG4+9qxF6BNiEw9tEUMjMzGThwIKbUFJa0CqF2MR8OJSQxeOs+0kymm/Yv392fFn82o0iVIiTsSWB7151kpuTc4n/OHOL5bhUkJSvHX5ixdUZuSeCC+X0CkJ0T7w38mOVzmJTyshBiKPAmMPrWHYQQw4HhAKNGjaJjx9xFgdLT0+02tFHTNOvyhV26dLnrce2pnR0PP/ww//zzD19//TXh4eEO1b4bztTPT9qRkZEAlChRgld37ON8yg0Ci3rzkJeB40vOAeD1oGe29s5Z6cuqf4pToqiJ6U/HUcr3edas/pMjR47w9NNPM2PGDD6pU5Eh+0+zJe4qEzPT+cBgwJh1mGYJqLGgKpFPniIlMoUDnxyi7KDS2dpepgiIOqWRxz35fPlVBrW/c5qHO523o8lP//P8pB0QEJB9QU6L597LKzQ09PnQ0NCh5vehoaGhn2ZTZ31oaGjVbLb7hIaGbroHnVxjz0WLIyIiNEArVaqUduPGDYdqZ0dsbKzm5uamubu7a3FxcQ7Vvhv5fLFoh2lv2bJFA7TQTl20UkvXaGWWrdUOXE3U4jZc0laUWqWtf2CjZso03Xac3UdNmke7TI3wTO2XTf+VHzx4UPP29tYA7YcfftA0TdMOXE3Uqvy0Xiu5dI02Xh7WTKbbjxezIlZbUWqVtq7BBi0jJSNH+xeuNmmEZ2ohT2Zme5x7PW9Hk5/+5/lMO08WRt8GdDC/7wxszVoohPAnS2hHCOEphLBkJQsHTtqo7zAsnab9+vXD09PTydZAuXLl6NSpExkZGSxbtszZ5iiywdKJ61m7PhrQvnxpAksUI3qpvhJaQL+K1rw5FpKSNfq/qZGeASN7wSPh/5U3bNjQOht7+PDhREVFEViiGIvCgvE0GPg6Mop3D0beZke5h8pSrGFRbsTcIGpxzi3DPm2hjB9EnIAdh2w8eUW+xSanL6WMAGKFEJuBhsCPQoivslS5NbRTEtgmhNgEjAfesEXfUZhMJhYvXgw4b9ROdqhRPPkby3BNrWIlABr4FSXjWgYxf8QCEPDY7aN2Rn+scSIKgmrA9Odvz+c0cuRIunbtytWrV3n88ccxmUy0KleK6XUr4WYwMOPIKb44fuamfQwGA7XG66u5RX58ClOa6bbjAnh5GnjqYf296tAtxOT0CJCPXrnGXo9e69ev1wCtWrVq9/zY64jHvqSkJM3X11cDtMjISIdq34l8/sjrMO2pU6dqgPbAFwu0kkvXaEtPn9fOLYrSVpRapW3ruuO2/Ret0cMrRTpkaodO5XyfxcTEaGXLltUAbfr06VbtxaeitZJL12gll67RFp+KvmkfU6ZJ29his7ai1Crt7PxzOR7732iTZmidqXk+mKnFXc0/93p+1c/n2nkS3nEJso7Nz8tsmveLr68vPXv2BLA+iSjyD5aW/rWifgDUK+5rDe1U6n9zJ1tktMaIGXrr+qPRBhpUy/k+8/f359tvvwXg1VdfZe/evQAMqFaRd0LqADBm12FWRl+07mMwGqg5Tk8ZcnLmv5jSs2/tV69ooGtzSEuHb1be3/kqCgbK6d+F1NRUfvjhByB/hXYsWGxauHChWlEpn3Hp0iVw9+CqhxdGA1RKMHJ5yxWM3kbKP/Lf2Py0dI0Bb2pcS4ZH28Az3e9+7IcffpiRI0eSnp7OwIEDSUnRR9s8V6cq/6tfnUxN46ntB9hy8b/F1iv2qoBvTR9SzqRwfvmFnA5tHb755a8aJpO6pwobyunfhRUrVpCYmEjjxo2pX7++s825jY4dO1K2bFmOHTvGnj17nG2OIgtxcXEYKwRgMhio7uvDleV6LN+/azk8ilsnr7NwDew6ClX8Yc5Lt2fHzInp06dTv359jh49yttvv23d/mpgTZ6qWYkbJhMDt0Sw94q+morBzUDNF/9r7WuZ2Tv0Lk2hWnk4dQFW78zVqSvyMcrp3wVnp124G+7u7vTv3x9QHbr5jUuXLuFWuRoAdYv7ErXMEtq5uQP358268500xEDJYvcePixSpAiLFy/G09OT+fPn8/vvvwN6x+17jevxaJXyJGVk8tjmvRxP1PP0VOxTAZ9qRUiOTOb8z9m39t3cDIx4ROXjKawop38Hrl69yooVKzAYDFbHmh+x/CAtWbJEraGbj8jq9Ktddyc5Mhkvf09Kt/lvglRyqsY6qb/v3vL+NUJCQqyrqj355JPExMQAYDQY+LxpQzqUL83lG+n02bSH+LR0jB5Gar6gt/YjZ/yLlkP45smHwdMDVmyH0xeU4y9MKKd/B7Jm1KxYMfukWPmBpk2bUqtWLZV5M59x6dIl3CpVBaD0bj3mXrFvRYzu/33t/tqtr2Ql6kGFMrkbJPDCCy8QHh7OpUuXGDZsGCZzSgYPo5F5LYNpXKo4UcmpjNt9BE3TCOhXEe9K3iQdv07M77HZHrNsCQN924KmwezfldMvTCinfwfye2jHgsq8mf+4ceMGiYmJuFXWnX7RP/S4+q2hnd+36Q61e8vcjwozGo18+OGHlCpVitWrV/Ppp59ay3zc3ZjTPAhfdzd+ORfLsjMXMHoaqTm2OgAnZ0Tm2Nq3pFye+wfcSFOOv7CgnH4OnD17lo0bN+Lt7U3v3r2dbc5dsTj9H3/80TqSQ+E8Ll++DB4euPlXxA0oF5lJ0XpFKVa/mLWOyaTxxzb9fY/bUhXeHxUqVGDu3LkAvPTSSxw4cMBaVr2oD1ND6gIwYc9RziSlUGlgAF7lvbh2KInYPy9me8wWgdCopr6s4k+bbLNPkX9QTj8HLBk1e/To4dSMmvdK7dq1adq0KUlJSaxdW6AyVhdKLl26hFvFymA0UkXzxCMDigcWu6nO7mNw4TJULgfBtWzX7NWrF08//TQ3btxg4MCBpKb+l055UPWKdAsoR1JGJiN2HgRPIzXHWFr7/2Y73NdgMFiHb879Q7X0CwvK6efAwoULgfwf2slK1jz7Cueid+LqTrVakp7M1qe6z011LKGdbi2x26S/mTNnUrt2bQ4ePMjEiROt2w0GAx+J+pT39uSfS/F8dPQUlYdWwrOcJ4n7Eolbdynb4/V/ELw8YcNeiLqoHH9hQDn9bNi/fz8HDx6kVKlSdOnSxdnm3DP9+vXDzc2Nv//+2zobVOEcLl26hNEcz69k7iv1rXGr09f/2hLPv5WiRYuyaNEi3N3d+fjjj1m1apW1rJSXJ581DQTgvUP/si8liRqj9B+myI/+zfZ4JYoZ6BGmd+guUg+QhQLl9LPBMgO3b9+++SKj5r3i7+9Px44dycjI4JdffnG2OS5NXFycdeROhUh9GG1Wp38uViPihL5MYbsH7KvdpEkT3nrrLQCGDRtmzfYJ0K58aUbUrkKGpvHsjoOUHlgBYxEjV3fEkxKdfV/QkE76j9KCNZqa9V0IUE4/G/78809AXyGroNGjRw+Am1p4CseTdbhm2f03APCp+Z/T/2O7/rejAG8v++dzeumll2jdujWxsbE89dRTNznr1xvVor5fUSKTkpkSGUm5Dvr6vTG/ZT98s0szPeXyoVN62mVFwUY5/VuIjY1l9+7deHt707ZtW2ebc99YwlFr164lPT39LrUVeUXMlasYy5XHTdMoe9qER0kPPEv+99T4+1bdCfcIy5sEfm5ubixYsAA/Pz9+//13vvrqv4zn3m5uzG4WiKfRwHf/RnOkh/5jdOGXmGyP5eFuoH97/f2CNaqlX9BRTv8W1qzRl/xt06YNRYoUcbI190/16tWpWbMmiYmJ7Nixw9nmuCxn0jIwGI2Uu5GJu+nm0E5SssZfe8BggIdb5J0NVapU4csvvwRg3LhxHD161FrWsEQxXg+qDcAU43kSyxmJlwmkRN05xLN4HWRkKMdfkFFO/xYsoZ2HHnrIyZbknnbt2gH/nYvC8cQY9BE7VVL0Rc99sjj9tVJPXdysAZQrmbepuvv378+QIUNISUlh4MCBpKWlWctG1KlCG/9SXE5LZ9Fz+lPIhRxCPE3qQ53KEHsF1u3OU5MVeYxy+lnIzMy0tvQL0qidW7GEpVRc33lc9vIFoHqC7vyztvTtMQv3fvj000+pXr06e/fuZfLkydbtRoOBz5o0pIibka3l04guDzE5hHgMBsN/HbqrVUu/IGOz0xdCvCeE2CyEWCCE8Miyva0Q4pwQ4m8hxF9Zto8VQmwVQvwmhMhXs56klFy+fJnq1atTp04dZ5uTa5o3b06RIkXYu3evNQGXwrFc9ysJQLXz5jH6ZqdvMmmsMHfi5ibBWm4oXrw4CxcuxGg0Mn369JvyM1X08aZ/NT01xKrORuJ3J5B8NvsQz+BO+t+fN8O1ZOX4Cyo2OX0hRDAQIKUMB44CfW6pslRK2VZK2d5cvwzQA2gFLAVG2qJvbywt44ceeihfrZB1v3h7e1tDPKtXr3ayNa6HpmlklC0PQKWT+jbfGnrLf+cRuHgVqpaHwBqOs6lly5ZMnjwZTdMYOnQoV678t7jKc3WqYAC2NIGEYhDzW/YNhWoVDIQ3gpQb8NNGBxmusDu2tvRbAmvM71cBt2YQedT8FDDW/LkJsFFKqeVQ36lYYuAFObRjwXIOKq7veGITEjGW9UdLT6fEAX24pq95uOZ/oR37zcK9V1577TWaN29OdHQ0w4cPtw7jrFXMly4Vy5LuBuvaGLjwa/ZxfYAhnf8bs68omLjbuH9JwLISQwJQKkuZBOqa3/8qhNhirp+YQ30rQojhwHCAUaNG0bFjx1wZl56eTnR09D3VvXr1Kjt37sTT05O6deve83720LY36enpPPCAPuNn1apVnD17Fjc3N4fqO/Pcna295sQpADwuXoSkCriVdONi0kVIgh//LgN40LLuZaKj0+58wFxo340ZM2bQqVMnfvzxR2bOnEm/fv0AeKyUD3+eh3VtoPurCZzaeQrPgNsnJrasY8DT3Z/1e2DX/gtULG1y6jWH/PE/z4/aAQEB2W631enHA5a4vB9gfWaUUiZZ3gshfgeCgRigVnb1syKlnA3MNn/MdZMiOjo6xxO/lU2bNqFpGuHh4XaJ59+Ptr2Jjo6mVatW1KxZk8jISKKjo2nRIg/HBmaj78xzd7Z29DHd6Ze6kgxAsVrFCAgI4PQFjWPnNIr5wKMdyuDpYb+W/r2ed0BAAJ9//jmPP/44r7/+Oj169KBWrVpUrKgREn2FCK6xuZlGo38gYPTtxwsAuoeZ+HEjbDhYnpcGGpx6zSF//M8Lkrat4Z1tQAfz+87AVkvBLZ20rYCTwC6gdXb1nU3WeH5hwXIuahSPYzmSoC9NGHBFb6/4VreEdvTyzk2xq8O/X4YMGUK/fv24fv06gwYNIj09HYPBwMi6+gziPzsYiP4154XTs47iUWkZCh42OX0pZQQQK4TYDDQEfhRCWKb+PSaE2CmE2AZESyk3SSnjgBVCiK3AQOBzW/TthclksjrGwhDPt6Di+s7h1A19JnSNy3p4xJJ+wTIL11FDNXPCYDDwxRdfULlyZXbu3GldVL1HJX8CvL2I8TewKf0ayWeSs93/oeZQ2g8OnoJ9Jx1pucIe2BreQUo54ZZNz5q3zwXmZlN/JjDTVl17EhERwcWLF6lcuTINGjRwtjl2o23btnh5eSGlJC4ujrJlyzrbJJfgvMkIRqgTq8/o9q3hQ+J1jb8jwGiErs2dax9AyZIlWbBgAe3ateOdd96hU6dOtGrVimfrVuH1fSdY2cFA/19jrTn3s+LpYaBfO43Pf9Fb++N6Od5+Re5Rk7O4edROQR6qeSu+vr60bt0aTdOsk84UeUtiWjqJ7h5oaTeodl4fpulbw4c1uyA9A1o0hDIl8sc91qZNGyZOnIjJZGLw4MEkJCQwtHoAvhg5UtfAlq05dxJaRvEsWgcZmY6yWGEPlNOncMbzLai4vmM5kqjH8zOjz1Ik3hsAnxq+eZ5gLbe8+eabCCE4c+YMI0eOpLinB0Nr6J2DP1ROJvl09iGeZg2gdiU9LcPmA16ONFlhIy7v9OPj49m+fTvu7u60b9/e2ebYHUtcf/Xq1ZhMJidbU/g5mqAPWvOIisGYYcSzrCdGXzdWmHPfOWoW7r3i4eHBokWL8PHxYdGiRSxatIgR9atiNMGOUNj7e1S2+xkMBgabO3R/3FLwEhO6Mi7v9NetW0dmZiZhYWEFYi3c+6VevXpUrVqVuLg49uzZ42xzCj1HzS394lH6dBTf6j7sPQGXE6BGRahX1ZnWZU+dOnX4+OOPAXj++efJjIuls4cfJjcD31w4n+N+lrQMq3Z5q7QMBQiXd/qFaRZudhgMBjWKx4EcTdRb+v4X9BE8PjV8rCNcmjVw/Czce+Wpp56iV69eJCYmMnjwYMY003NErKqbRuzJxGz3qVHRQFgQpKYZ+GWzI61V2IJLO31N0wp1PN+Cius7jmPm8E6183ruQd8aPhz4V28FB9XInw4f9B+jOXPmULFiRbZu3craeV8SdNWDlCIGZm/IeVxm/wf1c/p1i2rpFxRc2ukfOHCA8+fPU6FCBRo1auRsc/KMBx98EA8PD3bs2HFToi2FfUlIzyQmNQ0tNZVa8X4A+Nb0ZX+kXt6ophONuwdKly7Nd999B8CUKVPorplH6BiukJFDf1A3cx/F6p2Qlq4cf0HApZ1+1glZ+fWx2x4UK1aMVq1aYTKZWLdunbPNKbScTE4FIDP6DFXcKwNQpHoR9v+rlwc5MKtmbunQoQP/+9//yMzMZM4HIygfBxeLayzfcy7b+tUqGKhbOZ2kFNgY4VhbFbnDpZ1+YY/nZ0XF9fOeyGQ9o2bmuTP4G/TUykl+PlxOAL+iULmcM627d9555x2Cg4M5HnmUpjIOgOnHTpGeQ2u/Y2P9vC0ZRBX5G5d1+teuXWPLli0YjcZcZ/EsSGSN66t8KXnDyRTd+RWJisMDD7z8PTl8UZ/03qhG/u3EvRUvLy8WL16Mt7c3+7//GP+LGqc80vn60Nls63dsrD/h/LENdW8VAFzW6f/1119kZGTQvHlzSpYs6Wxz8pzAwEACAgKIiYlh3759zjanUGJp6Zc+rztBn+o+1nh+QQjtZKVBgwbMmDGDvak7afOjHtqZdjCSq+a8QlkJqZVOGT84dQEOn3awoYr7xmWdviuM2slK1qGbahRP3nDS7PQrmRNU+tbwtY7caVSzYLTys/Lcc8/x8MMPs3HLDBoc1Uh0MzF11/Hb6rkZ4WFz5u4/tjnYSMV945JOX9M0a2zbVZw+/HeuKq5vfy6lpnElPRNPUybVruqT/Hxq+BSoTtxbMRgMfPPNN1wpdZmGS/ZgMGl8G3We4+YJaFmxZA5Vcf38j0s6/aNHj3L27FnKlStnXWHKFWjfvj1ubm5s3bqVhIQEZ5tTqLBMyvJLvkYlNz13jXe1ItZwhyPXw7Un5cqV49tvv2XF8c9ovVUj0wivbj18W72OTcDDHbYfgkvxyvHnZ1zS6VuGLXbo0AGj0XUuQYkSJWjevDmZmZls2rTJ2eYUKo6aF04pcuUSFdwqAHDJ15e0dKhWHor7FrzwjoWuXbvS6/melFy+Fu8UjfXX4vkr5tJNdYr7GmgbAiYT/PmPc+xU3Buu4/GysGHDBoBCmWDtbjz44IPAf9dAYR8sLX1jzHkqGHWnfzRdT0SW3ydl3Qvvv/8+h/228PCfekfuK9uP3DZhq3uYCvEUBFzO6ZtMJv7++28A2rVr51xjnIDlnNevX+9kSwoXFqfv8W8sHgZPDKUMHDivL0ZfEOP5t1KkSBG+WvoVyat/pFycxsn0VOZF3pyBs5u5M1fNzs3f2Oz0hRDvCSE2CyEWCCE8smzvLoT4RwixRQjxcZbt14QQf5tfQbbq3y/79u3j6tWrVK1alerVb18V6F6Ij4/n88/zfqXHX375hcOHb4+f2kKLFi3w8vJi3759XL582a7HdlU0TbOui1vseDwA3lW9sqRfKLihnawEBwfT5OVguv+o/8C9s+cE8Wn/DeGsXtFAw+qQeB0273eWlYq7YZPTF0IEAwFSynDgKNAnS/E+IExK2QooJ4QQ5u3HpJRtza8DtujnBktYwxLmyA336/Q1TctVLvu8cPre3t60bKknTNm4caNdj+2qxKdlcCMzk2JuRvyi9BZusVrFOFCAR+7kxJiXRmO6sp16xzUSDCbeO3BzMjbLegGWRWMU+Q9bW/otAcs6fKuAMEuBlPKslDLD/DENsHi9mkKITUKIL4QQ3jbq3zeWsIYtoZ2JEycSGRlJSEgIL774Iu3bt6dx48YEBQXx66+/AnDu3Dnq1q3L0KFDCQwM5Ny5c7z99tvUrVuXVq1aMWDAAD744AMAIiMj6dKlC6GhoYSHh3P06FG2bdvGb7/9xoQJEwgJCSEyMtL2kzejQjz2paSXB2d7P8gPDStTOr0UAN41/DgTA16e+gpThQWj0ci4X1/goR/iMJg05pw4x2nzTGSAbtahm2p2bn7F1oXRSwLmqSgkAKVurSCEaAKUk1JaVvCoJaW8LIR4HRgJzMhmn+HAcIBRo0blOk1Ceno60dH/rfOZkZFhbd3Wq1fvprL74YUXXiAiIoIVK1aQkZFBSkoKxYoV48qVK3Tv3p3Q0FAyMjI4ceIE06dPZ+rUqURERPD999+zcuVKMjIy6NKlC7Vq1SI6OprHH3+cqVOnUqNGDfbs2cNTTz3FsmXL6NChA+3bt6dbt24A92zvred9K0FBelRt7dq1ub4GtujnJc7U1hKuUsFYEYDITH3h2NoV04mNvXSn3eyCQ8/bHWq0M9B6O2wMM/D63hPML6IvmVi5BJQqVo5/z7uxcddFagdk3PlYdsBV77e7aQcEBGS73VanHw9YlpvyA27K2yuEqAR8BPSybJNSWgLJy4GJ2R1USjkbmG3+eMfmQm7zmTRt2vSO5XdqpaSnp+Pu7k5AQADp6em8+OKLbNq0CaPRSGxsLO7u7ri7u1O1alV69OgBwA8//ECfPn2oWVMfytGrVy/8/Pzw8/Nj9+7djB492nr8GzduEBAQgI+PD6VLl87xn5cT0dHRd9ynbNmy+Pj4cPz4cdzc3Chfvvx9Hd9W/bzEmdqHDx+monmMflwx/ZqG1vNwiD2OPu+BMwcQH7qUf0JLsdfbyKFMI52q6KOWurU0MX817IosR9umed+f4ar3W261bQ3vbAM6mN93BrZaCoQQxYDvgWellBfN23yFEG7mKuFAzqszFBAWLVpEXFwcu3fvJiIiAn9/f1JT9dwrvr6+d93fZDJRokQJIiIirK8jR47kqc2enp6Eh4cDWEcyKWzn6qWrlDfqzj7iuj5cMz8vnGILBoOBnh+0pvtqvXE0evVWa0PJGuJRcf18iU1OX0oZAcQKITYDDYEfhRBfmYtfAKoDn5pH6rQBagO7hBCbgK7Ax7cf9f7QNC3HV1RU1E2fO3XSF/X8/vvv77jf3WKRxYoV49q1awAkJCRQrlw5PDw82LBhA2fOnMl2n7CwMH7//XdSU1NJSkrijz/+AKB48eJUr16dH374wXo+loRoWXXsjYrr25+EU4l4GDy47pVExFm9bVMYxujnRMCDFeme5E7xRI04Pz/e/WM1AJ2bgrsbbD0IlxOU489v2BreQUo54ZZNz5q3vw28nc0ujW3VzA1paWls2bIFgLZt29p0rNKlSxMWFkZgYCBNmjTh6NGjBAUFIYSgXr162e7TpEkTevToQaNGjfD39ycoKAg/P311pUWLFvHcc8/xf//3f6Snp9O/f3+Cg4Pp378/zzzzDLNmzWL58uXW0JA9sDh9NUnLfqScStH/Fk8plCN3sqPtjOZ0e3Uzi/u48emJizx98SL+5crRJkTjr92w6h8Y1MnZViqyYrPTLyjs3LmT5ORkGjZsiL+/v83HW7x48R3LPTw8OHjw4E3bxo8fz5QpU0hOTqZ169aEhoYCUL169WwzX4aFhdl9yKaFxo0bU7x4cU6ePMm5c+eoXLlynui4EpnReudtckk3Eq9D2RLgf9vQhsJF0Vq+9K9amj+vXuVqpQr0enMaWz+dQfeWBv7arfH7No1BnQpniKug4jIzcu0xVNNWhg8fTkhICI0bN+bRRx+lcWOnPPQA4O7uTuvWrQHV2rcXhjj965RYohigh3YKysIptlBluD+9t+jnfrpWU7746ivr2rmrdkJ6hgrx5CdcxulbHJsznf7ixYuJiIjg6NGjvPLKK06zw4LKw2NfvK/q004u+pYBCn9ox4Kbjxujetan7CWN1IDSTF6+mrTEo9SvCglJsEXNzs1XuITTT0lJYdu2bRgMBtq0aeNsc/INWTtz1UQa2yl6vSgApzz0xXALS/qFe6FK74oMPu4DgE/PoQwYPJiuzfRwl0rAlr9wCae/fft20tLSCA4OpnTp0s42J9/QqFEjSpUqxdmzZzl16pSzzSnQmDJM+KWVAOBQmmu19EEPY418phEVYjSulytKXKmGnD+oT7VRq2nlL1zC6dsj305hxGg0WkcyqaGbtpFyNgU33LhoiuPkZV+MRmhQzdlWOZZSDYszIkVvVLk/2o8l34ynWJF0TkTBsbOqtZ9fcAmnnx86cfMrauimfbgeqWfZPE8CmSYDtQLAx9t1wjsWhj8bRJUYuFbKk4adRpFx8Xf8S5o4E+NsyxQWCr3TT0pKYufOnRiNRuss1PxIREQEK1euvO96v/32G9OmTcu1btbOXBXXzz1Xj8YDEGNOLl6YJ2XdCa8Snowro2eYS+rZAY69QJj7ADo2cbJhCiuF3ulv2bKFjIwMhBDWyVD5kdw6/R49ejBxYrYpjO6J+vXr4+/vz4ULFzh27Fiuj+PqXDlyFYC4oiWBwpt+4V4Y3K8uteKMJBY30rTbOH76aTnffvuts81SmCn0Tj8vhmqePn2a+vXr88wzz9CwYUM6depESoo+GzMiIoLmzZvToUMHevXqxdWrV2/b/4cffiAwMJDg4GBat25NWloar7/+OkuXLiUkJISlS5eyc+dOWrRowQMPPEDLli05duxYtvXmzZvHqFGjAIiNjaVXr1507NiR4OBgtm27vQdt1apVNG7cmODgYNq3b4/BYLDm12/fvj3Nmzdn//79mEwmqlWrRnx8vHXf2rVrExsbe5v9Ckgyh3dii+oJsFy1pQ9gdDPyWqNaAPzbJZCKvrUYM2YMJ08W+FRbhYO75aDJB69cExUVpTVp0kQDtNWrV9tyqJs4deqU5ubmpu3du1fTNE3r27evtmDBAk3TNC0oKEj7+++/taioKG3y5Mna2LFjb9s/MDBQi4qK0jRN065evappmqZ9++232siRI611EhIStPT0dE3TNG3t2rVa7969s62X9fNjjz2mzZw5U4uKitIyMjK0+Pj4m3QvXryoVapUSfv33381TdO0y5cva5qmaW3bttUArU+fPtpff/2lBQcHa5qmaWPGjNG++eYbTdM0bceOHVr79u1ztD8rljJn4CztFfX+1FaUWqXVbHJGIzxTOxllcqh+frvmJpNJaz1ng1Zy6Rqt3zM/aIDWtGlTLS0tzSH6jiKfa2frUwt8S9/Q2pTjq9KACuzy3gHhmXT+vw53rHvr625Ur16dkJAQAEJDQzl9+jQJCQnEx8db5wI8/vjjbNq06bZ9w8LCGDZsGHPmzCHTnHf9VhISEujbty+BgYG8+OKLHDp06K42rV+/nueeew4ANze328JZO3bsoHXr1tZlIkuV0nMExMbGAnrGzbZt23L58mUSExPp168fS5cuBfQkdf369btn+10JU7oJ7RKYNBNnPSvgWwSqV3C2Vc7FYDAwpU0DADaHFadLrb7s3LmTt956y8mWKQq803cWXl5e1vdubm5kZNz7YhFffvkl//d//8e5c+cIDQ3Ndq3ayZMn065dOw4ePGjNzplXeHl5Ub58eS5dunRTvqAWLVpw8uRJ4uLi+OWXX+jdu/c92+9KmNJMXG4Vx+/aHtKNbgRWB6PRdWP6FtrVLkezDB9SihhoOXUERqORd999l82bNzvbNJemwDt9bZMxx9fTdZ+FzW5MbjvljvWye+UGPz8/SpYsab2pFyxYkO0M4MjISJo1a8Zbb71F2bJlOXfu3G1plBMSEqwLJMybN8+6/U7pltu3b88XX3wBQGZmJgkJCTeVN2/enE2bNlknYl25oq95Ex4ebk1CN3v2bMqUKUPx4sUxGAz06tWLcePGUb9+fevEtuzsd2Xcfd05Wv8Is330cYmuHM+/lRldG7GxY3Mm93mQV155BZPJxODBg2/qK1I4lgLv9O+EpSPTkePzv/vuOyZMmECHDh2IiIjg9ddfv63OhAkTCAoKIjAwkJYtWxIcHEy7du04fPiwtYP2pZde4pVXXuGBBx646Sni1npZ+fjjj9mwYQPt27cnNDT0tgydZcuWZfbs2fTu3Zvg4GBruGbKlCkYjfqtsHjxYr777jvrPv369WPhwoXWujnZ7+pcunQJfBsBrj1y51YalChGUEk9Ad0bb7xBkyZNOHv2LCNHjnSyZS5MTsH+fPTKFZcuXdIAzcvLS0tJScntYXJNPu/guY3Tp09rgObn56dlZGQ4XN9eOEu7V69eGsHbNMIztb/3OrYTV9MKzjU/fvy45uvrqwHawoULHa5vb/K5drY+1eZ8+kKI94CWwGngSSllunm7GzAHfbWs3VLKF8zbxwKPAZeBwVLKRFttyA7LMoAtW7bE29s7LyQKFVWrVqV69eqcOnWKiIgIa65/xb0Rd+ky+AYCrpVz536pXbs2s2bN4qmnnuL555+nZcuW1oEFCsdgU3hHCBEMBEgpw4GjQJ8sxd2A8+YyXyFECyFEGaAH0ApYCuTZM15+SKVc0LDMzlV5eO6f85e9wc2XciXSKVVchXfuxBNPPMGjjz5KYmIiQ4YMua9BEArbsTWm3xJYY36/Cgi7S1kTYKOUUsumvl2xOC6VZO3eUXl4ck9cir4gemD1uw/3dXUMBgOzZ88mICCArVu3MnXqVGeb5FLY6vRLApbwTAJQ6i5ld6pvN2JiYjhy5AhFihShSROV9ONesTj9zZs3k56e7mRrCg7JyckkmaoB8EAdD+caU0AoVaoU8+fPx2Aw8Oabb7Jjxw5nm+Qy2BrTjweKm9/7AVfuUhYP1MqhvhUhxHBgOMCoUaPo2LHjfRl18eJFRo0aRXJyMnFxcfe1r71IT08nOjq6wGnXrFmTyMhI/vzzz1zH9QvqueeWzZs3o/kEAVC1TALR0Xk3pyInCuI1r1u3LsOHD+err76if//+rF69mqJFizpM3x7kZ23LkO9bsdXpbwPGAfOBzsDWW8o6AJvMZd8CJ831yaa+FSnlbGC2+eN9p34MCAjgk08+ITo6OscTz2sKqnbHjh2JjIzk4MGD9OjRw+H6tuIM7QMHDoDvUADaiFIEBDg+pl9Qr/nHH3/MP//8Q0REBNOmTctVYraCeu7O0rYpvCOljABihRCbgYbAj0KIr8zFfwBVzGWpUsrtUso4YIUQYiswEPjcFn1ncfr0aQIDA7Mte/rpp28bH+9oEhIS+Pzzu1/a+Pj4m+qdP3+evXv3Aqoz935Yt2E7eNfEzWCiXhVnW1Ow8PLyYvHixXh7ezNv3jyWLVvmbJMKPzmN5cxHr1yTV2NoT506pTVs2DBPtS3J1nLD9u3b72qfpmV/HhcvXtQAzdvbW0tNTc2Vfj4fu2xXEhMTNaOHr2Yo10979dMLDtXOSkG/5p9//rkGaCVKlNDOnj3rcP3cks+1C2fCNWeRkZHBoEGDqF+/Pn369CE5ORmAtm3bIqUEoGjRokyaNIng4GCaN29uTWz2+++/06xZMx544AE6dOhg3T5lyhSGDBlCWFgYQ4YMoXXr1kRERFg1W7Vqxb59+26y49ChQzRt2pSQkBAaNWrEiRMnmDp1KpGRkYSEhDBhwgSSkpJo3749jRs3JigoiF9//RWAiRMn3lTv9OnTtGvXjqCgIFJTUxk6dCiBgYE0atSITz755LZrcPLkSTp06EBwcDCNGzcmMjISTdN4++23CQwMJCgoyDpruH///qxYscK677Bhw1i+fHm29hcktmzZgin9Ok2rn+b5nir5XG4ZMWIE3bp1Iz4+niFDhqhEfnlJTr8G+eiVa/KypQ9oW7Zs0TRN05544glt+vTpmqZpWps2bbRdu3ZpUVFRGqD99ttvmqZp2oQJE7S3335b0zRNu3LlimYy6bM258yZo40bN07TNE174403tMaNG2vJycmapmnavHnzrKmZjx07poWGht5my6hRo6wzG2/cuKElJyff1tJPT0/XEhISNE3TtLi4OK1mzZqayWS6raVv+TxmzBgN0OrXr2994rCkYc5K06ZNtZ9++knTNE1LSUnRrl+/ri1fvlwLDw/XMjIytJiYGK1y5cra+fPntZ9++kkbOnSo1c5KlSppycnJ2dpvC45ueY0fP14DtIkTJ+b3Vl++146NjdXKlSunAdq0adMcrp8b8rl23szIdTYrS6++Y/k+Dt6xPCe6Xu58x/LKlSsTFqZPMxg8eDCzZs1i/PjxN9Xx9PSkW7dugJ5+ee3atQBERUXRr18/Lly4QFpa2k0zEnv06EGRIkUA6Nu3L2+//TbTp0/nm2++YdiwYbfZ0aJFC9555x2ioqLo3bs3tWvXvq2Opmm8+uqrbNq0CaPRSHR0tPXpIjvatWvHrFmzcHd3x91dv0UsaZgtXLt2jejoaHr16gVgnfW8ZcsWHnnkEdzc3PD396dNmzbs2rWLhx56iLFjx3Ljxg1WrVpF69atKVKkyD3Zn59RkwDtR7ly5Zg3bx5du3bltddeo0OHDmpmeB6gwju5xGAw3PEzgIeHh3V71vTLo0ePZtSoURw4cICvvvrqprTJvr6+1vc+Pj507NiRX3/9lWXLljFo0KDbNAYOHMhvv/1GkSJF6Nq1a7YdsIsWLSIuLo7du3cTERGBv7//HVM1WzKDHj582Bq2shVvb2/atm3L6tWrWbp0qTWB273Yn1+5evUqe/bswcPDw9oAUNjGQw89xOjRo8nIyGDgwIFcv37d2SYVOgp8S/9OLfK8HE519uxZtm/fTosWLVi8eDGtWrW6532zpk3OmtEyO55++mm6d+9OeHg4JUuWvK3833//pUaNGowZM4azZ8+yf/9+OnTocFua5nLlyuHh4cGGDRs4c+YMkHOa5pIlS1K5cmXOnTvH5s2b6dy5M1euXLmptV+sWDEqVarEL7/8Qs+ePblx4waZmZmEh4cza9YsXnjhBa5cucKmTZuYPn06oGfsnDt3LlJKa7ro7OwvKLOoN23ahKZpNGvWDF9fX5Uu2E689957rF+/nkOHDjFu3Di++uqru++kuGdUSz+X1K1bl88++4z69etz9epV64pV98KUKVPo27cvoaGhlClT5o51Q0NDKV68OE888US25cuWLSMwMJCQkBAOHjzI0KFDKVmyJGFhYQQGBjJhwgQGDRqElJKgoCDmz59PvXr1AChduvRN9bLSt29fAAYNGkRwcDCLFy++TXvBggXMmjWLRo0a0bJlS2JiYujVqxf16tUjODiYBx98kPfff5/y5fUUBZ06dWLjxo106NABT0/PHO0vKFhCOwXlR6qgUKRIERYvXoynpyezZ8/ml19+cbZJhYucgv356JVr8nknyz0RHR2t1a5dW8vMzHSo9ooVKzRAa968+X3vWxiu+70QFBSkAdqGDRscrn0rhVH7ww8/1ACtdOnSWnR0tMP174V8rq2GbBY05s+fT7NmzXjnnXesi5w4ivDwcNzc3Ni1a1eOK3W5MnFxcRw4cABvb2+aN2/ubHMKJWPHjqVjx45cvnyZJ554ApNJJbOzB8rp52OGDh3KuXPnrKEWR1KsWDGaNGlCZmamWtM0G9R6DXmP0Whk3rx5lC5dmjVr1jBr1ixnm1QoUE5fkSMq1XLOqKGajqFixYrMnTsXgJdffpn9+/c72aKCj3L6ihyxOLSCNIzSUaj1GhxHz549GT58OGlpaQwcOJCUlBRnm1SgUU5fkSNhYWF4eHiwd+9erl696mxz8g3nz5/n2LFj+Pr6qvUaHMSHH35InTp1OHToEC+//LKzzSnQKKevyBEfHx+aN2+Opmls3LjR2ebkGyzx/PDwcDw81KIpjsDX15fFixfj7u7OJ598wsqVK51tUoFFOX0H8Pfff7Nt2zaHaHXt2pWEhIT73m/evHmMGjXqtu2W8EXWuP7p06ezHbd/K7fWk1IyZsyY+7Ytv2EJ7ah4vmMJDQ3l//7v/wB9nd2LFy862aKCiXL6DsARTl/TNEwmEytXrsTPz89ux82uMze3Tl8IUShGYKhJWc5j/PjxtG3blosXL/Lkk0+iafe9xpLLo5x+Lpk/fz6NGjUiODiYIUOGADenTO7fvz+xsbGcPn2aL7/8kpkzZxISEsLmzZuJi4vj0UcfpUmTJjRp0oStW/UFxOLi4ujYsSMNGzbk6aefpmrVqly6dAnQY5qBgYEEBgby0UcfAbpTrVu3rjUF8rlz56hWrRpXrly5JxuzpnXOibS0NAwGAwcOHCAoKIhr164xceJENm/eTEhICDNnzuT06dOEh4fTuHFjGjdubE0tfWu9v//+25qALikpiSeeeIKgoCAaNWrEjz/+eJv2rl27aNmyJcHBwTRt2pRr166Rmppq3e+BBx6wOuDmzZtz6NAh676WFNcbN24kJCSEkJAQHnjgAZvnHJw5c4Z///0XPz8/HnjgAZuOpbh/3NzcmD9/PiVKlGDFihV88cUXzjap4JHTrK189Mo1eTVb7uDBg1rt2rW1uLg4TdP+SzucNWXy+++/f1PKZEvqZU3TtAEDBmibN2/WNE3Tzpw5o9WrV0/TNE0bOXKk9u6772qapml//vmnBmhxcXGalFILDAzUkpKStGvXrmkNGjTQ9uzZo506dUozGAza9u3brceuWrWqtn///nuyMWta52+//VYbOXLkbefarVs3LTQ0VAO0+fPna+np6dqGDRu0hx9+2Frn+vXrWkpKiqZpmnb8+HGtUaNGmqZpt9XL+vmll16ypo222JWVGzduaNWrV9d27typaZqmJSQkaOnp6doHH3ygPfHEE5qmadqRI0e0ypUraykpKdqHH36ovf7661pUVJR2/vx5rU6dOlb7LSmwr127ZtPiNJbrBGjdu3e/rSyfz84sVNrLli2zLvazfv16h+tbyOfX3f6plYUQ7wEtgdPAk1LK9Cxl3YHXgHRgt5RyrHn7NWC3udpoKeUBW2wotWztXWrkbunCK4/lvBj7+vXr6du3rzVvjiURWdaUycnJyTmmCV63bt1NSyomJiaSlJTEli1b+PnnnwHo0qWLNcHali1b6NWrlzUDZ+/evdm8eTM9evSgatWq2c4IvRcbb03rnB1hYWF89tlnAKxZs8b6xJCV9PR0Ro0aRUREBG5ubhw/fvyOx7Rcg++//976+dZkcseOHaNChQrW0THFixe3XovRo0cDUK9ePapWrcrx48d57LHH6NSpE8OHD2fZsmX06dPHav+4ceMYNGgQvXv3plKlSne17U6ooZr5g759+zJs2DDmzZvH6NGj2b17N15eXs42q0CQ6/COECIYCJBShgNHgT63VNkHhEkpWwHlhBDCvP2YlLKt+WWTw89vZE2ZPG3atBzTF5tMJnbs2EFERAQRERFER0dTtGjRXGlmTcV8vzbemtY5OyZOnMjUqVMB+P777zl69OhtdWbOnIm/vz/79u1DSkl6evptdfKagIAASpcuzeHDh29K3Txx4kTmzp1LSkoKYWFh2dp/r2iapiZl5SNmzZpFjRo1OHToEK+99pqzzSkw2NLSbwmsMb9fBTwBLLEUSinPZqmbBlgSZ9QUQmwCDgEvSinv7HXuwp1a5HmVWvnBBx+kV69ejBs3jtKlS1vTDmdNmbx8+XJr/WLFipGYmGj93KlTJz755BNrZsuIiAhCQkIICwtj2bJlvPzyy6xZs8Y6Nj48PJxhw4YxceJENE3j559/ZsGCBTbbeLe0zgCRkZH069ePESNGcP36dbZt20ZwcPBtqZsrVaqE0Wjku+++sy51l1PqZoCOHTvy2WefWfsnrl69elNrv27duly4cIFdu3bRpEkTrl27RpEiRQgPD2fRokU8+OCDHD9+nLNnz1K3bl1AT938xRdfkJCQQKNGjaz2BwUFERQUxK5duzh69Kg1y+j9EhkZSVRUFKVLlyYoKChXx1DYj2LFirFo0SJatWrFBx98QOfOnenQoYOzzcr32OL0SwIXzO8TgFLZVRJCNAHKSSn3mDfVklJeFkK8DowEZmSzz3BgOMCoUaPo2DFnx34n0tPTiY6OztW+d6JEiRI8//zzhIWFYTQaCQwMZObMmYwePZrevXvj5+dH8+bNOXv2LNHR0TRt2pThw4ezfPly3n77bV555RUmTZrEN998Q2ZmJs2aNWPatGkMHz6ckSNH8u2339K4cWPKlStHYmIi/v7+9OrVi8aNGwMwYMAAypUrx7lz58jIyLjpHDMzM0lPT8ff3/+uNrZs2ZIbN24QHR3N1atXSUpKuu16/d///R/bt2+3fo6Pj6d06dJkZGTQoEEDHnvsMXr37s3w4cP5+uuvadu2LT4+PkRHR99Wr2HDhqSmphIdHc2TTz7JpEmTqFevHkajkRdffJGuXbvepP3JJ5/w7LPPkpqaire3N99//z2PPPII//zzD/Xq1cPd3Z3p06dbO7vDwsIYO3YsY8eOtZ6HxX6DwUDdunVp1KhRru8JS2dzs2bNuHDhwm3leXW/3Quuql25cmXGjBnDzJkzGTx4MOvWrct23Ym8Ij9f9xwbvDkF+y2v0NDQ8qGhoX9n83o1NDR0qLlOaGho6KfZ7FspNDR0a2hoaLlsyhqEhobOv5t+Hnd05Bm50U5NTbV2NG7btk0LDg52mPbdeP/99zVAe+qpp5yif6/kpXb//v01QPv0008drn03XFVb0zTt9OnTWlhYmAZovXr1sg5UcAT5/LrnriNXShkDtL11uxAiBBgHzAc6A1tvKS8GfA88K6W8aN7mC6RKKTOBcODk3fRdibNnz/LYY49hMpnw9PRkzpw5zjbJiqvn4dGyxPNVJ27+wt3dnQULFhAcHMzPP//MN998w1NPPeVss/Itue7IlVJGALFCiM1AQ+BHACGEZW2zF4DqwKdCiL+FEG2A2sAuc0y/K/Bx7k0vfNSuXZu9e/eyb98+ayw7v/DAAw/g5+fHqVOnOH36tLPNcThHjhwhNjaW8uXL57pPQJF3VK9enc8//xyAMWPG3NMIMlfFpiGbUsoJ2Wx71vz3beDtbHZrbIumwjm4ubnRpk0bfvvtNzZs2JDj8o2FlaypFyyL3SvyF4MGDWLlypUsWbKEQYMGsW3bNpUbKRvUjFzFPePK+fXVUM38j8Fg4PPPP6dKlSpIKZkyZYqzTcqXKKevuGfat28PwNq1a11q6bqMjAw1KauAUKJECRYuXIjRaGTq1Kls2rTJ2SblO5TTV9wzgYGBVKxYkZiYGJdaweiff/4hPj6e2rVrU7NmTWebo7gL4eHhvPLKK2iaxuDBg9VaELegnL7injEYDHTp0gWAP//808nWOA7LuVrOXZH/eeONN2jSpAnnzp3jueeeU9k4s6CcvuK+eOihhwBYtWqVky1xHJZztZy7Iv/j4eHBokWL8PX1ZenSpSxcuNDZJuUblNNX3BcdOnTAzc2NrVu35mqxloJGbGysNZlXmzZtnG2O4j6oXbu2df2GkSNHcurUKSdblD9QTl9xX5QoUYIWLVqQmZnJX3/95Wxz8pw1a/T0Upb0EoqCxRNPPMGjjz7KtWvXGDx4MBkZGc42yekop6+4byxhDleI66t4fsHGYDAwe/ZsAgIC2LZtG++++66zTXI6yukr7huLA1y1alWh7iDLzMy0tvRVPL/gUqpUKebPn4/BYOCtt966KYGgK6KcvuK+CQkJwd/fn6ioqJuWKCxsSCm5fPky1apVo06dOs42R2EDDz74IOPHjyczM5NBgwbdlOrc1VBOX3HfGI1GOnfuDBTuEE/WUTsq9ULB5+233yYkJIRTp04xZswYZ5vjNJTTV+QKVxi6qeL5hQsvLy8WL16Mt7c33333HUuXLnW2SU5BOX1FrujYsSNGo5HNmzfnuDpWQeby5cvs3LkTT09PlXqhEFG/fn0+/PBDAEaMGMHZs2fvskfhQzl9Ra4oXbo0TZs2JT09vVAmYFuzZg2aphEeHp7r9YsV+ZMRI0bQrVs34uPjGTp0qHV5T1dBOX1FrinMKRksYSsV2il8GAwGvv76a/z9/dm4cSMffPCBs01yKMrpK3JN1rh+YRq6aTKZVOqFQk65cuWYN28eAK+99hq7d+92rkEORDl9Ra4RQlCmTBlOnz7NsWPHnG2O3YiIiODixYtUqlSJBg0aONscRR7RpUsXxowZQ0ZGBgMHDuT69evONskh2LRylhDiPaAlcBp4UkqZnqWsLbAAiAQypZTtzdvHAo8Bl4HBUkrXHTBbwDEajXTq1InFixezatWqQrOMoCVcpYZqFn7ee+891q9fz8GDBxk3bhxfffXV3Xcq4OS6pS+ECAYCpJThwFGgTzbVlkop22Zx+GWAHkArYCkwMrf6ivxBYUzJoOL5roO3tzeLFy/Gy8uL2bNn88svvzjbpDzHlvBOS2CN+f0qICybOo8KITabW/cATYCNUkrtDvsoChCdOnUCYOPGjSQnJzvZGtuJj49n+/btuLu7W1cKUxRugoKCeO+99wB4+umnOX/+vJMtyltsCe+UBC6Y3ycApW4pl0Bd8/tfhRBbzPsk3mEfAIQQw4HhAKNGjaJjx465MjA9PZ3o6Ohc7WsrrqQdHBzMvn37WL58Oe3bty/Q5/7HH3+QmZlJs2bNSEpKIikpyWHatuCq2vbS7927Nz///DMbN25kwIAB1iUXHaGdW+6mHRAQkO32uzp9IUR54PtsitYAxc3v/YArWQullNZvixDidyAYiAFq5bRPln1nA7PNH3M9LCQ6OjrHE89rXEm7e/fu7Nu3DyklQ4cOLdDnvnPnTgB69ux538cpyOddULXtqb9kyRIaNWrEpk2bWL58OS+++KLDtHNDbrXv+lMmpYwxx+VvegErgQ7map2BrVn3E0IUz/KxFXAS2AW0zmkfRcGksMT1NU1T8XwXpkKFCnz99dcATJw4kX379jnZorwh1zF9KWUEECuE2Aw0BH4EEEJYur8fE0LsFEJsA6KllJuklHHACiHEVmAg8LlN1ivyBU2bNqVEiRKcPHmSkydPOtucXHPgwAHOnz9P+fLlCQ4OdrY5CifQo0cPRowYQVpaGgMHDiQlJcXZJtkdm4ZsSiknZLPtWfPfucDcbMpnAjNt0VXkL9zd3enUqRPLli1j1apV9OrVy9km5YqsrXw1VNN1mTFjBhs2bODw4cO89NJLfPLJJ842ya6oyVkKu1AYsm6qWbgKAB8fHxYvXoyHhweffvopK1eudLZJdkU5fYVdsOTXX79+PampqU625v65du0aW7ZswWg00qFDh7vvoCjUNG7cmHfeeQfQ19m9ePGiky2yH8rpK+xChQoVCAkJISUlxToCpiCxfv160tPTad68OaVKZTuSWOFi/O9//+PBBx/k4sWLPPnkk4Umv5Ry+gq7YRnxsnbtWidbcv/8/vvvgBq1o/gPo9HId999R8mSJVmxYgVffPGFs02yC8rpK+xG7969Ad2BZmRkONmae+fGjRv8+OOPwH/noFAAVKpUidmz9SlD//vf/zh8+LCTLbId5fQVdkMIQZ06dbh06RJ//fWXs825Z1auXEl8fDwhISE0bNjQ2eYo8hl9+vThiSeeIDU1lYEDB3Ljxg1nm2QTyukr7IbBYGDQoEEALFq0yMnW3DsWWy22KxS38vHHH1OzZk327dvHpEmTnG2OTSinr7ArAwcOBODnn38uEPnJ4+Pj+f333zEYDAwYMMDZ5ijyKcWKFWPRokW4ubkxY8YM1q1b52yTco1y+gq7UqtWLRo3bkxSUhK//fabs825Kz/++CNpaWm0a9fOqfljFPmfZs2aMWXKFACGDh3K5cuXnWtQLlFOX2F3LDNyC0KIR4V2FPfDK6+8QqtWrbhw4QLPPPNMgRzGqZy+wu50794dNzc3Vq9eTVxcnLPNyZGoqCj+/vtvvLy8ePTRR51tjqIA4ObmxoIFCyhevDg///wzixcvdrZJ941y+gq7U6ZMGTp37kxGRgbLli1ztjk5smTJEjRNo3v37vj5+TnbHEUBoVq1atYx+1OmTOH48eNOtuj+UE5fkScUhFE8KrSjyC0DBw5k0KBBpKSkMHDgQNLS0pxt0j2jnL4iT3jkkUfw9fVl+/bt/Pvvv8425zYOHjzIvn37KFmypEqwpsgVn332GZUqVWL37t3WDt6CgHL6ijzB19eXnj17AvmztW+xqW/fvnh5eTnZGkVBxM/Pj1mzZmE0Gpk2bRobN250tkn3hHL6ijxj8ODBgO5g89MoB5PJpEI7CrvQtGlTJk2ahKZpDBkyhKtXrzrbpLuinL4iz+jQoQPlypXj2LFj7Nmzx9nmWNmyZQvnzp2jSpUqtGrVytnmKAo4kydPplmzZpw7d44RI0bkqwZOdti0cpYQ4j2gJXAaeFJKmZ6lrBcw1vyxBjBDSvmxEOIEYFnC/R0pZcFLyai4J9zd3enXrx+ffPIJCxcuJDQ01NkmAf+FdgYOHIjRqNo9Ctvw8PBg4cKFhISEsGzZMh5++GGGDh3qbLNyJNd3vBAiGAiQUoYDR4E+WcullD9nWUQ9EvjFXJSQZYF15fALOZYQz/fff09mZqaTrdEzalqGkVpsUyhspVatWtZlFUeOHElkZKSTLcoZW5o5LYE15vergLDsKgkhygNeUsoz5k1FhRAbhRCLhRBqtYpCTpMmTahVqxYxMTGsX7/e2ebw559/Eh8fT3BwsMqoqbArw4YNo0+fPiQlJTFkyJB8m17clvBOSeCC+X0CkJMD7w38mOVzmJTyshBiKPAmMPrWHYQQw4HhAKNGjaJjx465MjA9PZ3o6Oi7V8wDXFU7O/3u3bszc+ZMZs+eTYMGDRyqfStz584FoFu3bna/Rq76P89v95sztd944w22bNnC9u3befnllxk3bpzDtG8lp1xShrt1Ophb6t9nU7QGiJJSzhdChAJPSClHZbP/enPZmVu2+wCrpJSt72gA5LpXJDo62mlJtFxVOzv9EydOUKdOHYoWLUpsbCw+Pj4O085KQkIC/v7+pKWlcfbsWSpVquQw7bzGVbWdrZ+d9oYNG2jfvj0Gg4HNmzfTsmVLh2nfgiG7jXcN70gpY7LE4NtmidOvBCwrSHcGtt66rxDCnyyhHSGEpxDCMig6HDh5N31Fwad27do0bdqUpKQk67KEzuDHH3/kxo0btG3b1u4OX6Gw0K5dOyZMmIDJZGLw4MEkJiY626SbyHVMX0oZAcQKITYDDTGHcIQQX2WpdmtopySwTQixCRgPvJFbfUXBwjIefuHChU6zwaKtxuYr8pq3336bxo0bc+rUKUaPvi2C7Vw0Tcvvr1wTFRVly+424araOenHxMRobm5umru7uxYXF+dQbct2g8GgeXl5aVevXnWotiNwVW1n699J+8iRI1qRIkU0QFuyZIlDtc1k61PVIGWFQ/D396djx45kZGTw5ZdfOlz/008/RdM0unXrRokSJRyur3A96tWrx8yZMwEYMWIEZ8+edbJFOsrpKxzGyy+/DMB7771HTEyMw3TPnj3LRx99BMCECRMcpqtQDB8+nB49epCQkMCQIUPyxVwV5fQVDqNt27b06NGDpKQkh2YlnDRpEqmpqfTv359mzZo5TFehMBgMzJ07l/Lly7Np0ybef/99Z5uknL7Csbz33nu4ubkxZ84cDh8+nOd6u3fvZuHChXh6evLuu+/muZ5CcStly5Zl3rx5ALz++uvs2rXLqfYop69wKPXq1ePZZ5/FZDLleahF0zTGjx8PwJgxY6hevXqe6ikUOdG5c2deeOEFMjIyGDRoEElJSU6zRTl9hcOZMmUKxYoVY+XKlaxbty7PdH7//Xf+/vtvSpUqxaRJk/JMR6G4F6ZOnUpQUBAnTpzgxRdfdJodyukrHE7ZsmV59dVXARg/fnyedG6lp6fz0ksvAfrUeDViR+FsvL29Wbx4MV5eXsydO5eff/7ZKXYop69wCmPHjqVKlSrs27ePBQsW2P34c+bM4dixY9SqVYsRI0bY/fgKRW4IDAxk+vTpADz99NNOyRmknL7CKRQpUsTasTpp0iSSk5PtduyEhATeeEOf7P3+++/j6elpt2MrFLYyatQounTpwpUrVxg2bBgmk8mh+srpK5zGgAEDCA0N5fz588yYMcNux502bRqXLl2iVatW1nV6FYr8gsFg4Ntvv6Vs2bKsW7fOOofEUSinr3AaRqPR6uztNWErOjraOgtyxowZGAzZJhpUKJxK+fLl+eabbwB45ZVXiIiIcJi2cvoKp9KmTRseeeQRrl+/bg3J2MK0adO4ceMGAwYMoGnTpnawUKHIG7p168bzzz9PWloaAwcOtGuI804op69wOpYJW3PnzuXgwYO5Po6Ukp9//llNxFIUGKZPn069evU4cuSIdbRZXqOcvsLp1K1blxEjRmAymRg/fnyuOrYs+4I+MqhatWp2tlKhsD8+Pj4sXrwYDw8PPvvsM/74448811ROX5EveOONNyhevDirV6+mbdu2HDp06J733bdvHy1btmTjxo2ULFnSOgdAoSgIPPDAA9Yn0yeffJLY2Ng81VNOX5EvKFu2LEuWLKFcuXJs3ryZkJAQXn311TvGOZOSkhg/fjyhoaH8888/VKxYkc8//1xNxFIUOMaNG8eDDz5IXFwcTzzxBNpdlrG1hVw7fSGEnxBipxAiSQgRmE25mxDiGyHEZiHER1m2jxVCbBVC/CaEKJ5bfUXho2vXrhw9epRnn32WjIwMpk6dSmBgIKtWrbqt7q+//kqDBg2YMWMGmqYxevRojhw5Qnh4uBMsVyhsw2g0Mn/+fEqWLMmff/7JZ599lndaNuybDDwMLM+hvBtwXkoZDvgKIVoIIcoAPYBWwFJgpA36ikJIyZIl+fLLL9m2bRtBQUGcOnWKhx56iH79+nH+/HnOnj1Lz5496dmzJ+fOnbO28mfNmkXx4qoNoSi4BAQEMGfOHEBf9+F+Qpz3gy1r5KZLKePuUKUlsMb8fhUQBjQBNkoptSzbFIrbaNGiBbt372b69On4+PiwbNky6tevT4MGDfj1118pVqwYH3/8Mf/88w9CCGebq1DYhUcffZSnnnqK1NRUBg4cyI0bN+yu4W73I/5HScCyDHwCUCqHbbchhBgODAd9ynLHjh1zZUB6erpTclu4sra99QcMGEB4eDiTJ09m7dq1ADz88MNMmTKFChUq3Dahy1Wvu6tqO1s/L7Rfeukl/vrrL/bv38/UqVN55plncqUdEBCQ7fa7On0hRHng+2yK+ksp7zSFMh6wPG/7AVfM22rdsu02pJSzgdnmj7nu0YiOjs7xxPMaV9XOC/2AgABWr17NX3/9hYeHB23atHGY9v2gtJ1DYTz3ZcuW8ccffzBp0iQ8PDzsqn1Xp2927G3v+8iwDegAbAI6A98CJ4Fx5vLOwNZcHFfhghgMBjp06OBsMxQKh9CkSROaNGmSJ8e2acimEGIl0AmYI4QYZt72lbn4D6CKEGIzkCql3G7uA1ghhNgKDAQ+t0VfoVAoFPeHTTF9KWXXbLY9a/6bAQzLpnwmMNMWXYVCoVDkDjU5S6FQKFwI5fQVCoXChVBOX6FQKFwI5fQVCoXChVBOX6FQKFwIQ15mc1MoFApF/kK19BUKhcKFUE5foVAoXAjl9BUKhcKFUE5foVAoXAjl9BUKhcKFUE5foVAoXIhC4fSFEF7OtkGhcARCCF9n26Ao2BTocfpCCHdgKhADfCmlvO5g7SHAESnlDkfpZtEeCRwGtjnyvM36E4FkKeUsIYRRSmlykK478CYQC/wjpfzHEbpZ9D2AEcAJYIuUMsmB2u7AO0Aa8LGU8pKDtZ15vzntu2bWHwNcllIucsL9/gpwFv1+i7THcQus0zdfkBcBAzBLSpnqQO1ywBzgNPoKYKuBVVLKqw7QLgF8CFxDX5z+GvCtlPJCXmub9X2AJUB5oLWU0v6LeGav6w98AZwBNgP9gBcceN7N0dd/2AakA0eAhVLKZAfpPwmUBT5y1DU365bAufeb075rZv3y6Pe7B9DBUX5GCFEZmAccQF98qhIwTUoZb+uxC2x4x5yvvwr6l+8DIcRLQojWeamZJYyUAkRLKccC76E7wD55rG1ZevIa4AlMBt4yf34+j7WLWd6bndw2IAl43VyeZ/fRLeedCrwlpfwJOAo4pLUrhDCgt7Cfl1KOAn4Bqkspk81ljiAU+Bf4RAjxlhBiQF6KOfN+M+s787vmaXlvXjlwOXAe/Skzr+93y3mnAyeklC8A36EvRBVvD40C09IXQlQEJgJzpZT7hRBu6DdfS2AZeou/K/C1lHK7nbXLoz9aXwLWAlvQHd4c9EevFujLP34tpTxtZ+1SwLvoC978CawCngYOSSnXma/LFOAzKeW+PNReKaX8yfyFeBpYASwGHr3LWsm2anugr8K2EngMCEdv8TVFX0d5h5RyQx7oVwQ+QA+l/COEKC6lTDSXFUN3/N3zoqWfRfsjKeVO87bBQH/gayAS/dp8KqVcZWft8sD/mT/+CfwM/A/YI6X8Ky/vN7N+GfSQ7WXgL2A3MBbd8Z0hb79rWbX/llKuMj9hPgt8gn4f9pVSnrenbjba64CdwFD0Fn5lIAR98andUsq9tmgViJa++RHvLaAB+s2OlDITvdVRHjhmbv1tAprZWbs+8Bl6PPM39Ef8NCADCEP/sTlp/uuZw2FsoQv6r/5U4CHzZw+gmhCisvkGPIJ+HfJK+12gmxCir5QyDagHJABfAb8KISbktTbQQ0q5ANgHxEspqwOH0GPsdkUIURQYA1QFXgOwOHwzTdHjy3nh8LNqT85SFIUe3rkmpdyPfu0fsrN2ZfT77Cj6Pf8iUBrdEdUXQgTk8f0Geis+Hvge3dlXB4oBTdD9VV5+1yzaS4AxQoimUspY9GsQD3wDLBNCPJ6H2t+jX/e66OHMTPP25oCGHta0iQLh9IErwBQpZQfghhBiqHn7ZmA9egsfoAZ6a8CenAbellLOkFJuBTagtzaWA43R43wx6L/GPnbWBvBCb3VEAovQv2zXgaJAb3Od6uZteaX9L3pLq44Qoi16eOc5oCdQG7iYh9qRwHygthCiDXAB/QcHoDh6S9AuZAnVJAMzpJRhQJq5lZ31sd8T+EsI0VMI8aoQomwealvu9UPorXxLaKMaYFOLLxvti8AHUsoPzK3JPUA79KdLX+BRc708ud/MYRN/4HspZQT6d6wJINHDW+3z6rt2i/Y+dMffSQgRiO4DHkf3M4GAXTtysznvpehPMzXR7/d/pJTX0O/3k7bq5UunnzWGDNb4vSWEMBUYIYTwMjuEJUBRIcQPgEBvkdtTO8X8j0AI4Y3+y3ve3NpaBbQUQvyJ3vq2KcxhfrS+lRvoTxQAO9CfMmKB34EyQohf0L8E5/JYe6dZtwR6eKUyeoukBfCPLXHOe9S+iN7SPQOkCCHWAAOANbnVvVVfSqmZ/5oAS0fhVOB58/2WZt7WEz201x9YLaWMy0Nty70eh96xFy2EmA/0AHblVjcH7RuYvz/mH4IrwElz6/5HoLS97jezRqUs7y2jYhKBp8ybf0LvtzsJ/Aq0EkKsxD7ftbtp/4r+w+KN3gBpD4xDjyTssqUv5x60fzFrl0L/wakthFgLtEX3ATaRr2L65pEC76Jf6A3mx/ms5QYppSaE+Bg4LaWcmcXZ1JdSHsorbXMdd+BzKeVwIYSvlPK6ufUXaks/gln7TfRWxK/ov/gXs5T/CnxijuG3A/pLKZ8192s0kFIecJB2e/Qwy9gs5bkewpaL8+4rpXze3NnVTEq5KTe696Gf9X47JaX8yOwoHwZSpJSLHaRtudf90J1DgJQyKq+0s9T7GngBPZySZn41tOV+y3LsTujft7eklL8JIdzNjTuEEJuAl6SUO4QQg4BWUsrn7PFdu0/twYAwd6Za9rVpyOZ9nncLKeUoIURJ9PvdLv03+aalb+4wmYbemnwPvXVV01xm+VW1/J0MvCmE2IN+E5psdPj3og1QEagihBgGbBBCNJBSptno8P2ACehxuyeAIPQYomVcOOjx1Z5CiB7o/RpJQggfKWWmjQ7/frXrARlCiCLmOgYbHH5uzjvFfN437ODw76Sf3f32thBiN7rD/dpGh3+/2m8JIST6D7xmo8O/F22EEKXRwzgD0YdJNjJ/z2x2+Gauoo/G6iuEKCqlzMjyf58FvGAOrZQGIoUQnrZ+13KhXQqIspTZcr/nQrs0cEYI4SGlvGrPDnunt/SFEAI4K6W8KIRoaHHeQoi30DvsPrylfgn0Dp76wCtSylMO1H4QPZy0BH1uwL82akdJKWOEEMXMMTuEEJ8B+4F5Mst4bCFEY/Q4X11gkpQyWmk7RN8bGI0eW37ZDvdbQdFugD56ZwUw3RZt8/FCgQvmUBFCiCZAHaAC4CGlnCqEcJP6AA2EEN3QQ4e1gbHShnkBNmqPkTaMTnPmeeeE05y+0EMTn6E779PAZinlXHOZAX2Y2G55y3A8869iGRtvgtxqVwcq29LKvEX7FPoMx9nmR9ehwDPonXbHge/s+U93VW1b9YUQJaUNk4EKorbQh2YGSClt7TfIqv8veqfkl0Kf7DYcmIT+lP0F+oiohKz7Wpyh0rYfzgzv+ABXpJRt0OOLXYUQ9cDasVQNOCaE8DE/kmIuS7eDQ8it9ilbwwq3aL8FdBFC1JN6J+FaKWUz9KnXJdE7M+2Jq2rbpG+L0y2o2lLK87Y6/Gz03wY6C30Y9L/oAyGuoIcyvjLbYMUOjs9Vte+IM52+EWgthKhqDpNsQp98YxlBUx99fPZf6BMTCrN2PwAppWW4aVf0+Lm70i4U+q6qnZ3+RvSRR2XRO4mXoz9pnECfl6G08xiHOP2srWXzZw/z48xPwBvmzd8AbcyxRDf0TqQWwFNSyo2FXLu1EKK++cnic/QY9gSZzYgKpZ2/9V1V+z71O6PPbp8OjJdSvgwMsKW/xlW1c0OexvSFPrztK/QxvR9IKU+LW4Y8CX386btSyg1CiFfQJ9vsRB+Vs9WFtCPQJ35Vk1IeVdoFS99VtXOpPwnYK6VcaS4zmMOqStsB5LXT743+63YASJDmse/mTo4Z6DlsUtCHhcUArYCB0g4pRAug9gBpw2ggV9d2tr6ratug31/aOCLIlbVtwe5OXwjxGHrHxAJpzk0ihOiCnrJglZRyjxCiGfrIgclSyitCiAroidM2SdtmNyptF9J2tr6rajtb31W17YXdnL7Qhzq+gJ4Y6Bh6uoLNUsr15pPuDSCl/OyW/WxelEBpu5a2s/VdVdvZ+q6qbW/s1pFrjk0VQ/8FfB09R8RQoc+gvICeHEoTQvQRQnQFa0zL5guitF1L29n6rqrtbH1X1bY39h69cxQoK/TVldahTwbpby7bC3RATyIVAP8lelLaSrsA6ruqtrP1XVXbbtjb6R9Cn15cR+oTDHajJzAD6IieLa+tlHKOnXWVtutpO1vfVbWdre+q2nbD3pMxDqOvbNRZCHEKPQ2vr7lspZTyNzvrKW3X1Xa2vqtqO1vfVbXthl1b+ubHmfno60l+jZ4fer+5LMOeWkrbtbWdre+q2s7Wd1Vte5Jn4/SFEA2B41JKh04xVtqup+1sfVfVdra+q2rbitNTKysUCoXCceSbRVQUCoVCkfcop69QKBQuhHL6CoVC4UIop69QKBQuhHL6CoVC4ULkxUo5CkWBwzy1/iXgtJRynhBiGPAt+uIiHzjVOIXCjqiWvkKh44O+ytEw8+eNwADgd2cZpFDkBaqlr1DoSPPfNkIIDTgDVAUmAMeEEKeBMsB3wGD0BTI+BWajf4+ekFKuEkJ4Au+i/2D4AmuB52U+yKOuUIBq6SsUFl41/z2C7rCzC+lY8qxsR19M/Av09U7LAdPMZa8A/0N/QvgIeAj4Mk8sVihygXL6CoXOGvPfi1LK74GkbOqYgBeBH82fF0gpZ6HnYqlu3tbN/PdZ9HCRL3oGRoUiX6DCOwqFzr3kI0mRUqYJISz5VhLMfzMBtyz1MtCdf6b5s2pcKfIN6mZUKHQS0VvytYQQg9Dj+bnhD/TG1ONAFaALeqtfocgXKKevUADmbInT0XOkL+S/Vvr9MtV8nHD0jt6H0EcCKRT5ApVlU6FQKFwI1dJXKBQKF0I5fYVCoXAhlNNXKBQKF0I5fYVCoXAhlNNXKBQKF0I5fYVCoXAhlNNXKBQKF0I5fYVCoXAh/h/9H/5kM82YKAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metric\n", + " no st bin st cat st\n", + "RMSE 0.274442 0.155583 0.131133\n" + ] + } + ], + "source": [ + "for series, ps_no_st, ps_st_bin, ps_st_cat in zip(\n", + " train_series, preds, preds_st_bin, preds_st_cat\n", + "):\n", + " series[-40:].plot(label=\"target\")\n", + " ps_no_st.quantile(0.5).plot(label=\"no static covs\")\n", + " ps_st_bin.quantile(0.5).plot(label=\"binary static covs\")\n", + " ps_st_cat.quantile(0.5).plot(label=\"categorical static covs\")\n", + " plt.show()\n", + " print(\"Metric\")\n", + " print(\n", + " pd.DataFrame(\n", + " {\n", + " name: [rmse(series, ps)]\n", + " for name, ps in zip(\n", + " [\"no st\", \"bin st\", \"cat st\"], [ps_no_st, ps_st_bin, ps_st_cat]\n", + " )\n", + " },\n", + " index=[\"RMSE\"],\n", + " )\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "b30c424f-42d1-428d-8424-48c2ca052c66", + "metadata": {}, + "source": [ + "These are great results! Both approaches using static covariates decreased the RMSE by more than halve for both series compared to the baseline!\n", + "\n", + "*Note that we only used one static covariate feature, but you can use as many as want including mixtures of data types (numeric and categorical).*" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "darts_39", + "language": "python", + "name": "darts_39" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 044bd2b28f591651d0ef957aa1b3da7a90dfc9f3 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Sun, 17 Jul 2022 15:08:49 +0200 Subject: [PATCH 11/16] TFTModel docstring update --- darts/models/forecasting/tft_model.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/darts/models/forecasting/tft_model.py b/darts/models/forecasting/tft_model.py index 9bab80f543..364ee12ce0 100644 --- a/darts/models/forecasting/tft_model.py +++ b/darts/models/forecasting/tft_model.py @@ -683,9 +683,12 @@ def __init__( prediction time). hidden_continuous_size : int Default for hidden size for processing continuous variables - categorical_embedding_sizes : int - Embedding size for categorical static covariates. Only effective if the target series contains - categorical (non-numeric) static covariates. + categorical_embedding_sizes : dict + A dictionary containing embedding sizes for categorical static covariates. The keys are the column names + of the categorical static covariates. The values are tuples of integers with + `(number of unique categories, embedding size)`. For example `{"some_column": (64, 8)}`. + Note that `TorchForecastingModels` can only handle numeric data. Consider transforming/encoding your data + with `darts.dataprocessing.transformers.static_covariates_transformer.StaticCovariatesTransformer`. add_relative_index : bool Whether to add positional values to future covariates. Defaults to ``False``. This allows to use the TFTModel without having to pass future_covariates to :fun:`fit()` and From 094d57f4bec3d0e3fe7ac2b2bfe570f91f28aec9 Mon Sep 17 00:00:00 2001 From: Dennis Bader Date: Mon, 18 Jul 2022 19:35:42 +0200 Subject: [PATCH 12/16] Apply suggestions from code review Co-authored-by: Julien Herzen --- .../transformers/static_covariates_transformer.py | 4 ++-- docs/userguide/covariates.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index 3216c2ba2c..23863fb326 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -56,11 +56,11 @@ def __init__( into integer valued arrays where each integer stands for a specific category. cols_num Optionally, a list of column names for which to apply the numeric transformer ``scaler_num``. - By default, the transformer will infer all numerical features and scale them with `scaler_num`. + By default, the transformer will infer all numerical features based on types, and scale them with `scaler_num`. If an empty list, no column will be scaled. cols_cat Optionally, a list of column names for which to apply the categorical transformer `scaler_cat`. - By default, the transformer will infer all categorical features and transform them with `scaler_cat`. + By default, the transformer will infer all categorical features based on types, and transform them with `scaler_cat`. If an empty list, no column will be transformed. name A specific name for the :class:`StaticCovariatesTransformer`. diff --git a/docs/userguide/covariates.md b/docs/userguide/covariates.md index 7b8dd5055e..7b1f73f507 100644 --- a/docs/userguide/covariates.md +++ b/docs/userguide/covariates.md @@ -99,7 +99,7 @@ Let's have a look at some examples of past, future, and static covariates: Temporal attributes are powerful because they are known in advance and can help models capture trends and / or seasonal patterns of the `target` series. -Static attributes are powerful when working with multiple `targets`. The time independent information can help models identify the nature/environment of the underlying series and improve forecasts across different `targets`. +Static attributes are powerful when working with multiple `targets` (either multiple `TimeSeries`, or multivariate series containing multiple dimensions each). The time independent information can help models identify the nature/environment of the underlying series and improve forecasts across different `targets`. In this guide we'll focus on past and future covariates. Here's a simple rule-of-thumb to know if your series are **past** or **future covariates**: From 8e10b86a332324c6ce644771ffd5aa6998232e8e Mon Sep 17 00:00:00 2001 From: dennisbader Date: Mon, 18 Jul 2022 21:38:40 +0200 Subject: [PATCH 13/16] applied suggestions from PR review --- .../static_covariates_transformer.py | 98 ++++++++----------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index 23863fb326..fa7cf9a6a3 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -8,7 +8,7 @@ import numpy as np import pandas as pd from scipy.sparse import csr_matrix -from sklearn.preprocessing import MinMaxScaler, OneHotEncoder, OrdinalEncoder +from sklearn.preprocessing import MinMaxScaler, OrdinalEncoder from darts.logging import get_logger, raise_log from darts.timeseries import TimeSeries @@ -30,14 +30,16 @@ def __init__( n_jobs: int = 1, verbose: bool = False, ): - """Generic wrapper class for scalers/encoders/transformers of static covariates. + """Generic wrapper class for scalers/encoders/transformers of static covariates. This transformer acts + only on static covariates of the series passed to ``fit()``, ``transform()``, ``fit_transform()``, and + ``inverse_transform()``. It can both scale numerical features, as well as encode categorical features. - The underlying ``scaler_num`` and ``scaler_cat`` have to implement the ``fit()``, ``transform()`` + The underlying ``scaler_num`` and ``scaler_cat`` have to implement the ``fit()``, ``transform()``, and ``inverse_transform()`` methods (typically from scikit-learn). By default, numerical and categorical columns/features are inferred and allocated to ``scaler_num`` and - ``scaler_cat``, respectively. Alternatively, specify which columns to scale/transform with ``cols_num`` and - ``cols_cat``. + ``scaler_cat``, respectively. Alternatively, specify which columns to scale/transform with ``cols_num`` + and ``cols_cat``. Both ``scaler_num`` and ``scaler_cat`` are fit globally on static covariate data from all series passed to :class:`StaticCovariatesTransformer.fit()` @@ -56,12 +58,12 @@ def __init__( into integer valued arrays where each integer stands for a specific category. cols_num Optionally, a list of column names for which to apply the numeric transformer ``scaler_num``. - By default, the transformer will infer all numerical features based on types, and scale them with `scaler_num`. - If an empty list, no column will be scaled. + By default, the transformer will infer all numerical features based on types, and scale them with + `scaler_num`. If an empty list, no column will be scaled. cols_cat Optionally, a list of column names for which to apply the categorical transformer `scaler_cat`. - By default, the transformer will infer all categorical features based on types, and transform them with `scaler_cat`. - If an empty list, no column will be transformed. + By default, the transformer will infer all categorical features based on types, and transform them with + `scaler_cat`. If an empty list, no column will be transformed. name A specific name for the :class:`StaticCovariatesTransformer`. n_jobs @@ -141,14 +143,12 @@ def fit( data = pd.concat([s.static_covariates for s in series], axis=0) self.cols = data.columns - # get all numeric and categorical columns - mask_num = data.columns.isin(data.select_dtypes(include=np.number).columns) - mask_cat = ~mask_num - # infer numeric and categorical columns if user didn't supply them at transformer construction if self.cols_num is None: + mask_num = data.columns.isin(data.select_dtypes(include=np.number).columns) self.cols_num = data.columns[mask_num] if self.cols_cat is None: + mask_cat = data.columns.isin(data.select_dtypes(exclude=np.number).columns) self.cols_cat = data.columns[mask_cat] self.mask_num = data.columns.isin(self.cols_num) @@ -159,8 +159,15 @@ def fit( self.scaler_num.fit(data[:, self.mask_num]) if sum(self.mask_cat): self.scaler_cat.fit(data[:, self.mask_cat]) - if isinstance(self.scaler_cat, OneHotEncoder): - # OneHotEncoder will generate more features, create a 1-many column map for that + # check how many features the transformer generates + n_cat_out = self.scaler_cat.transform( + np.expand_dims(data[0, self.mask_cat], 0) + ).shape[-1] + if n_cat_out == sum(self.mask_cat): + # transformer generates same number of features -> make a 1-1 column map + self.col_map_cat = OrderedDict({col: [col] for col in self.cols_cat}) + else: + # transformer generates more features (i.e. OneHotEncoder) -> create a 1-many column map self.col_map_cat = OrderedDict( { col: [f"{col}_{cat}" for cat in categories] @@ -169,9 +176,6 @@ def fit( ) } ) - else: - # other encoders will have a 1-1 column map - self.col_map_cat = OrderedDict({col: [col] for col in self.cols_cat}) else: self.col_map_cat = {} return self @@ -182,6 +186,7 @@ def transform( kwargs = {key: val for key, val in kwargs.items()} kwargs["component_mask"] = (self.mask_num, self.mask_cat) kwargs["col_map_cat"] = self.col_map_cat + kwargs["method"] = "transform" return super().transform(series, *args, **kwargs) def inverse_transform( @@ -214,6 +219,7 @@ def inverse_transform( kwargs["col_map_cat"] = OrderedDict( {name: [col] for col, names in self.col_map_cat.items() for name in names} ) + kwargs["method"] = "inverse_transform" return super().inverse_transform(series, *args, **kwargs) @staticmethod @@ -227,16 +233,16 @@ def ts_transform(series: TimeSeries, *args, **kwargs) -> TimeSeries: transformer_num, transformer_cat = args component_mask_num, component_mask_cat = kwargs.get("component_mask") col_map_cat = kwargs.get("col_map_cat") + method = kwargs.get("method") # "transform" or "inverse_transform" vals_num, vals_cat = StaticCovariatesTransformer._reshape_in( series, component_mask=(component_mask_num, component_mask_cat) ) - tr_out_num, tr_out_cat = None, None if sum(component_mask_num): - tr_out_num = transformer_num.transform(vals_num) + tr_out_num = getattr(transformer_num, method)(vals_num) if sum(component_mask_cat): - tr_out_cat = transformer_cat.transform(vals_cat) + tr_out_cat = getattr(transformer_cat, method)(vals_cat) # sparse one hot encoding to dense array if isinstance(tr_out_cat, csr_matrix): @@ -253,27 +259,8 @@ def ts_transform(series: TimeSeries, *args, **kwargs) -> TimeSeries: @staticmethod def ts_inverse_transform(series: TimeSeries, *args, **kwargs) -> TimeSeries: - transformer_num, transformer_cat = args - component_mask_num, component_mask_cat = kwargs.get("component_mask") - col_map_cat = kwargs.get("col_map_cat") - - vals_num, vals_cat = StaticCovariatesTransformer._reshape_in( - series, component_mask=(component_mask_num, component_mask_cat) - ) - tr_out_num, tr_out_cat = None, None - if sum(component_mask_num): - tr_out_num = transformer_num.inverse_transform(vals_num) - if sum(component_mask_cat): - tr_out_cat = transformer_cat.inverse_transform(vals_cat) - - transformed_df = StaticCovariatesTransformer._reshape_out( - series, - (tr_out_num, tr_out_cat), - component_mask=(component_mask_num, component_mask_cat), - col_map_cat=col_map_cat, - ) - - return series.with_static_covariates(transformed_df) + # inverse transform will be called with kwarg method="inverse_transform" + return StaticCovariatesTransformer.ts_transform(series, *args, **kwargs) def _transform_iterator( self, series: Sequence[TimeSeries] @@ -301,7 +288,6 @@ def _reshape_in( component_mask: Optional[Tuple[np.ndarray, np.ndarray]] = None, ) -> Tuple[np.array, np.array]: # we expect component mask to be (numeric component mask, categorical component mask) - assert component_mask is not None component_mask_num, component_mask_cat = component_mask # returns tuple of (numeric static covariates, categorical static covariates) @@ -316,24 +302,22 @@ def _reshape_out( col_map_cat: Optional[Dict[str, str]] = None, ) -> pd.DataFrame: # we expect component mask to be (numeric component mask, categorical component mask) - assert component_mask is not None component_mask_num, component_mask_cat = component_mask - - # `col_map_cat` contains information to which features the original categorical feature were mapped - # (i.e. 1-1 mapping for OrdinalEncoder, or 1-many mapping for OneHotEncoder) - assert col_map_cat is not None - vals_num, vals_cat = vals # infer the number of categorical output features - n_cat_cols = len( - np.unique([name for names in col_map_cat.values() for name in names]) - ) + # `col_map_cat` contains information to which features the original categorical feature were mapped + # (i.e. 1-1 mapping for OrdinalEncoder, or 1-many mapping for OneHotEncoder). + n_cat_cols = len({name for names in col_map_cat.values() for name in names}) # quick check if everything is in order - if vals_cat is None: - assert n_cat_cols == 0 - else: - assert n_cat_cols == vals_cat.shape[1] + n_vals_cat_cols = 0 if vals_cat is None else vals_cat.shape[1] + if n_vals_cat_cols != n_cat_cols: + raise_log( + ValueError( + f"Expected `{n_cat_cols}` categorical value columns but only encountered `{n_vals_cat_cols}`" + ), + logger, + ) data = {} idx_num, idx_cat = 0, 0 @@ -352,8 +336,6 @@ def _reshape_out( data[col_name] = vals_cat[:, idx_cat] static_cov_columns.append(col_name) idx_cat += 1 - else: - pass else: # is_num and is_cat are False -> feature is not part of transformer, use original values data[col] = series.static_covariates[col] static_cov_columns.append(col) From b9f5b3494f1a566f122ae6b6c073dbe864325333 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Mon, 18 Jul 2022 22:09:08 +0200 Subject: [PATCH 14/16] applied suggestions from PR review part 2 --- .ipynb_checkpoints/Untitled-checkpoint.ipynb | 6 ++ Untitled.ipynb | 33 ++++++++++ .../static_covariates_transformer.py | 66 ++++++++++--------- .../test_static_covariates_transformer.py | 36 +++++----- darts/timeseries.py | 4 +- docs/userguide/covariates.md | 5 ++ examples/15-static-covariates.ipynb | 10 +-- 7 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 .ipynb_checkpoints/Untitled-checkpoint.ipynb create mode 100644 Untitled.ipynb diff --git a/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/.ipynb_checkpoints/Untitled-checkpoint.ipynb new file mode 100644 index 0000000000..363fcab7ed --- /dev/null +++ b/.ipynb_checkpoints/Untitled-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Untitled.ipynb b/Untitled.ipynb new file mode 100644 index 0000000000..9d969f0b3f --- /dev/null +++ b/Untitled.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "c28921d2-5852-488d-b2b6-e1161a1a3373", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "darts_37", + "language": "python", + "name": "darts_37" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/darts/dataprocessing/transformers/static_covariates_transformer.py b/darts/dataprocessing/transformers/static_covariates_transformer.py index fa7cf9a6a3..acbe44cb7e 100644 --- a/darts/dataprocessing/transformers/static_covariates_transformer.py +++ b/darts/dataprocessing/transformers/static_covariates_transformer.py @@ -22,8 +22,8 @@ class StaticCovariatesTransformer(InvertibleDataTransformer, FittableDataTransformer): def __init__( self, - scaler_num=None, - scaler_cat=None, + transformer_num=None, + transformer_cat=None, cols_num: Optional[List[str]] = None, cols_cat: Optional[List[str]] = None, name="StaticCovariatesTransformer", @@ -34,36 +34,36 @@ def __init__( only on static covariates of the series passed to ``fit()``, ``transform()``, ``fit_transform()``, and ``inverse_transform()``. It can both scale numerical features, as well as encode categorical features. - The underlying ``scaler_num`` and ``scaler_cat`` have to implement the ``fit()``, ``transform()``, + The underlying ``transformer_num`` and ``transformer_cat`` have to implement the ``fit()``, ``transform()``, and ``inverse_transform()`` methods (typically from scikit-learn). - By default, numerical and categorical columns/features are inferred and allocated to ``scaler_num`` and - ``scaler_cat``, respectively. Alternatively, specify which columns to scale/transform with ``cols_num`` + By default, numerical and categorical columns/features are inferred and allocated to ``transformer_num`` and + ``transformer_cat``, respectively. Alternatively, specify which columns to scale/transform with ``cols_num`` and ``cols_cat``. - Both ``scaler_num`` and ``scaler_cat`` are fit globally on static covariate data from all series passed - to :class:`StaticCovariatesTransformer.fit()` + Both ``transformer_num`` and ``transformer_cat`` are fit globally on static covariate data from all series + passed to :class:`StaticCovariatesTransformer.fit()` Parameters ---------- - scaler_num - The scaler to transform numeric static covariate columns with. It must provide ``fit()``, + transformer_num + The transformer to transform numeric static covariate columns with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. Default: :class:`sklearn.preprocessing.MinMaxScaler(feature_range=(0, 1))`; this will scale all values between 0 and 1. - scaler_cat + transformer_cat The encoder to transform categorical static covariate columns with. It must provide ``fit()``, ``transform()`` and ``inverse_transform()`` methods. Default: :class:`sklearn.preprocessing.OrdinalEncoder()`; this will convert categories into integer valued arrays where each integer stands for a specific category. cols_num - Optionally, a list of column names for which to apply the numeric transformer ``scaler_num``. + Optionally, a list of column names for which to apply the numeric transformer ``transformer_num``. By default, the transformer will infer all numerical features based on types, and scale them with - `scaler_num`. If an empty list, no column will be scaled. + `transformer_num`. If an empty list, no column will be scaled. cols_cat - Optionally, a list of column names for which to apply the categorical transformer `scaler_cat`. + Optionally, a list of column names for which to apply the categorical transformer `transformer_cat`. By default, the transformer will infer all categorical features based on types, and transform them with - `scaler_cat`. If an empty list, no column will be transformed. + `transformer_cat`. If an empty list, no column will be transformed. name A specific name for the :class:`StaticCovariatesTransformer`. n_jobs @@ -103,21 +103,25 @@ def __init__( comp3 0.5 1.0 """ super().__init__(name=name, n_jobs=n_jobs, verbose=verbose) - self.scaler_num = MinMaxScaler() if scaler_num is None else scaler_num - self.scaler_cat = OrdinalEncoder() if scaler_cat is None else scaler_cat + self.transformer_num = ( + MinMaxScaler() if transformer_num is None else transformer_num + ) + self.transformer_cat = ( + OrdinalEncoder() if transformer_cat is None else transformer_cat + ) - for scaler, scaler_name in zip( - [self.scaler_num, self.scaler_cat], - ["scaler_num", "scaler_cat"], + for transformer, transformer_name in zip( + [self.transformer_num, self.transformer_cat], + ["transformer_num", "transformer_cat"], ): if ( - not callable(getattr(scaler, "fit", None)) - or not callable(getattr(scaler, "transform", None)) - or not callable(getattr(scaler, "inverse_transform", None)) + not callable(getattr(transformer, "fit", None)) + or not callable(getattr(transformer, "transform", None)) + or not callable(getattr(transformer, "inverse_transform", None)) ): raise_log( ValueError( - f"The provided `{scaler_name}` object must have fit(), transform() and " + f"The provided `{transformer_name}` object must have fit(), transform() and " f"inverse_transform() methods" ), logger, @@ -156,11 +160,11 @@ def fit( data = data.to_numpy(copy=False) if sum(self.mask_num): - self.scaler_num.fit(data[:, self.mask_num]) + self.transformer_num.fit(data[:, self.mask_num]) if sum(self.mask_cat): - self.scaler_cat.fit(data[:, self.mask_cat]) + self.transformer_cat.fit(data[:, self.mask_cat]) # check how many features the transformer generates - n_cat_out = self.scaler_cat.transform( + n_cat_out = self.transformer_cat.transform( np.expand_dims(data[0, self.mask_cat], 0) ).shape[-1] if n_cat_out == sum(self.mask_cat): @@ -172,7 +176,7 @@ def fit( { col: [f"{col}_{cat}" for cat in categories] for col, categories in zip( - self.cols_cat, self.scaler_cat.categories_ + self.cols_cat, self.transformer_cat.categories_ ) } ) @@ -268,8 +272,8 @@ def _transform_iterator( # use numerical and categorical transformers for 'ts_transform()' return zip( series, - [self.scaler_num] * len(series), - [self.scaler_cat] * len(series), + [self.transformer_num] * len(series), + [self.transformer_cat] * len(series), ) def _inverse_transform_iterator( @@ -278,8 +282,8 @@ def _inverse_transform_iterator( # use numerical and categorical transformers for 'ts_inverse_transform()' return zip( series, - [self.scaler_num] * len(series), - [self.scaler_cat] * len(series), + [self.transformer_num] * len(series), + [self.transformer_cat] * len(series), ) @staticmethod diff --git a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py index 36a62bd7b5..a8e00033a2 100644 --- a/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py +++ b/darts/tests/dataprocessing/transformers/test_static_covariates_transformer.py @@ -56,7 +56,7 @@ def test_scaling_single_series(self): ) for series in [self.series1, self.series2]: scaler = StaticCovariatesTransformer( - scaler_num=MinMaxScaler(feature_range=(-1, 1)) + transformer_num=MinMaxScaler(feature_range=(-1, 1)) ) self.helper_test_scaling(series, scaler, test_values) @@ -68,23 +68,23 @@ def test_scaling_single_series(self): ] ) for series in [self.series1, self.series2]: - scaler = StaticCovariatesTransformer(scaler_cat=OneHotEncoder()) + scaler = StaticCovariatesTransformer(transformer_cat=OneHotEncoder()) self.helper_test_scaling(series, scaler, test_values) def test_single_type_scaler(self): - scaler_cont = StaticCovariatesTransformer() + transformer_cont = StaticCovariatesTransformer() series_cont = self.series1.with_static_covariates( self.series1.static_covariates[["cont1", "cont2"]] ) test_cont = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]]) - self.helper_test_scaling(series_cont, scaler_cont, test_cont) + self.helper_test_scaling(series_cont, transformer_cont, test_cont) - scaler_cat = StaticCovariatesTransformer() + transformer_cat = StaticCovariatesTransformer() series_cat = self.series1.with_static_covariates( self.series1.static_covariates[["cat1", "cat2"]] ) test_cat = np.array([[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]]) - self.helper_test_scaling(series_cat, scaler_cat, test_cat) + self.helper_test_scaling(series_cat, transformer_cat, test_cat) def test_selected_columns(self): test_cont = ( @@ -94,10 +94,10 @@ def test_selected_columns(self): .astype(dtype={1: "O", 3: "O"}) .values ) - scaler_cont2 = StaticCovariatesTransformer( + transformer_cont2 = StaticCovariatesTransformer( cols_num=["cont1", "cont2"], cols_cat=[] ) - self.helper_test_scaling(self.series1, scaler_cont2, test_cont) + self.helper_test_scaling(self.series1, transformer_cont2, test_cont) test_contcat = ( pd.DataFrame( @@ -106,16 +106,18 @@ def test_selected_columns(self): .astype(dtype={1: "O"}) .values ) - scaler_contcat = StaticCovariatesTransformer( + transformer_contcat = StaticCovariatesTransformer( cols_num=["cont2"], cols_cat=["cat2"] ) - self.helper_test_scaling(self.series1, scaler_contcat, test_contcat) + self.helper_test_scaling(self.series1, transformer_contcat, test_contcat) test_cat = pd.DataFrame( [[0.0, 0.0, 0.1, 0.0], [1.0, 1.0, 0.2, 1], [2.0, 2.0, 0.3, 2.0]], ).values - scaler_cat = StaticCovariatesTransformer(cols_num=[], cols_cat=["cat1", "cat2"]) - self.helper_test_scaling(self.series1, scaler_cat, test_cat) + transformer_cat = StaticCovariatesTransformer( + cols_num=[], cols_cat=["cat1", "cat2"] + ) + self.helper_test_scaling(self.series1, transformer_cat, test_cat) def test_custom_scaler(self): # invalid scaler with missing inverse_transform @@ -127,19 +129,19 @@ def transform(self): pass with pytest.raises(ValueError): - _ = StaticCovariatesTransformer(scaler_num=InvalidScaler()) + _ = StaticCovariatesTransformer(transformer_num=InvalidScaler()) with pytest.raises(ValueError): - _ = StaticCovariatesTransformer(scaler_cat=InvalidScaler()) + _ = StaticCovariatesTransformer(transformer_cat=InvalidScaler()) class ValidScaler(InvalidScaler): def inverse_transform(self): pass - _ = StaticCovariatesTransformer(scaler_num=ValidScaler()) - _ = StaticCovariatesTransformer(scaler_cat=ValidScaler()) + _ = StaticCovariatesTransformer(transformer_num=ValidScaler()) + _ = StaticCovariatesTransformer(transformer_cat=ValidScaler()) _ = StaticCovariatesTransformer( - scaler_num=ValidScaler(), scaler_cat=ValidScaler() + transformer_num=ValidScaler(), transformer_cat=ValidScaler() ) def test_scaling_multi_series(self): diff --git a/darts/timeseries.py b/darts/timeseries.py index c78028c4a6..9873367f3b 100644 --- a/darts/timeseries.py +++ b/darts/timeseries.py @@ -770,9 +770,7 @@ def from_group_dataframe( # store static covariate Series and group DataFrame (without static cov columns) splits.append( ( - pd.DataFrame([static_cov_vals], columns=static_cov_cols).astype( - {col: df[col].dtype for col in static_cov_cols} - ), + pd.DataFrame([static_cov_vals], columns=static_cov_cols), group.drop(columns=static_cov_cols), ) ) diff --git a/docs/userguide/covariates.md b/docs/userguide/covariates.md index 7b1f73f507..cbee309ce8 100644 --- a/docs/userguide/covariates.md +++ b/docs/userguide/covariates.md @@ -125,17 +125,22 @@ Model | Past Covariates | Future Covariates | Static Covariates --- | :---: | :---: | :---: **Local Forecasting Models (LFMs)** | | | `ExponentialSmoothing` | | | +`BATS` and `TBATS` | | | `Theta` and `FourTheta` | | | `FFT` | | | +`Croston method`| | | `ARIMA` | | ✅ | `VARIMA` | | ✅ | `AutoARIMA` | | ✅ | +`StatsForecastAutoARIMA` | | ✅ | +`KalmanForecaster` | | ✅ | `Prophet` | | ✅ | **Global Forecasting Models (GFMs)** | | | `RegressionModel`* | ✅ | ✅ | `RNNModel`** | | ✅ | `BlockRNNModel`*** | ✅ | | `NBEATSModel` | ✅ | | +`NHiTSModel` | ✅ | | `TCNModel` | ✅ | | `TransformerModel` | ✅ | | `TFTModel` | ✅ | ✅ | ✅ diff --git a/examples/15-static-covariates.ipynb b/examples/15-static-covariates.ipynb index 724fc1192d..c658df5ee4 100644 --- a/examples/15-static-covariates.ipynb +++ b/examples/15-static-covariates.ipynb @@ -364,11 +364,11 @@ "id": "71c466ae-b136-4307-a9b5-550554ffc467", "metadata": {}, "source": [ - "### 5. Scaling/Encoding static covariate data\n", + "### 5. Scaling/Encoding/Transforming static covariate data\n", "There might be the need to scale numeric static covariates or encode categorical static covariates as not all models can handle non numeric static covariates. \n", "\n", "Use `StaticCovariatesTransformer` (see the docs [here](https://unit8co.github.io/darts/generated_api/darts.dataprocessing.transformers.static_covariates_transformer.html#staticcovariatestransformer)) to scale/transform static covariates. By default it uses a `MinMaxScaler` to scale numeric data, and a `OrdinalEncoder` to encode categorical data.\n", - "Both the numeric scaler and categorical encoder will be fit globally on static covariate data of all time series passed to `StaticCovariatesTransformer.fit()`" + "Both the numeric and categorical transformers will be fit globally on static covariate data of all time series passed to `StaticCovariatesTransformer.fit()`" ] }, { @@ -975,9 +975,9 @@ ], "metadata": { "kernelspec": { - "display_name": "darts_39", + "display_name": "darts_37", "language": "python", - "name": "darts_39" + "name": "darts_37" }, "language_info": { "codemirror_mode": { @@ -989,7 +989,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.12" + "version": "3.7.12" } }, "nbformat": 4, From ba82e6d5337eb805f4ab26251626f3a682dfc369 Mon Sep 17 00:00:00 2001 From: dennisbader Date: Tue, 19 Jul 2022 21:13:09 +0200 Subject: [PATCH 15/16] added automatic embedding size option for TFTModel --- darts/models/forecasting/block_rnn_model.py | 6 +- darts/models/forecasting/nbeats.py | 6 +- darts/models/forecasting/nhits.py | 6 +- darts/models/forecasting/rnn_model.py | 6 +- darts/models/forecasting/tcn_model.py | 6 +- darts/models/forecasting/tft_model.py | 70 +++++++++++-------- darts/models/forecasting/transformer_model.py | 12 ++-- 7 files changed, 62 insertions(+), 50 deletions(-) diff --git a/darts/models/forecasting/block_rnn_model.py b/darts/models/forecasting/block_rnn_model.py index 3dd8e9d606..736a6a2a2b 100644 --- a/darts/models/forecasting/block_rnn_model.py +++ b/darts/models/forecasting/block_rnn_model.py @@ -178,12 +178,12 @@ def __init__( PyTorch loss function used for training. This parameter will be ignored for probabilistic models if the ``likelihood`` parameter is specified. Default: ``torch.nn.MSELoss()``. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood One of Darts' :meth:`Likelihood ` models to be used for probabilistic forecasts. Default: ``None``. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs diff --git a/darts/models/forecasting/nbeats.py b/darts/models/forecasting/nbeats.py index 189ec9c93b..8616cea110 100644 --- a/darts/models/forecasting/nbeats.py +++ b/darts/models/forecasting/nbeats.py @@ -601,12 +601,12 @@ def __init__( PyTorch loss function used for training. This parameter will be ignored for probabilistic models if the ``likelihood`` parameter is specified. Default: ``torch.nn.MSELoss()``. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood One of Darts' :meth:`Likelihood ` models to be used for probabilistic forecasts. Default: ``None``. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs diff --git a/darts/models/forecasting/nhits.py b/darts/models/forecasting/nhits.py index 7bd06b7a37..9f5efb0be2 100644 --- a/darts/models/forecasting/nhits.py +++ b/darts/models/forecasting/nhits.py @@ -537,12 +537,12 @@ def __init__( PyTorch loss function used for training. This parameter will be ignored for probabilistic models if the ``likelihood`` parameter is specified. Default: ``torch.nn.MSELoss()``. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood One of Darts' :meth:`Likelihood ` models to be used for probabilistic forecasts. Default: ``None``. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs diff --git a/darts/models/forecasting/rnn_model.py b/darts/models/forecasting/rnn_model.py index 2f709be84a..ddfba48c65 100644 --- a/darts/models/forecasting/rnn_model.py +++ b/darts/models/forecasting/rnn_model.py @@ -257,12 +257,12 @@ def __init__( PyTorch loss function used for training. This parameter will be ignored for probabilistic models if the ``likelihood`` parameter is specified. Default: ``torch.nn.MSELoss()``. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood One of Darts' :meth:`Likelihood ` models to be used for probabilistic forecasts. Default: ``None``. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs diff --git a/darts/models/forecasting/tcn_model.py b/darts/models/forecasting/tcn_model.py index 008805e146..d1d39c111a 100644 --- a/darts/models/forecasting/tcn_model.py +++ b/darts/models/forecasting/tcn_model.py @@ -300,12 +300,12 @@ def __init__( PyTorch loss function used for training. This parameter will be ignored for probabilistic models if the ``likelihood`` parameter is specified. Default: ``torch.nn.MSELoss()``. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood One of Darts' :meth:`Likelihood ` models to be used for probabilistic forecasts. Default: ``None``. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs diff --git a/darts/models/forecasting/tft_model.py b/darts/models/forecasting/tft_model.py index 364ee12ce0..c6a807b0bb 100644 --- a/darts/models/forecasting/tft_model.py +++ b/darts/models/forecasting/tft_model.py @@ -22,6 +22,7 @@ _InterpretableMultiHeadAttention, _MultiEmbedding, _VariableSelectionNetwork, + get_embedding_size, ) from darts.models.forecasting.torch_forecasting_model import MixedCovariatesTorchModel from darts.utils.data import ( @@ -630,7 +631,9 @@ def __init__( feed_forward: str = "GatedResidualNetwork", dropout: float = 0.1, hidden_continuous_size: int = 8, - categorical_embedding_sizes: Optional[Dict[str, Tuple[int, int]]] = None, + categorical_embedding_sizes: Optional[ + Dict[str, Union[int, Tuple[int, int]]] + ] = None, add_relative_index: bool = False, loss_fn: Optional[nn.Module] = None, likelihood: Optional[Likelihood] = None, @@ -656,51 +659,50 @@ def __init__( Parameters ---------- - input_chunk_length : int + input_chunk_length Encoder length; number of past time steps that are fed to the forecasting module at prediction time. - output_chunk_length : int + output_chunk_length Decoder length; number of future time steps that are fed to the forecasting module at prediction time. - hidden_size : int + hidden_size Hidden state size of the TFT. It is the main hyper-parameter and common across the internal TFT architecture. - lstm_layers : int + lstm_layers Number of layers for the Long Short Term Memory (LSTM) Encoder and Decoder (1 is a good default). - num_attention_heads : int + num_attention_heads Number of attention heads (4 is a good default) - full_attention : bool + full_attention If ``True``, applies multi-head attention query on past (encoder) and future (decoder) parts. Otherwise, only queries on future part. Defaults to ``False``. - feed_forward : str + feed_forward A feedforward network is a fully-connected layer with an activation. TFT Can be one of the glu variant's FeedForward Network (FFN)[2]. The glu variant's FeedForward Network are a series of FFNs designed to work - better with Transformer based models. Defaults to ``"GatedResidualNetwork"``. - ["GLU", "Bilinear", "ReGLU", "GEGLU", "SwiGLU", "ReLU", "GELU"] - or the TFT original FeedForward Network. - ["GatedResidualNetwork"] - dropout : float + better with Transformer based models. Defaults to ``"GatedResidualNetwork"``. ["GLU", "Bilinear", "ReGLU", + "GEGLU", "SwiGLU", "ReLU", "GELU"] or the TFT original FeedForward Network ["GatedResidualNetwork"]. + dropout Fraction of neurons affected by dropout. This is compatible with Monte Carlo dropout at inference time for model uncertainty estimation (enabled with ``mc_dropout=True`` at prediction time). - hidden_continuous_size : int + hidden_continuous_size Default for hidden size for processing continuous variables - categorical_embedding_sizes : dict - A dictionary containing embedding sizes for categorical static covariates. The keys are the column names - of the categorical static covariates. The values are tuples of integers with - `(number of unique categories, embedding size)`. For example `{"some_column": (64, 8)}`. - Note that `TorchForecastingModels` can only handle numeric data. Consider transforming/encoding your data + categorical_embedding_sizes + A dictionary used to construct embeddings for categorical static covariates. The keys are the column names + of the categorical static covariates. Each value is either a single integer or a tuple of integers. + For a single integer give the number of unique categories (n) of the corresponding variable. For example + ``{"some_column": 64}``. The embedding size will be automatically determined by + ``min(round(1.6 * n**0.56), 100)``. + For a tuple of integers, give (number of unique categories, embedding size). For example + ``{"some_column": (64, 8)}``. + Note that ``TorchForecastingModels`` only support numeric data. Consider transforming/encoding your data with `darts.dataprocessing.transformers.static_covariates_transformer.StaticCovariatesTransformer`. - add_relative_index : bool + add_relative_index Whether to add positional values to future covariates. Defaults to ``False``. - This allows to use the TFTModel without having to pass future_covariates to :fun:`fit()` and + This allows to use the TFTModel without having to pass future_covariates to :func:`fit()` and :func:`train()`. It gives a value to the position of each step from input and output chunk relative to the prediction point. The values are normalized with ``input_chunk_length``. - loss_fn : nn.Module + loss_fn PyTorch loss function used for training. By default, the TFT model is probabilistic and uses a ``likelihood`` instead (``QuantileRegression``). To make the model deterministic, you can set the ` `likelihood`` to None and give a ``loss_fn`` argument. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood The likelihood model to be used for probabilistic forecasts. By default, the TFT uses a ``QuantileRegression`` likelihood. @@ -708,6 +710,9 @@ def __init__( Optional arguments to initialize the pytorch_lightning.Module, pytorch_lightning.Trainer, and Darts' :class:`TorchForecastingModel`. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs @@ -990,11 +995,20 @@ def _create_model(self, train_sample: MixedCovariatesTrainTensorType) -> nn.Modu static_input_numeric.append(static_var) reals_input.append(static_var) else: + # get embedding sizes for each categorical variable + embedding = self.categorical_embedding_sizes[col_name] + raise_if_not( + isinstance(embedding, (int, tuple)), + "Dict values of `categorical_embedding_sizes` must either be integers or tuples. Read " + "the TFTModel documentation for more information.", + logger, + ) + if isinstance(embedding, int): + embedding = (embedding, get_embedding_size(n=embedding)) + categorical_embedding_sizes[vars_meta[idx]] = embedding + static_input_categorical.append(static_var) categorical_input.append(static_var) - categorical_embedding_sizes[ - vars_meta[idx] - ] = self.categorical_embedding_sizes[col_name] variables_meta["model_config"]["reals_input"] = list(dict.fromkeys(reals_input)) variables_meta["model_config"]["categorical_input"] = list( diff --git a/darts/models/forecasting/transformer_model.py b/darts/models/forecasting/transformer_model.py index e33768b2e2..203ed9d7bc 100644 --- a/darts/models/forecasting/transformer_model.py +++ b/darts/models/forecasting/transformer_model.py @@ -257,10 +257,8 @@ def __init__( The activation function of encoder/decoder intermediate layer, (default='relu'). can be one of the glu variant's FeedForward Network (FFN)[2]. A feedforward network is a fully-connected layer with an activation. The glu variant's FeedForward Network are a series - of FFNs designed to work better with Transformer based models. - ["GLU", "Bilinear", "ReGLU", "GEGLU", "SwiGLU", "ReLU", "GELU"] - or one the pytorch internal activations - ["relu", "gelu"] + of FFNs designed to work better with Transformer based models. ["GLU", "Bilinear", "ReGLU", "GEGLU", + "SwiGLU", "ReLU", "GELU"] or one the pytorch internal activations ["relu", "gelu"] custom_encoder A custom user-provided encoder module for the transformer (default=None). custom_decoder @@ -273,12 +271,12 @@ def __init__( PyTorch loss function used for training. This parameter will be ignored for probabilistic models if the ``likelihood`` parameter is specified. Default: ``torch.nn.MSELoss()``. - torch_metrics - A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found - at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. likelihood One of Darts' :meth:`Likelihood ` models to be used for probabilistic forecasts. Default: ``None``. + torch_metrics + A torch metric or a ``MetricCollection`` used for evaluation. A full list of available metrics can be found + at https://torchmetrics.readthedocs.io/en/latest/. Default: ``None``. optimizer_cls The PyTorch optimizer class to be used. Default: ``torch.optim.Adam``. optimizer_kwargs From 81be52615699cbf18b74af484416329daa5dd4aa Mon Sep 17 00:00:00 2001 From: dennisbader Date: Wed, 20 Jul 2022 20:37:29 +0200 Subject: [PATCH 16/16] added test for TFTModel categorical static covariate support --- darts/tests/models/forecasting/test_TFT.py | 32 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/darts/tests/models/forecasting/test_TFT.py b/darts/tests/models/forecasting/test_TFT.py index 9f378dc7d1..b66f818d7f 100644 --- a/darts/tests/models/forecasting/test_TFT.py +++ b/darts/tests/models/forecasting/test_TFT.py @@ -14,6 +14,7 @@ from torch.nn import MSELoss from darts.models.forecasting.tft_model import TFTModel + from darts.models.forecasting.tft_submodels import get_embedding_size from darts.utils.likelihood_models import QuantileRegression TORCH_AVAILABLE = True @@ -171,22 +172,49 @@ def test_static_covariates_support(self): ) target_multi = target_multi.with_static_covariates( - pd.DataFrame([[0.0, 1.0], [2.0, 3.0]], index=["st1", "st2"]) + pd.DataFrame( + [[0.0, 1.0, 0, 2], [2.0, 3.0, 1, 3]], + columns=["st1", "st2", "cat1", "cat2"], + ) ) # should work with cyclic encoding for time index + # set categorical embedding sizes once with automatic embedding size with an `int` and once by + # manually setting it with `tuple(int, int)` model = TFTModel( input_chunk_length=3, output_chunk_length=4, add_encoders={"cyclic": {"future": "hour"}}, + categorical_embedding_sizes={"cat1": 2, "cat2": (2, 2)}, pl_trainer_kwargs={"fast_dev_run": True}, ) model.fit(target_multi, verbose=False) + assert len(model.model.static_variables) == len( target_multi.static_covariates.columns ) - model.predict(n=1, series=target_multi, verbose=False) + # check model embeddings + target_embedding = { + "static_covariate_2": ( + 2, + get_embedding_size(2), + ), # automatic embedding size + "static_covariate_3": (2, 2), # manual embedding size + } + assert model.categorical_embedding_sizes == target_embedding + for cat_var, embedding_dims in target_embedding.items(): + assert ( + model.model.input_embeddings.embeddings[cat_var].num_embeddings + == embedding_dims[0] + ) + assert ( + model.model.input_embeddings.embeddings[cat_var].embedding_dim + == embedding_dims[1] + ) + + preds = model.predict(n=1, series=target_multi, verbose=False) + assert preds.static_covariates.equals(target_multi.static_covariates) # raise an error when trained with static covariates of wrong dimensionality target_multi = target_multi.with_static_covariates(