sktime.transformations.panel.rocket._minirocket_multivariate_variable 源代码

"""Multivariate MiniRocket transformer."""

__author__ = ["angus924", "michaelfeil"]
__all__ = ["MiniRocketMultivariateVariable"]

import multiprocessing
import warnings
from typing import Union

import numpy as np
import pandas as pd

from sktime.transformations.base import BaseTransformer


[文档]class MiniRocketMultivariateVariable(BaseTransformer): """MINIROCKET (Multivariate, unequal length). MINImally RandOm Convolutional KErnel Transform. [1]_ **Multivariate** and **unequal length** A provisional and naive extension of MINIROCKET to multivariate input with unequal length provided by the authors [2]_ . For better performance, use the sktime class MiniRocket for univariate input, and MiniRocketMultivariate to equal length multivariate input. This transformer fits one set of paramereters per individual series, and applies the transform with fitted parameter i to the i-th series in transform. Vanilla use requires same number of series in fit and transform. To fit and transform series at the same time, without an identification of fit/transform instances, wrap this transformer in ``FitInTransform``, from ``sktime.transformations.compose``. Parameters ---------- num_kernels : int, default=10_000 number of random convolutional kernels. This should be a multiple of 84. If it is lower than 84, it will be set to 84. If it is higher than 84 and not a multiple of 84, the number of kernels used to transform the data will rounded down to the next positive multiple of 84. max_dilations_per_kernel : int, default=32 maximum number of dilations per kernel. reference_length : int or str, default = ``'max'`` series-length of reference, str defines how to infer from X during 'fit'. options are ``'max'``, ``'mean'``, ``'median'``, ``'min'``. pad_value_short_series : float or None, default=None if padding series with len<9 to value. if None, not padding is performed. n_jobs : int, default=1 The number of jobs to run in parallel for ``transform``. ``-1`` means using all processors. random_state : None or int, default = None Attributes ---------- num_kernels_ : int The true number of kernels used in the rocket transform. This is num_kernels rounded down to the nearest multiple of 84. It is 84 if num_kernels is less than 84. Examples -------- >>> from sktime.transformations.panel.rocket import MiniRocketMultivariateVariable >>> from sktime.datasets import load_japanese_vowels >>> # load multivariate and unequal length dataset >>> X_train, _ = load_japanese_vowels(split="train", return_X_y=True) >>> X_test, _ = load_japanese_vowels(split="test", return_X_y=True) >>> pre_clf = MiniRocketMultivariateVariable( ... pad_value_short_series=0.0 ... ) # doctest: +SKIP >>> pre_clf.fit(X_train, y=None) # doctest: +SKIP MiniRocketMultivariateVariable(...) >>> X_transformed = pre_clf.transform(X_test) # doctest: +SKIP >>> X_transformed.shape # doctest: +SKIP (370, 9996) Raises ------ ValueError If any multivariate series_length in X is < 9 and pad_value_short_series is set to None See Also -------- MultiRocket, MiniRocket, MiniRocketMultivariate, Rocket References ---------- .. [1] Angus Dempster, Daniel F Schmidt, Geoffrey I Webb MINIROCKET: A Very Fast (Almost) Deterministic Transform for Time Series Classification, 2020, arXiv:2012.08791 .. [2] Angus Dempster, Daniel F Schmidt, Geoffrey I Webb https://github.com/angus924/minirocket """ _tags = { "authors": ["angus924", "michaelfeil"], "maintainers": ["angus924", "michaelfeil"], "univariate-only": False, "fit_is_empty": False, "scitype:transform-input": "Series", "scitype:transform-output": "Primitives", "capability:unequal_length": True, "scitype:transform-labels": "None", "scitype:instancewise": False, # is this an instance-wise transform? "X_inner_mtype": "df-list", # which mtypes do _fit/_predict support for X? "y_inner_mtype": "None", # which mtypes do _fit/_predict support for X? "requires_y": False, "python_dependencies": "numba", } def __init__( self, num_kernels=10_000, max_dilations_per_kernel=32, reference_length="max", pad_value_short_series=None, n_jobs=1, random_state=None, ): self.num_kernels = num_kernels self.max_dilations_per_kernel = max_dilations_per_kernel self.reference_length = reference_length self._fitted_reference_length = None self.pad_value_short_series = pad_value_short_series self.num_kernels_ = None self.n_jobs = n_jobs self.random_state = random_state if random_state is None: self.random_state_ = random_state elif isinstance(random_state, int): self.random_state_ = np.int32(random_state) else: raise ValueError( f"random_state in MiniRocketMultivariateVariable must be int or None, " f"but found <{type(random_state)} {random_state}>" ) self._reference_modes = ["max", "mean", "median", "min"] if not (isinstance(reference_length, int) and reference_length >= 9) and not ( isinstance(reference_length, str) and (reference_length in self._reference_modes) ): raise ValueError( "reference_length in MiniRocketMultivariateVariable must be int>=9 or " "'max', 'mean', 'median', but found reference_length=" f"{reference_length}" ) super().__init__() def _fit(self, X: list[pd.DataFrame], y=None): """Fits dilations and biases to input time series. Parameters ---------- X : pd.DataFrame Dataframe with n_instances-rows and n_dimensions-columns, each cell containing a series_length-long array. n_dimensions is equal across all instances in ``X``, and series_length is constant within each instance. y : ignored argument for interface compatibility Returns ------- self Raises ------ ValueError If any multivariate series_length in X is < 9 and pad_value_short_series is set to None """ from sktime.transformations.panel.rocket._minirocket_multi_var_numba import ( _fit_multi_var, ) X_2d_t, lengths_1darray = _nested_dataframe_to_transposed2D_array_and_len_list( X, pad=self.pad_value_short_series ) if isinstance(self.reference_length, int): _reference_length = self.reference_length elif self.reference_length in self._reference_modes: # np.mean, np.max, np.median, np.min .. _reference_length = getattr(np, self.reference_length)(lengths_1darray) else: raise ValueError( "reference_length in MiniRocketMultivariateVariable must be int>=9 or " "'max', 'mean', 'median', but found reference_length=" f"{self.reference_length}" ) self._fitted_reference_length = int(max(9, _reference_length)) if lengths_1darray.min() < 9: failed_index = np.where(lengths_1darray < 9)[0] raise ValueError( f"X must be >= 9 for all samples, but found minimum to be " f"{lengths_1darray.min()}; at index {failed_index}, pad shorter " "series so that n_timepoints >= 9 for all samples." ) if lengths_1darray.min() == lengths_1darray.max(): warnings.warn( "X is of equal length, consider using MiniRocketMultivariate for " "speedup and stability instead.", stacklevel=2, ) if X_2d_t.shape[0] == 1: warnings.warn( "X is univariate, consider using MiniRocket as Univariante for " "speedup and stability instead.", stacklevel=2, ) self.parameters = _fit_multi_var( X_2d_t, L=lengths_1darray, reference_length=self._fitted_reference_length, num_features=self.num_kernels, max_dilations_per_kernel=self.max_dilations_per_kernel, seed=self.random_state_, ) if self.num_kernels < 84: self.num_kernels_ = 84 else: self.num_kernels_ = (self.num_kernels // 84) * 84 return self def _transform(self, X, y=None): """Transform input time series. Parameters ---------- X : pd.DataFrame with nested columns Dataframe with n_instances-rows and n_dimensions-columns, each cell containing a series_length-long array y : ignored argument for interface compatibility Returns ------- pandas.DataFrame, size (n_instances, num_kernels) Raises ------ ValueError If any multivariate series_length in X is < 9 and pad_value_short_series is set to None """ from numba import get_num_threads, set_num_threads from sktime.transformations.panel.rocket._minirocket_multi_var_numba import ( _transform_multi_var, ) X_2d_t, L = _nested_dataframe_to_transposed2D_array_and_len_list( X, pad=self.pad_value_short_series ) # change n_jobs depended on value and existing cores prev_threads = get_num_threads() if self.n_jobs < 1 or self.n_jobs > multiprocessing.cpu_count(): n_jobs = multiprocessing.cpu_count() else: n_jobs = self.n_jobs set_num_threads(n_jobs) X_ = _transform_multi_var(X_2d_t, L, self.parameters) set_num_threads(prev_threads) return pd.DataFrame(X_)
def _nested_dataframe_to_transposed2D_array_and_len_list( X: list[pd.DataFrame], pad: Union[int, float, None] = 0 ): """Convert a nested dataframe to a 2D array and a list of lengths. Parameters ---------- X : List of dataframes List of length n_instances, with dataframes of series_length-rows and n_dimensions-columns pad : float or None. if float/int,pads multivariate series with 'pad', so that each series has at least length 9. if None, no padding is applied. Returns ------- np.array: 2D array of shape = [n_dimensions, sum(length_series(i) for i in n_instances)], np.float32 np.array: 1D array of shape = [n_instances] with length of each series, np.int32 Raises ------ ValueError If any multivariate series_length in X is < 9 and pad_value_short_series is set to None """ if not len(X): raise ValueError("X is empty") if isinstance(X, (tuple, list)) and isinstance(X[0], (pd.DataFrame, np.array)): pass else: raise ValueError("X must be List of pd.DataFrame") if not all(X[0].shape[1] == _x.shape[1] for _x in X): raise ValueError( "X must be nested pd.DataFrame or List of pd.DataFrame with n_dimensions" ) vec = [] lengths = [] for _x in X: _x_shape = _x.shape if _x_shape[0] < 9: if pad is not None: # emergency: pad with zeros up to 9. lengths.append(9) vec.append( np.vstack( [_x.values, np.full([9 - _x_shape[0], _x_shape[1]], float(pad))] ) ) else: raise ValueError( "X series_length must be >= 9 for all samples" f"but sample with series_length {_x_shape[0]} found. Consider" " padding, discard, or setting a pad_value_short_series value" ) else: lengths.append(_x_shape[0]) vec.append(_x.values) X_2d_t = np.vstack(vec).T.astype(dtype=np.float32) lengths = np.array(lengths, dtype=np.int32) if not lengths.sum() == X_2d_t.shape[1]: raise ValueError("X_new and lengths do not match. check input dimension") return X_2d_t, lengths