Source code for getml.feature_learning.multirel_time_series

# Copyright 2020 The SQLNet Company GmbH

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

"""
Feature learning based on Multi-Relational Decision Tree Learning.
"""

from .aggregations import Avg, Count, Max, Min, Sum

from .feature_learner import _FeatureLearner

from .loss_functions import SquareLoss

from .validation import (
    _validate_multirel_model_parameters,
    _validate_time_series_parameters
)

# --------------------------------------------------------------------


[docs]class MultirelTimeSeries(_FeatureLearner): """Feature learning for time series based on multi-relational decision tree learning. :class:`~getml.models.MultirelTimeSeries` automates feature learning for time series. It adds a convenience layer over :class:`~getml.feature_learning.MultirelModel` which abstracts away the self-join over the population table. For more information on the underlying feature learning algorithm, check out the :ref:`User guide <feature_engineering_algorithms_multirel>`. Args: horizon (float, optional): The period of time you want to look ahead to generate the predictions. memory (float, optional): The period of time you want to the to look back until the algorithm "forgets" the data. If you set memory to 0.0, then there will be no limit. self_join_keys (List[str], optional): A list of the join keys to use for the self join. If none are passed, then the self join will take place on the entire population table. ts_name (str, optional): The name of the time stamp column to be used. If none is passed, then the row ID will be used. allow_lagged_targets (bool, optional): In some time series problems, it is allowed to aggregate over target variables from the past. In others, this is not allowed. If *allow_lagged_targets* is set to True, you must pass a horizon that is greater than zero, otherwise you would have a data leak (an exception will be thrown to prevent this). aggregation (List[:class:`~getml.models.aggregations`], optional): Mathematical operations used by the automated feature learning algorithm to create new features. Possible options: * :const:`~getml.models.aggregations.Avg` * :const:`~getml.models.aggregations.Count` * :const:`~getml.models.aggregations.CountDistinct` * :const:`~getml.models.aggregations.CountMinusCountDistinct` * :const:`~getml.models.aggregations.Max` * :const:`~getml.models.aggregations.Median` * :const:`~getml.models.aggregations.Min` * :const:`~getml.models.aggregations.Stddev` * :const:`~getml.models.aggregations.Sum` * :const:`~getml.models.aggregations.Var` allow_sets (bool, optional): Multirel can summarize different categories into sets for producing conditions. When expressed as SQL statements these sets might look like this: .. code-block:: sql t2.category IN ( 'value_1', 'value_2', ... ) This can be very powerful, but it can also produce features that are hard to read and might be prone to overfitting when the `sampling_factor` is too low. delta_t (float, optional): Frequency with which lag variables will be explored in a time series setting. When set to 0.0, there will be no lag variables. For more information please refer to :ref:`data_model_time_series`. Range: [0, :math:`\\infty`] grid_factor (float, optional): Multirel will try a grid of critical values for your numerical features. A higher `grid_factor` will lead to a larger number of critical values being considered. This can increase the training time, but also lead to more accurate features. Range: (0, :math:`\\infty`] include_categorical (bool, optional): Whether you want to pass categorical columns from the population table to the `feature_selector` and `predictor`. Passing columns directly allows you to include handcrafted feature as well as raw data. Note, however, that this does not guarantee their presence in the resulting features because it is the task of the `feature_selector` to pick only the best performing ones. loss_function (:class:`~getml.models.loss_functions`, optional): Objective function used by the feature learning algorithm to optimize your features. For regression problems use :class:`~getml.models.loss_functions.SquareLoss` and for classification problems use :class:`~getml.models.loss_functions.CrossEntropyLoss`. max_length (int, optional): The maximum length a subcondition might have. Multirel will create conditions in the form .. code-block:: sql (condition 1.1 AND condition 1.2 AND condition 1.3 ) OR ( condition 2.1 AND condition 2.2 AND condition 2.3 ) ... Using this parameter you can set the maximum number of conditions allowed in the brackets. Range: [0, :math:`\\infty`] min_num_samples (int, optional): Determines the minimum number of samples a subcondition should apply to in order for it to be considered. Higher values lead to less complex statements and less danger of overfitting. Range: [1, :math:`\\infty`] num_features (int, optional): Number of features generated by the feature learning algorithm. Range: [1, :math:`\\infty`] num_subfeatures (int, optional): The number of subfeatures you would like to extract in a subensemble (for snowflake data model only). See :ref:`data_model_snowflake_schema` for more information. Range: [1, :math:`\\infty`] num_threads (int, optional): Number of threads used by the feature learning algorithm. If set to zero or a negative value, the number of threads will be determined automatically by the getML engine. Range: [:math:`0`, :math:`\\infty`] regularization (float, optional): Most important regularization parameter for the quality of the features produced by Multirel. Higher values will lead to less complex features and less danger of overfitting. A `regularization` of 1.0 is very strong and allows no conditions. Range: [0, 1] round_robin (bool, optional): If True, the Multirel picks a different `aggregation` every time a new feature is generated. sampling_factor (float, optional): Multirel uses a bootstrapping procedure (sampling with replacement) to train each of the features. The sampling factor is proportional to the share of the samples randomly drawn from the population table every time Multirel generates a new feature. A lower sampling factor (but still greater than 0.0), will lead to less danger of overfitting, less complex statements and faster training. When set to 1.0, roughly 2,000 samples are drawn from the population table. If the population table contains less than 2,000 samples, it will use standard bagging. When set to 0.0, there will be no sampling at all. Range: [0, :math:`\\infty`] seed (Union[int,None], optional): Seed used for the random number generator that underlies the sampling procedure to make the calculation reproducible. Internally, a `seed` of None will be mapped to 5543. Range: [0, :math:`\\infty`] share_aggregations (float, optional): Every time a new feature is generated, the `aggregation` will be taken from a random subsample of possible aggregations and values to be aggregated. This parameter determines the size of that subsample. Only relevant when `round_robin` is False. Range: (0, 1] share_conditions (float, optional): Every time a new column is tested for applying conditions, it might be skipped at random. This parameter determines the probability that a column will *not* be skipped. Range: [0, 1] shrinkage (float, optional): Since Multirel works using a gradient-boosting-like algorithm, `shrinkage` (or learning rate) scales down the weights and thus the impact of each new tree. This gives more room for future ones to improve the overall performance of the model in this greedy algorithm. Higher values will lead to more danger of overfitting. Range: [0, 1] silent (bool, optional): Controls the logging during training. use_timestamps (bool, optional): Whether you want to ignore all elements in the peripheral tables where the time stamp is greater than the one in the corresponding elements of the population table. In other words, this determines whether you want add the condition .. code-block:: sql t2.time_stamp <= t1.time_stamp at the very end of each feature. It is strongly recommend to enable this behavior. Raises: TypeError: If any of the input arguments is of wrong type. KeyError: If an unsupported instance variable is encountered. TypeError: If any instance variable is of wrong type. ValueError: If any instance variable does not match its possible choices (string) or is out of the expected bounds (numerical). Example: .. code-block:: python # Our forecast horizon is 0. # We do not predict the future, instead we infer # the present state from current and past sensor data. horizon = 0.0 # We do not allow the time series features # to use target values from the past. # (Otherwise, we would need the horizon to # be greater than 0.0). allow_lagged_targets = False # We want our time series features to only use # data from the last 15 minutes memory = getml.data.time.minutes(15) feature_learner = getml.feature_learning.MultirelTimeSeries( ts_name="date", horizon=horizon, memory=memory, allow_lagged_targets=allow_lagged_targets, num_features=30, loss_function=getml.feature_learning.loss_functions.CrossEntropyLoss ) predictor = getml.predictors.XGBoostClassifier(reg_lambda=500) pipe = getml.pipeline.Pipeline( tags=["memory=15", "no ts_name", "multirel"], feature_learners=[feature_learner], predictors=[predictor] ) pipe.check(data_train) pipe = pipe.fit(data_train) predictions = pipe.predict(data_test) scores = pipe.score(data_test) """ # ---------------------------------------------------------------- def __init__(self, horizon=0.0, memory=0.0, self_join_keys=None, ts_name="", allow_lagged_targets=True, aggregation=None, allow_sets=True, delta_t=0.0, grid_factor=1.0, loss_function=SquareLoss, max_length=4, min_num_samples=1, num_features=100, num_subfeatures=5, num_threads=0, regularization=0.0, round_robin=False, sampling_factor=1.0, seed=None, share_aggregations=0.25, share_conditions=1.0, shrinkage=0.0, silent=True, use_timestamps=True): # ------------------------------------------------------------ aggregation = aggregation or [Avg, Count, Max, Min, Sum] # ------------------------------------------------------------ self.type = "MultirelTimeSeries" # ------------------------------------------------------------ # Standard multirel hyperparameters self.aggregation = aggregation self.allow_sets = allow_sets self.delta_t = delta_t self.grid_factor = grid_factor self.loss_function = loss_function self.max_length = max_length self.min_num_samples = min_num_samples self.num_features = num_features self.num_subfeatures = num_subfeatures self.num_threads = num_threads self.regularization = regularization self.round_robin = round_robin self.sampling_factor = sampling_factor self.seed = seed or 5543 self.share_aggregations = share_aggregations self.share_conditions = share_conditions self.shrinkage = shrinkage self.silent = silent self.use_timestamps = use_timestamps # ------------------------------------------------------------ # Additional time series parameters self.horizon = horizon self.memory = memory self.self_join_keys = self_join_keys or [] self.ts_name = ts_name self.allow_lagged_targets = allow_lagged_targets # ------------------------------------------------------------ MultirelTimeSeries._supported_params = list(self.__dict__.keys()) # ------------------------------------------------------------ self.validate() # ----------------------------------------------------------------
[docs] def validate(self, params=None): """Checks both the types and the values of all instance variables and raises an exception if something is off. Args: params (dict, optional): A dictionary containing the parameters to validate. If not is passed, the own parameters will be validated. Raises: KeyError: If an unsupported instance variable is encountered. TypeError: If any instance variable is of wrong type. ValueError: If any instance variable does not match its possible choices (string) or is out of the expected bounds (numerical). """ # ------------------------------------------------------------ params = params or self.__dict__ if not isinstance(params, dict): raise ValueError("params must be None or a dictionary!") # ------------------------------------------------------------ if not isinstance(params["silent"], bool): raise TypeError("'silent' must be of type bool") if params["type"] != "MultirelTimeSeries": raise ValueError("'type' must be 'MultirelTimeSeries'") # ------------------------------------------------------------ for kkey in params: if kkey not in MultirelTimeSeries._supported_params: raise KeyError( """Instance variable [""" + kkey + """] is not supported in MultirelTimeSeries.""") # ------------------------------------------------------------ _validate_multirel_model_parameters(**params) # ------------------------------------------------------------ _validate_time_series_parameters(**params)
# --------------------------------------------------------------------