Skip to content

Commit

Permalink
Merge pull request #548 from GAA-UAM/feature/pls
Browse files Browse the repository at this point in the history
Functional PLS for dimensionality reduction and regression
  • Loading branch information
vnmabus committed Nov 7, 2023
2 parents 4207c4d + e0c4ab5 commit 4a368bd
Show file tree
Hide file tree
Showing 11 changed files with 1,636 additions and 5 deletions.
15 changes: 14 additions & 1 deletion docs/modules/ml/regression.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,17 @@ regression is fitted using the coefficients of the functions in said basis.
.. autosummary::
:toctree: autosummary

skfda.ml.regression.FPCARegression
skfda.ml.regression.FPCARegression

FPLS regression
-----------------
This module includes the implementation of FPLS (Functional Partial Least Squares)
regression. This implementation accepts either functional or multivariate data as the regressor and the response.
FPLS regression consists on performing the FPLS dimensionality reduction algorithm
but using a regression deflation strategy.


.. autosummary::
:toctree: autosummary

skfda.ml.regression.FPLSRegression
7 changes: 5 additions & 2 deletions docs/modules/preprocessing/dim_reduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ Other dimensionality reduction methods construct new features from
existing ones. For example, in functional principal component
analysis, we project the data samples into a smaller sample of
functions that preserve most of the original
variance.
variance. Similarly, in functional partial least squares, we project
the data samples into a smaller sample of functions that preserve most
of the covariance between the two data blocks.

.. autosummary::
:toctree: autosummary

skfda.preprocessing.dim_reduction.FPCA
skfda.preprocessing.dim_reduction.FPCA
skfda.preprocessing.dim_reduction.FPLS
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ ignore =
WPS507,
# Comparison with not is not the same as with equality
WPS520,
# Found bad magic module function: {0}
WPS413

per-file-ignores =
__init__.py:
Expand Down
2 changes: 2 additions & 0 deletions skfda/ml/regression/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"_kernel_regression": ["KernelRegression"],
"_linear_regression": ["LinearRegression"],
"_fpca_regression": ["FPCARegression"],
"_fpls_regression": ["FPLSRegression"],
"_neighbors_regression": [
"KNeighborsRegressor",
"RadiusNeighborsRegressor",
Expand All @@ -19,6 +20,7 @@

if TYPE_CHECKING:
from ._fpca_regression import FPCARegression
from ._fpls_regression import FPLSRegression as FPLSRegression
from ._historical_linear_model import (
HistoricalLinearRegression as HistoricalLinearRegression,
)
Expand Down
118 changes: 118 additions & 0 deletions skfda/ml/regression/_fpls_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from __future__ import annotations

from typing import Any, TypeVar, Union

from sklearn.utils.validation import check_is_fitted

from ..._utils._sklearn_adapter import BaseEstimator, RegressorMixin
from ...misc.regularization import L2Regularization
from ...preprocessing.dim_reduction import FPLS
from ...representation import FDataGrid
from ...representation.basis import Basis, FDataBasis
from ...typing._numpy import NDArrayFloat

InputType = TypeVar(
"InputType",
bound=Union[FDataGrid, FDataBasis, NDArrayFloat],
)

OutputType = TypeVar(
"OutputType",
bound=Union[FDataGrid, FDataBasis, NDArrayFloat],
)


class FPLSRegression(
BaseEstimator,
RegressorMixin[InputType, OutputType],
):
r"""
Regression using Functional Partial Least Squares.
Parameters:
n_components: Number of components to keep.
By default all available components are utilized.
regularization_X: Regularization for the calculation of the X weights.
weight_basis_X: Basis to use for the X block. Only
applicable if X is a FDataBasis. Otherwise it must be None.
weight_basis_Y: Basis to use for the Y block. Only
applicable if Y is a FDataBasis. Otherwise it must be None.
Attributes:
coef\_: Coefficients of the linear model.
fpls\_: FPLS object used to fit the model.
Examples:
Fit a FPLS regression model with two components.
>>> from skfda.ml.regression import FPLSRegression
>>> from skfda.datasets import fetch_tecator
>>> from skfda.representation import FDataGrid
>>> from skfda.typing._numpy import NDArrayFloat
>>> X, y = fetch_tecator(return_X_y=True)
>>> fpls = FPLSRegression[FDataGrid, NDArrayFloat](n_components=2)
>>> fpls = fpls.fit(X, y)
"""

def __init__(
self,
n_components: int | None = None,
regularization_X: L2Regularization[Any] | None = None,
weight_basis_X: Basis | None = None,
weight_basis_Y: Basis | None = None,
_integration_weights_X: NDArrayFloat | None = None,
_integration_weights_Y: NDArrayFloat | None = None,
) -> None:
self.n_components = n_components
self._integration_weights_X = _integration_weights_X
self._integration_weights_Y = _integration_weights_Y
self.regularization_X = regularization_X
self.weight_basis_X = weight_basis_X
self.weight_basis_Y = weight_basis_Y

def fit(
self,
X: InputType,
y: OutputType,
) -> FPLSRegression[InputType, OutputType]:
"""
Fit the model using the data for both blocks.
Args:
X: Data of the X block
y: Data of the Y block
Returns:
self
"""
self.fpls_ = FPLS[InputType, OutputType](
n_components=self.n_components,
regularization_X=self.regularization_X,
component_basis_X=self.weight_basis_X,
component_basis_Y=self.weight_basis_Y,
_integration_weights_X=self._integration_weights_X,
_integration_weights_Y=self._integration_weights_Y,
_deflation_mode="reg",
)

self.fpls_.fit(X, y)

self.coef_ = (
self.fpls_.x_rotations_matrix_
@ self.fpls_.y_loadings_matrix_.T
)
return self

def predict(self, X: InputType) -> OutputType:
"""Predict using the model.
Args:
X: Data to predict.
Returns:
Predicted values.
"""
check_is_fitted(self)
return self.fpls_.inverse_transform_y(self.fpls_.transform_x(X))
8 changes: 6 additions & 2 deletions skfda/preprocessing/dim_reduction/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
],
submod_attrs={
"_fpca": ["FPCA"],
"_neighbor_transforms": ["KNeighborsTransformer"]
"_fpls": ["FPLS"],
"_neighbor_transforms": ["KNeighborsTransformer"],
},
)

if TYPE_CHECKING:
from ._fpca import FPCA as FPCA
from ._neighbor_transforms import KNeighborsTransformer as KNeighborsTransformer
from ._fpls import FPLS as FPLS
from ._neighbor_transforms import (
KNeighborsTransformer as KNeighborsTransformer,
)


def __getattr__(name: str) -> Any:
Expand Down

0 comments on commit 4a368bd

Please sign in to comment.