Source code for skfolio.preprocessing._transformer._cross_sectional._base

"""Base class for cross-sectional transformers."""

# Copyright (c) 2023-2026
# Author: Hugo Delatte <hugo.delatte@skfoliolabs.com>
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

from abc import ABC, abstractmethod

import sklearn.base as skb
import sklearn.utils.validation as skv
from sklearn.utils.validation import FLOAT_DTYPES

from skfolio.typing import ArrayLike, FloatArray

__all__ = ["BaseCSTransformer"]


[docs] class BaseCSTransformer(skb.OneToOneFeatureMixin, skb.BaseEstimator, ABC): """Base class for all cross-sectional transformers in skfolio. Cross-sectional transformers process each observation of a 2D input array using values from the same observation only. These transformers are stateless. The default `fit` method validates the estimator parameters, validates `X`, and records `n_features_in_` for scikit-learn compatibility. Notes ----- All estimators should specify all the parameters that can be set at the class level in their `__init__` as explicit keyword arguments (no `*args` or `**kwargs`). """ @abstractmethod def __init__(self): """Initialize the transformer.""" pass def _validate_params(self) -> None: """Validate estimator-specific parameters.""" return None
[docs] def fit( self, X: ArrayLike, y=None, cs_weights: ArrayLike | None = None, cs_groups: ArrayLike | None = None, ): """Fit the transformer. Cross-sectional transformers are stateless and do not learn data-dependent parameters. This method validates the estimator parameters, validates `X`, and records `n_features_in_` for scikit-learn compatibility. Parameters ---------- X : array-like of shape (n_observations, n_assets) Input matrix where each row is an observation and each column is an asset. y : Ignored Not used, present for API consistency by convention. cs_weights : array-like of shape (n_observations, n_assets), optional Optional cross-sectional weights accepted for API consistency with `transform`. They are ignored during fitting. cs_groups : array-like of shape (n_observations, n_assets), optional Optional cross-sectional group labels accepted for API consistency with `transform`. They are ignored during fitting. Returns ------- self : BaseCSTransformer Fitted estimator. """ self._validate_params() skv.validate_data( self, X, reset=True, dtype=FLOAT_DTYPES, ensure_all_finite="allow-nan" ) return self
[docs] @abstractmethod def transform( self, X: ArrayLike, cs_weights: ArrayLike | None = None, cs_groups: ArrayLike | None = None, ) -> FloatArray: """Transform `X` observation by observation. Parameters ---------- X : array-like of shape (n_observations, n_assets) Input matrix where each row is an observation and each column is an asset. cs_weights : array-like of shape (n_observations, n_assets), optional Optional cross-sectional weights used by the concrete transformer. cs_groups : array-like of shape (n_observations, n_assets), optional Optional cross-sectional group labels used by the concrete transformer. Returns ------- X_transformed : ndarray of shape (n_observations, n_assets) Transformed values. """ pass
[docs] def fit_transform( self, X: ArrayLike, y=None, cs_weights: ArrayLike | None = None, cs_groups: ArrayLike | None = None, ) -> FloatArray: """Fit to `X` and return the transformed values. Parameters ---------- X : array-like of shape (n_observations, n_assets) Input matrix where each row is an observation and each column is an asset. y : Ignored Not used, present for API consistency by convention. cs_weights : array-like of shape (n_observations, n_assets), optional Optional cross-sectional weights forwarded to `transform`. cs_groups : array-like of shape (n_observations, n_assets), optional Optional cross-sectional group labels forwarded to `transform`. Returns ------- X_new : ndarray of shape (n_observations, n_assets) Transformed array. """ return self.fit( X, cs_weights=cs_weights, cs_groups=cs_groups, ).transform( X, cs_weights=cs_weights, cs_groups=cs_groups, )
def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.allow_nan = True tags.requires_fit = False return tags