"""
Normalization methods for spectroscopic data.
"""
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.preprocessing import StandardScaler
from sklearn.utils.validation import check_is_fitted
[docs]
class Normalization(BaseEstimator, TransformerMixin):
"""
General normalization transformer that supports different methods.
Parameters
----------
method : str, default='minmax'
Normalization method:
- 'minmax': Scales each spectrum to a range specified by feature_range
- 'zscore': Standardizes each spectrum (mean=0, std=1)
feature_range : tuple (min, max), default=(0, 1)
Range to scale the spectra to when method='minmax'.
Attributes
----------
min_ : ndarray of shape (n_samples, 1)
Minimum values of each sample (for minmax scaling).
max_ : ndarray of shape (n_samples, 1)
Maximum values of each sample (for minmax scaling).
mean_ : ndarray of shape (n_samples, 1)
Mean values of each sample (for zscore scaling).
std_ : ndarray of shape (n_samples, 1)
Standard deviation of each sample (for zscore scaling).
is_fitted_ : bool
Flag indicating if the transformer has been fitted.
"""
def __init__(self, method="minmax", feature_range=(0, 1)):
self.method = method
self.feature_range = feature_range
[docs]
def fit(self, X, y=None):
"""
Fit the normalization parameters.
Parameters
----------
X : array-like of shape (n_samples, n_features)
The input spectra.
y : None
Ignored.
Returns
-------
self : object
Returns self.
"""
if self.method == "minmax":
self.min_ = np.min(X, axis=1, keepdims=True)
self.max_ = np.max(X, axis=1, keepdims=True)
elif self.method == "zscore":
self.mean_ = np.mean(X, axis=1, keepdims=True)
self.std_ = np.std(X, axis=1, keepdims=True)
else:
raise ValueError("Unsupported normalization method. Choose 'minmax' or 'zscore'.")
self.is_fitted_ = True
return self
[docs]
class Autoscaling(BaseEstimator, TransformerMixin):
"""
Autoscaling (column-wise standardization).
Centers and scales data to unit variance along columns (features).
Attributes
----------
mean_ : ndarray of shape (n_features,)
Mean value for each feature.
std_ : ndarray of shape (n_features,)
Standard deviation for each feature.
"""
def __init__(self):
self.mean_ = None
self.std_ = None
[docs]
def fit(self, X, y=None):
"""
Compute the mean and standard deviation of each feature.
Parameters
----------
X : array-like of shape (n_samples, n_features)
The input spectra.
y : None
Ignored.
Returns
-------
self : object
Returns self.
"""
self.mean_ = np.mean(X, axis=0)
self.std_ = np.std(X, axis=0)
return self
[docs]
class MeanCentering(BaseEstimator, TransformerMixin):
"""
Mean Centering transformation.
Centers data by subtracting the column means, without scaling.
Attributes
----------
mean_ : ndarray of shape (n_features,)
Mean value for each feature.
"""
def __init__(self):
self.mean_ = None
[docs]
def fit(self, X, y=None):
"""
Compute the mean of each feature.
Parameters
----------
X : array-like of shape (n_samples, n_features)
The input spectra.
y : None
Ignored.
Returns
-------
self : object
Returns self.
"""
self.mean_ = np.mean(X, axis=0)
return self
[docs]
class GlobalScaler(BaseEstimator, TransformerMixin):
"""
Applies global scaling to spectra by a constant factor with optional mean centering and standardization.
Parameters
----------
factor : float, default=1.0
Scaling factor to multiply spectra.
mean : bool, default=False
Whether to subtract the mean of each feature.
std : bool, default=False
Whether to divide by the standard deviation of each feature.
"""
def __init__(self, factor=1.0, mean=False, std=False):
self.factor = factor
self.mean = mean
self.std = std
self.mean_value = None
self.std_value = None
[docs]
def fit(self, X, y=None):
"""
Compute mean and standard deviation if needed.
Parameters
----------
X : array-like of shape (n_samples, n_features)
The training input samples.
y : None
Ignored.
Returns
-------
self : object
Returns self.
"""
if self.mean:
self.mean_value = np.mean(X, axis=0)
if self.std:
self.std_value = np.std(X, axis=0)
return self
[docs]
class RowStandardizer(BaseEstimator, TransformerMixin):
"""Standardizes each row independently (i.e. across columns)."""
[docs]
def fit(self, X, y=None):
# No fitting needed since each row is standardized independently
return self
[docs]
class ColumnStandardizer(BaseEstimator, TransformerMixin):
"""Standardizes columns using StandardScaler fitted on the training set."""
def __init__(self):
self.scaler = StandardScaler()
[docs]
def fit(self, X, y=None):
self.scaler.fit(X)
return self
# autoscaling is same as standardscaler or column-wise standardization
# SNV is same as row standardization