Source code for gtda.mapper.utils.decorators

"""Convenience class decorators for use in a Mapper context."""
# License: GNU AGPLv3

from sklearn.base import TransformerMixin


[docs]def method_to_transform(cls, method_name): """Wrap a class to add a :meth:`transform` method as an alias to an existing method. An example of use is for classes possessing a :meth:`score` method such as kernel density estimators and anomaly/novelty detection estimators, allow for these estimators are to be used as steps in a pipeline. Note that 1D array outputs are reshaped into 2D column vectors before being returned by the new :meth:`transform`. Parameters ---------- cls : object Class to be wrapped. If `method_name` is not one of its methods, :meth:`transform` always returns ``None``. method_name : str Name of the method in `cls` to which :meth:`transform` will be an alias. The fist argument of this method (after ``self``) becomes the ``X`` input for :meth:`transform`. Returns ------- wrapped_cls : object New class inheriting from :class:`sklearn.base.TransformerMixin`, so that both :meth:`transform` and :meth:`fit_transform` are available. Its name is the name of `cls` prepended with ``'Extended'``. Examples -------- >>> import numpy as np >>> from sklearn.neighbors import KernelDensity >>> from gtda.mapper import method_to_transform >>> X = np.random.random((100, 2)) >>> kde = KernelDensity() Extend ``KernelDensity`` to give it a ``transform`` method as an alias of ``score_samples`` (up to output shape). The new class is instantiated with the same parameters as the original one. >>> ExtendedKDE = method_to_transform(KernelDensity, 'score_samples') >>> extended_kde = ExtendedKDE() >>> Xt = kde.fit(X).score_samples(X) >>> print(Xt.shape) (100,) >>> Xt_extended = extended_kde.fit_transform(X) >>> print(Xt_extended.shape) (100, 1) >>> np.array_equal(Xt, Xt_extended.flatten()) True """ class ExtendedEstimator(cls, TransformerMixin): def transform(self, X, y=None): has_method = hasattr(self, method_name) if has_method: Xt = getattr(self, method_name)(X) # reshape 1D estimators to have shape (n_samples, 1) if Xt.ndim == 1: Xt = Xt[:, None] return Xt ExtendedEstimator.__name__ = 'Extended' + cls.__name__ return ExtendedEstimator