Source code for pyts.multivariate.classification.multivariate

"""Utility class for multivariate time series classification."""

# Author: Johann Faouzi <johann.faouzi@gmail.com>
# License: BSD-3-Clause

import numpy as np
from numba import njit, prange
from sklearn.base import BaseEstimator, ClassifierMixin, clone
from sklearn.preprocessing import LabelEncoder
from sklearn.utils.validation import check_is_fitted
from ..utils import check_3d_array

import sklearn
SKLEARN_VERSION = sklearn.__version__


@njit()
def _hard_vote(y_pred, weights):
    n_samples, n_features = y_pred.shape
    maj = np.empty(n_samples, dtype=np.int64)
    for i in prange(n_samples):
        maj[i] = np.argmax(np.bincount(y_pred[i], weights))
    return maj


[docs]class MultivariateClassifier(BaseEstimator, ClassifierMixin): """Classifier for multivariate time series. It provides a convenient class to classify multivariate time series with classifier that can only deal with univariate time series. The labels are predicted in a hard voting fashion using the predictions for each feature. Parameters ---------- estimator : estimator object or list thereof Classifier. If one estimator is provided, it is cloned and each clone performs prediction for one feature. If a list of estimators is provided, each estimator performs prediction for one feature. weights : array-like, shape = (n_classifiers,) or None (default=None) Sequence of weights (`float` or `int`) to weight the occurrences of predicted class labels. Uses uniform weights if None. Attributes ---------- classes_ : array, shape = (n_classes,) An array of class labels known to the classifier. estimators_ : list of estimator objects The collection of fitted classifiers. Examples -------- >>> from pyts.classification import BOSSVS >>> from pyts.datasets import load_basic_motions >>> from pyts.multivariate.classification import MultivariateClassifier >>> X_train, X_test, y_train, y_test = load_basic_motions(return_X_y=True) >>> clf = MultivariateClassifier(BOSSVS()) >>> clf.fit(X_train, y_train) # doctest: +ELLIPSIS MultivariateClassifier(...) >>> clf.score(X_test, y_test) 1.0 """
[docs] def __init__(self, estimator, weights=None): self.estimator = estimator self.weights = weights
[docs] def fit(self, X, y): """Fit each classifier. Parameters ---------- X : array-like, shape = (n_samples, n_features, n_timestamps) Multivariate time series. y : None or array-like, shape = (n_samples,) Class labels. Returns ------- self : object """ X = check_3d_array(X) _, n_features, _ = X.shape self._check_params(n_features) if self.weights is None: self._weights = None else: self._weights = np.asarray(self.weights) self._le = LabelEncoder().fit(y) self.classes_ = self._le.classes_ y_ind = self._le.transform(y) for i, clf in enumerate(self.estimators_): clf.fit(X[:, i, :], y_ind) return self
[docs] def predict(self, X): """Predict class labels using hard voting. Parameters ---------- X : array-like, shape = (n_samples, n_features, n_timestamps) Multivariate time series. Returns ------- y_pred : array, shape = (n_samples,) Predicted class labels. """ X = check_3d_array(X) if SKLEARN_VERSION >= '0.22': check_is_fitted(self) else: check_is_fitted(self, 'estimators_') n_samples, n_features, _ = X.shape y_pred = np.empty((n_samples, n_features)) for i, clf in enumerate(self.estimators_): y_pred[:, i] = clf.predict(X[:, i, :]) maj = _hard_vote(y_pred.astype('int64'), self._weights) return self._le.inverse_transform(maj)
def _check_params(self, n_features): """Check parameters.""" if (isinstance(self.estimator, BaseEstimator) and hasattr(self.estimator, 'predict')): self.estimators_ = [clone(self.estimator) for _ in range(n_features)] elif isinstance(self.estimator, list): if len(self.estimator) != n_features: raise ValueError( "If 'estimator' is a list, its length must be equal to " "the number of features ({0} != {1})" .format(len(self.estimator), n_features) ) for i, estimator in enumerate(self.estimator): if not (isinstance(estimator, BaseEstimator) and hasattr(estimator, 'predict')): raise ValueError("Estimator {} must be a classifier." .format(i)) self.estimators_ = self.estimator else: raise TypeError( "'estimator' must be a classifier that inherits from " "sklearn.base.BaseEstimator or a list thereof." )