|
11 | 11 | import pandas as pd |
12 | 12 | from scipy.stats import median_abs_deviation |
13 | 13 | from sklearn.base import BaseEstimator, TransformerMixin |
14 | | -from sklearn.preprocessing import StandardScaler |
| 14 | +from sklearn.preprocessing import QuantileTransformer, StandardScaler |
15 | 15 |
|
16 | 16 | Spherize_type = TypeVar("Spherize_type", bound="Spherize") |
17 | 17 | RobustMAD_type = TypeVar("RobustMAD_type", bound="RobustMAD") |
@@ -338,3 +338,92 @@ def transform(self, X: pd.DataFrame, copy: Optional[bool] = None) -> pd.DataFram |
338 | 338 | RobustMAD transformed dataframe |
339 | 339 | """ |
340 | 340 | return (X - self.median) / (self.mad + self.epsilon) |
| 341 | + |
| 342 | + |
| 343 | +class InverseNormalTransform(BaseEstimator, TransformerMixin): |
| 344 | + """Inverse normal transform. |
| 345 | +
|
| 346 | + Apply a rank-based quantile transformation to each feature independently |
| 347 | + and map the resulting values to a normal distribution. |
| 348 | +
|
| 349 | + 1) Rank the values of each feature independently. |
| 350 | + 2) Map the ranks to quantiles of a normal distribution. |
| 351 | + 3) Return the transformed values. |
| 352 | +
|
| 353 | + This class wraps sklearn.preprocessing.QuantileTransformer with |
| 354 | + output_distribution="normal". |
| 355 | +
|
| 356 | + Parameters |
| 357 | + ---------- |
| 358 | + n_quantiles : int, default=1000 |
| 359 | + Number of quantiles to be computed. It corresponds to the number of landmarks |
| 360 | + used to discretize the cumulative distribution function. If ``n_quantiles`` is |
| 361 | + larger than the number of samples, it is set to the number of samples because |
| 362 | + a larger number of quantiles does not improve the cumulative distribution |
| 363 | + function estimate. The actual number used after fitting is available as |
| 364 | + ``n_quantiles_``. See sklearn.preprocessing.QuantileTransformer for more details. |
| 365 | + random_state : int, RandomState instance or None, default=None |
| 366 | + Determines random number generation for smoothing noise. Pass an int for |
| 367 | + reproducible results across multiple calls. |
| 368 | +
|
| 369 | + Notes |
| 370 | + ----- |
| 371 | + This transform is rank-based: values are first converted to quantile ranks, |
| 372 | + then mapped to a normal distribution. The transformed values are therefore |
| 373 | + normal scores, not the original raw measurements, and distances between raw |
| 374 | + values are not preserved. |
| 375 | + """ |
| 376 | + |
| 377 | + def __init__( |
| 378 | + self, |
| 379 | + n_quantiles=1000, |
| 380 | + random_state=None, |
| 381 | + ): |
| 382 | + self.n_quantiles = n_quantiles |
| 383 | + self.random_state = random_state |
| 384 | + |
| 385 | + def fit(self, x, y=None): |
| 386 | + """Fit inverse normal transform. |
| 387 | +
|
| 388 | + Parameters |
| 389 | + ---------- |
| 390 | + x : pandas.DataFrame or numpy.ndarray |
| 391 | + Data to fit. |
| 392 | + y : None |
| 393 | + Has no effect; only used for consistency in sklearn transform API |
| 394 | +
|
| 395 | + Returns |
| 396 | + ------- |
| 397 | + self |
| 398 | + Fitted inverse normal transform. |
| 399 | + """ |
| 400 | + # Set number of quantiles, if n_quantiles is greater than the number of samples\ |
| 401 | + # set it to the number of samples. |
| 402 | + self.n_quantiles_ = min(self.n_quantiles, x.shape[0]) |
| 403 | + |
| 404 | + # Initialize transformer and set output distribution to normal. |
| 405 | + # We set it to normal because we want to map the ranks to a normal distribution. |
| 406 | + self.transformer_ = QuantileTransformer( |
| 407 | + n_quantiles=self.n_quantiles_, |
| 408 | + output_distribution="normal", |
| 409 | + random_state=self.random_state, |
| 410 | + ) |
| 411 | + |
| 412 | + self.transformer_.fit(x) |
| 413 | + |
| 414 | + return self |
| 415 | + |
| 416 | + def transform(self, x): |
| 417 | + """Apply inverse normal transform. |
| 418 | +
|
| 419 | + Parameters |
| 420 | + ---------- |
| 421 | + x : pandas.DataFrame or numpy.ndarray |
| 422 | + Data to transform. |
| 423 | +
|
| 424 | + Returns |
| 425 | + ------- |
| 426 | + numpy.ndarray |
| 427 | + Transformed data. |
| 428 | + """ |
| 429 | + return self.transformer_.transform(x) |
0 commit comments