Aluode/PerceptionLabPortable
0
1# Authors: The scikit-learn developers
2# SPDX-License-Identifier: BSD-3-Clause
3
4
5import warnings
6from numbers import Integral, Real
7
8import numpy as np
9from scipy import sparse, stats
10from scipy.special import boxcox, inv_boxcox
11
12from sklearn.utils import metadata_routing
13
14from ..base import (
15 BaseEstimator,
16 ClassNamePrefixFeaturesOutMixin,
17 OneToOneFeatureMixin,
18 TransformerMixin,
19 _fit_context,
20)
21from ..utils import _array_api, check_array, resample
22from ..utils._array_api import (
23 _find_matching_floating_dtype,
24 _modify_in_place_if_numpy,
25 device,
26 get_namespace,
27 get_namespace_and_device,
28)
29from ..utils._param_validation import Interval, Options, StrOptions, validate_params
30from ..utils.extmath import _incremental_mean_and_var, row_norms
31from ..utils.fixes import _yeojohnson_lambda
32from ..utils.sparsefuncs import (
33 incr_mean_variance_axis,
34 inplace_column_scale,
35 mean_variance_axis,
36 min_max_axis,
37)
38from ..utils.sparsefuncs_fast import (
39 inplace_csr_row_normalize_l1,
40 inplace_csr_row_normalize_l2,
41)
42from ..utils.validation import (
43 FLOAT_DTYPES,
44 _check_sample_weight,
45 check_is_fitted,
46 check_random_state,
47 validate_data,
48)
49from ._encoders import OneHotEncoder
50
51BOUNDS_THRESHOLD = 1e-7
52
53__all__ = [
54 "Binarizer",
55 "KernelCenterer",
56 "MaxAbsScaler",
57 "MinMaxScaler",
58 "Normalizer",
59 "OneHotEncoder",
60 "PowerTransformer",
61 "QuantileTransformer",
62 "RobustScaler",
63 "StandardScaler",
64 "add_dummy_feature",
65 "binarize",
66 "maxabs_scale",
67 "minmax_scale",
68 "normalize",
69 "power_transform",
70 "quantile_transform",
71 "robust_scale",
72 "scale",
73]
74
75
76def _is_constant_feature(var, mean, n_samples):
77 """Detect if a feature is indistinguishable from a constant feature.
78
79 The detection is based on its computed variance and on the theoretical
80 error bounds of the '2 pass algorithm' for variance computation.
81
82 See "Algorithms for computing the sample variance: analysis and
83 recommendations", by Chan, Golub, and LeVeque.
84 """
85 # In scikit-learn, variance is always computed using float64 accumulators.
86 eps = np.finfo(np.float64).eps
87
88 upper_bound = n_samples * eps * var + (n_samples * mean * eps) ** 2
89 return var <= upper_bound
90
91
92def _handle_zeros_in_scale(scale, copy=True, constant_mask=None):
93 """Set scales of near constant features to 1.
94
95 The goal is to avoid division by very small or zero values.
96
97 Near constant features are detected automatically by identifying
98 scales close to machine precision unless they are precomputed by
99 the caller and passed with the `constant_mask` kwarg.
100
101 Typically for standard scaling, the scales are the standard
102 deviation while near constant features are better detected on the
103 computed variances which are closer to machine precision by
104 construction.
105 """
106 # if we are fitting on 1D arrays, scale might be a scalar
107 if np.isscalar(scale):
108 if scale == 0.0:
109 scale = 1.0
110 return scale
111 # scale is an array
112 else:
113 xp, _ = get_namespace(scale)
114 if constant_mask is None:
115 # Detect near constant values to avoid dividing by a very small
116 # value that could lead to surprising results and numerical
117 # stability issues.
118 constant_mask = scale < 10 * xp.finfo(scale.dtype).eps
119
120 if copy:
121 # New array to avoid side-effects
122 scale = xp.asarray(scale, copy=True)
123 scale[constant_mask] = 1.0
124 return scale
125
126
127@validate_params(
128 {
129 "X": ["array-like", "sparse matrix"],
130 "axis": [Options(Integral, {0, 1})],
131 "with_mean": ["boolean"],
132 "with_std": ["boolean"],
133 "copy": ["boolean"],
134 },
135 prefer_skip_nested_validation=True,
136)
137def scale(X, *, axis=0, with_mean=True, with_std=True, copy=True):
138 """Standardize a dataset along any axis.
139
140 Center to the mean and component wise scale to unit variance.
141
142 Read more in the :ref:`User Guide <preprocessing_scaler>`.
143
144 Parameters
145 ----------
146 X : {array-like, sparse matrix} of shape (n_samples, n_features)
147 The data to center and scale.
148
149 axis : {0, 1}, default=0
150 Axis used to compute the means and standard deviations along. If 0,
151 independently standardize each feature, otherwise (if 1) standardize
152 each sample.
153
154 with_mean : bool, default=True
155 If True, center the data before scaling.
156
157 with_std : bool, default=True
158 If True, scale the data to unit variance (or equivalently,
159 unit standard deviation).
160
161 copy : bool, default=True
162 If False, try to avoid a copy and scale in place.
163 This is not guaranteed to always work in place; e.g. if the data is
164 a numpy array with an int dtype, a copy will be returned even with
165 copy=False.
166
167 Returns
168 -------
169 X_tr : {ndarray, sparse matrix} of shape (n_samples, n_features)
170 The transformed data.
171
172 See Also
173 --------
174 StandardScaler : Performs scaling to unit variance using the Transformer
175 API (e.g. as part of a preprocessing
176 :class:`~sklearn.pipeline.Pipeline`).
177
178 Notes
179 -----
180 This implementation will refuse to center scipy.sparse matrices
181 since it would make them non-sparse and would potentially crash the
182 program with memory exhaustion problems.
183
184 Instead the caller is expected to either set explicitly
185 `with_mean=False` (in that case, only variance scaling will be
186 performed on the features of the CSC matrix) or to call `X.toarray()`
187 if he/she expects the materialized dense array to fit in memory.
188
189 To avoid memory copy the caller should pass a CSC matrix.
190
191 NaNs are treated as missing values: disregarded to compute the statistics,
192 and maintained during the data transformation.
193
194 We use a biased estimator for the standard deviation, equivalent to
195 `numpy.std(x, ddof=0)`. Note that the choice of `ddof` is unlikely to
196 affect model performance.
197
198 For a comparison of the different scalers, transformers, and normalizers,
199 see: :ref:`sphx_glr_auto_examples_preprocessing_plot_all_scaling.py`.
200
201 .. warning:: Risk of data leak
202
203 Do not use :func:`~sklearn.preprocessing.scale` unless you know
204 what you are doing. A common mistake is to apply it to the entire data
205 *before* splitting into training and test sets. This will bias the
206 model evaluation because information would have leaked from the test
207 set to the training set.
208 In general, we recommend using
209 :class:`~sklearn.preprocessing.StandardScaler` within a
210 :ref:`Pipeline <pipeline>` in order to prevent most risks of data
211 leaking: `pipe = make_pipeline(StandardScaler(), LogisticRegression())`.
212
213 Examples
214 --------
215 >>> from sklearn.preprocessing import scale
216 >>> X = [[-2, 1, 2], [-1, 0, 1]]
217 >>> scale(X, axis=0) # scaling each column independently
218 array([[-1., 1., 1.],
219 [ 1., -1., -1.]])
220 >>> scale(X, axis=1) # scaling each row independently
221 array([[-1.37, 0.39, 0.98],
222 [-1.22, 0. , 1.22]])
223 """
224 X = check_array(
225 X,
226 accept_sparse="csc",
227 copy=copy,
228 ensure_2d=False,
229 estimator="the scale function",
230 dtype=FLOAT_DTYPES,
231 ensure_all_finite="allow-nan",
232 )
233 if sparse.issparse(X):
234 if with_mean:
235 raise ValueError(
236 "Cannot center sparse matrices: pass `with_mean=False` instead"
237 " See docstring for motivation and alternatives."
238 )
239 if axis != 0:
240 raise ValueError(
241 "Can only scale sparse matrix on axis=0, got axis=%d" % axis
242 )
243 if with_std:
244 _, var = mean_variance_axis(X, axis=0)
245 var = _handle_zeros_in_scale(var, copy=False)
246 inplace_column_scale(X, 1 / np.sqrt(var))
247 else:
248 X = np.asarray(X)
249 if with_mean:
250 mean_ = np.nanmean(X, axis)
251 if with_std:
252 scale_ = np.nanstd(X, axis)
253 # Xr is a view on the original array that enables easy use of
254 # broadcasting on the axis in which we are interested in
255 Xr = np.rollaxis(X, axis)
256 if with_mean:
257 Xr -= mean_
258 mean_1 = np.nanmean(Xr, axis=0)
259 # Verify that mean_1 is 'close to zero'. If X contains very
260 # large values, mean_1 can also be very large, due to a lack of
261 # precision of mean_. In this case, a pre-scaling of the
262 # concerned feature is efficient, for instance by its mean or
263 # maximum.
264 if not np.allclose(mean_1, 0):
265 warnings.warn(
266 "Numerical issues were encountered "
267 "when centering the data "
268 "and might not be solved. Dataset may "
269 "contain too large values. You may need "
270 "to prescale your features."
271 )
272 Xr -= mean_1
273 if with_std:
274 scale_ = _handle_zeros_in_scale(scale_, copy=False)
275 Xr /= scale_
276 if with_mean:
277 mean_2 = np.nanmean(Xr, axis=0)
278 # If mean_2 is not 'close to zero', it comes from the fact that
279 # scale_ is very small so that mean_2 = mean_1/scale_ > 0, even
280 # if mean_1 was close to zero. The problem is thus essentially
281 # due to the lack of precision of mean_. A solution is then to
282 # subtract the mean again:
283 if not np.allclose(mean_2, 0):
284 warnings.warn(
285 "Numerical issues were encountered "
286 "when scaling the data "
287 "and might not be solved. The standard "
288 "deviation of the data is probably "
289 "very close to 0. "
290 )
291 Xr -= mean_2
292 return X
293
294
295class MinMaxScaler(OneToOneFeatureMixin, TransformerMixin, BaseEstimator):
296 """Transform features by scaling each feature to a given range.
297
298 This estimator scales and translates each feature individually such
299 that it is in the given range on the training set, e.g. between
300 zero and one.
301
302 The transformation is given by::
303
304 X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
305 X_scaled = X_std * (max - min) + min
306
307 where min, max = feature_range.
308
309 This transformation is often used as an alternative to zero mean,
310 unit variance scaling.
311
312 `MinMaxScaler` doesn't reduce the effect of outliers, but it linearly
313 scales them down into a fixed range, where the largest occurring data point
314 corresponds to the maximum value and the smallest one corresponds to the
315 minimum value. For an example visualization, refer to :ref:`Compare
316 MinMaxScaler with other scalers <plot_all_scaling_minmax_scaler_section>`.
317
318 Read more in the :ref:`User Guide <preprocessing_scaler>`.
319
320 Parameters
321 ----------
322 feature_range : tuple (min, max), default=(0, 1)
323 Desired range of transformed data.
324
325 copy : bool, default=True
326 Set to False to perform inplace row normalization and avoid a
327 copy (if the input is already a numpy array).
328
329 clip : bool, default=False
330 Set to True to clip transformed values of held-out data to
331 provided `feature range`.
332
333 .. versionadded:: 0.24
334
335 Attributes
336 ----------
337 min_ : ndarray of shape (n_features,)
338 Per feature adjustment for minimum. Equivalent to
339 ``min - X.min(axis=0) * self.scale_``
340
341 scale_ : ndarray of shape (n_features,)
342 Per feature relative scaling of the data. Equivalent to
343 ``(max - min) / (X.max(axis=0) - X.min(axis=0))``
344
345 .. versionadded:: 0.17
346 *scale_* attribute.
347
348 data_min_ : ndarray of shape (n_features,)
349 Per feature minimum seen in the data
350
351 .. versionadded:: 0.17
352 *data_min_*
353
354 data_max_ : ndarray of shape (n_features,)
355 Per feature maximum seen in the data
356
357 .. versionadded:: 0.17
358 *data_max_*
359
360 data_range_ : ndarray of shape (n_features,)
361 Per feature range ``(data_max_ - data_min_)`` seen in the data
362
363 .. versionadded:: 0.17
364 *data_range_*
365
366 n_features_in_ : int
367 Number of features seen during :term:`fit`.
368
369 .. versionadded:: 0.24
370
371 n_samples_seen_ : int
372 The number of samples processed by the estimator.
373 It will be reset on new calls to fit, but increments across
374 ``partial_fit`` calls.
375
376 feature_names_in_ : ndarray of shape (`n_features_in_`,)
377 Names of features seen during :term:`fit`. Defined only when `X`
378 has feature names that are all strings.
379
380 .. versionadded:: 1.0
381
382 See Also
383 --------
384 minmax_scale : Equivalent function without the estimator API.
385
386 Notes
387 -----
388 NaNs are treated as missing values: disregarded in fit, and maintained in
389 transform.
390
391 Examples
392 --------
393 >>> from sklearn.preprocessing import MinMaxScaler
394 >>> data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
395 >>> scaler = MinMaxScaler()
396 >>> print(scaler.fit(data))
397 MinMaxScaler()
398 >>> print(scaler.data_max_)
399 [ 1. 18.]
400 >>> print(scaler.transform(data))
401 [[0. 0. ]
402 [0.25 0.25]
403 [0.5 0.5 ]
404 [1. 1. ]]
405 >>> print(scaler.transform([[2, 2]]))
406 [[1.5 0. ]]
407 """
408
409 _parameter_constraints: dict = {
410 "feature_range": [tuple],
411 "copy": ["boolean"],
412 "clip": ["boolean"],
413 }
414
415 def __init__(self, feature_range=(0, 1), *, copy=True, clip=False):
416 self.feature_range = feature_range
417 self.copy = copy
418 self.clip = clip
419
420 def _reset(self):
421 """Reset internal data-dependent state of the scaler, if necessary.
422
423 __init__ parameters are not touched.
424 """
425 # Checking one attribute is enough, because they are all set together
426 # in partial_fit
427 if hasattr(self, "scale_"):
428 del self.scale_
429 del self.min_
430 del self.n_samples_seen_
431 del self.data_min_
432 del self.data_max_
433 del self.data_range_
434
435 def fit(self, X, y=None):
436 """Compute the minimum and maximum to be used for later scaling.
437
438 Parameters
439 ----------
440 X : array-like of shape (n_samples, n_features)
441 The data used to compute the per-feature minimum and maximum
442 used for later scaling along the features axis.
443
444 y : None
445 Ignored.
446
447 Returns
448 -------
449 self : object
450 Fitted scaler.
451 """
452 # Reset internal state before fitting
453 self._reset()
454 return self.partial_fit(X, y)
455
456 @_fit_context(prefer_skip_nested_validation=True)
457 def partial_fit(self, X, y=None):
458 """Online computation of min and max on X for later scaling.
459
460 All of X is processed as a single batch. This is intended for cases
461 when :meth:`fit` is not feasible due to very large number of
462 `n_samples` or because X is read from a continuous stream.
463
464 Parameters
465 ----------
466 X : array-like of shape (n_samples, n_features)
467 The data used to compute the mean and standard deviation
468 used for later scaling along the features axis.
469
470 y : None
471 Ignored.
472
473 Returns
474 -------
475 self : object
476 Fitted scaler.
477 """
478 feature_range = self.feature_range
479 if feature_range[0] >= feature_range[1]:
480 raise ValueError(
481 "Minimum of desired feature range must be smaller than maximum. Got %s."
482 % str(feature_range)
483 )
484
485 if sparse.issparse(X):
486 raise TypeError(
487 "MinMaxScaler does not support sparse input. "
488 "Consider using MaxAbsScaler instead."
489 )
490
491 xp, _ = get_namespace(X)
492
493 first_pass = not hasattr(self, "n_samples_seen_")
494 X = validate_data(
495 self,
496 X,
497 reset=first_pass,
498 dtype=_array_api.supported_float_dtypes(xp),
499 ensure_all_finite="allow-nan",
500 )
501
502 device_ = device(X)
503 feature_range = (
504 xp.asarray(feature_range[0], dtype=X.dtype, device=device_),
505 xp.asarray(feature_range[1], dtype=X.dtype, device=device_),
506 )
507
508 data_min = _array_api._nanmin(X, axis=0, xp=xp)
509 data_max = _array_api._nanmax(X, axis=0, xp=xp)
510
511 if first_pass:
512 self.n_samples_seen_ = X.shape[0]
513 else:
514 data_min = xp.minimum(self.data_min_, data_min)
515 data_max = xp.maximum(self.data_max_, data_max)
516 self.n_samples_seen_ += X.shape[0]
517
518 data_range = data_max - data_min
519 self.scale_ = (feature_range[1] - feature_range[0]) / _handle_zeros_in_scale(
520 data_range, copy=True
521 )
522 self.min_ = feature_range[0] - data_min * self.scale_
523 self.data_min_ = data_min
524 self.data_max_ = data_max
525 self.data_range_ = data_range
526 return self
527
528 def transform(self, X):
529 """Scale features of X according to feature_range.
530
531 Parameters
532 ----------
533 X : array-like of shape (n_samples, n_features)
534 Input data that will be transformed.
535
536 Returns
537 -------
538 Xt : ndarray of shape (n_samples, n_features)
539 Transformed data.
540 """
541 check_is_fitted(self)
542
543 xp, _ = get_namespace(X)
544
545 X = validate_data(
546 self,
547 X,
548 copy=self.copy,
549 dtype=_array_api.supported_float_dtypes(xp),
550 force_writeable=True,
551 ensure_all_finite="allow-nan",
552 reset=False,
553 )
554
555 X *= self.scale_
556 X += self.min_
557 if self.clip:
558 device_ = device(X)
559 X = _modify_in_place_if_numpy(
560 xp,
561 xp.clip,
562 X,
563 xp.asarray(self.feature_range[0], dtype=X.dtype, device=device_),
564 xp.asarray(self.feature_range[1], dtype=X.dtype, device=device_),
565 out=X,
566 )
567 return X
568
569 def inverse_transform(self, X):
570 """Undo the scaling of X according to feature_range.
571
572 Parameters
573 ----------
574 X : array-like of shape (n_samples, n_features)
575 Input data that will be transformed. It cannot be sparse.
576
577 Returns
578 -------
579 X_original : ndarray of shape (n_samples, n_features)
580 Transformed data.
581 """
582 check_is_fitted(self)
583
584 xp, _ = get_namespace(X)
585
586 X = check_array(
587 X,
588 copy=self.copy,
589 dtype=_array_api.supported_float_dtypes(xp),
590 force_writeable=True,
591 ensure_all_finite="allow-nan",
592 )
593
594 X -= self.min_
595 X /= self.scale_
596 return X
597
598 def __sklearn_tags__(self):
599 tags = super().__sklearn_tags__()
600 tags.input_tags.allow_nan = True
601 tags.array_api_support = True
602 return tags
603
604
605@validate_params(
606 {
607 "X": ["array-like"],
608 "axis": [Options(Integral, {0, 1})],
609 },
610 prefer_skip_nested_validation=False,
611)
612def minmax_scale(X, feature_range=(0, 1), *, axis=0, copy=True):
613 """Transform features by scaling each feature to a given range.
614
615 This estimator scales and translates each feature individually such
616 that it is in the given range on the training set, i.e. between
617 zero and one.
618
619 The transformation is given by (when ``axis=0``)::
620
621 X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
622 X_scaled = X_std * (max - min) + min
623
624 where min, max = feature_range.
625
626 The transformation is calculated as (when ``axis=0``)::
627
628 X_scaled = scale * X + min - X.min(axis=0) * scale
629 where scale = (max - min) / (X.max(axis=0) - X.min(axis=0))
630
631 This transformation is often used as an alternative to zero mean,
632 unit variance scaling.
633
634 Read more in the :ref:`User Guide <preprocessing_scaler>`.
635
636 .. versionadded:: 0.17
637 *minmax_scale* function interface
638 to :class:`~sklearn.preprocessing.MinMaxScaler`.
639
640 Parameters
641 ----------
642 X : array-like of shape (n_samples, n_features)
643 The data.
644
645 feature_range : tuple (min, max), default=(0, 1)
646 Desired range of transformed data.
647
648 axis : {0, 1}, default=0
649 Axis used to scale along. If 0, independently scale each feature,
650 otherwise (if 1) scale each sample.
651
652 copy : bool, default=True
653 If False, try to avoid a copy and scale in place.
654 This is not guaranteed to always work in place; e.g. if the data is
655 a numpy array with an int dtype, a copy will be returned even with
656 copy=False.
657
658 Returns
659 -------
660 X_tr : ndarray of shape (n_samples, n_features)
661 The transformed data.
662
663 .. warning:: Risk of data leak
664
665 Do not use :func:`~sklearn.preprocessing.minmax_scale` unless you know
666 what you are doing. A common mistake is to apply it to the entire data
667 *before* splitting into training and test sets. This will bias the
668 model evaluation because information would have leaked from the test
669 set to the training set.
670 In general, we recommend using
671 :class:`~sklearn.preprocessing.MinMaxScaler` within a
672 :ref:`Pipeline <pipeline>` in order to prevent most risks of data
673 leaking: `pipe = make_pipeline(MinMaxScaler(), LogisticRegression())`.
674
675 See Also
676 --------
677 MinMaxScaler : Performs scaling to a given range using the Transformer
678 API (e.g. as part of a preprocessing
679 :class:`~sklearn.pipeline.Pipeline`).
680
681 Notes
682 -----
683 For a comparison of the different scalers, transformers, and normalizers,
684 see: :ref:`sphx_glr_auto_examples_preprocessing_plot_all_scaling.py`.
685
686 Examples
687 --------
688 >>> from sklearn.preprocessing import minmax_scale
689 >>> X = [[-2, 1, 2], [-1, 0, 1]]
690 >>> minmax_scale(X, axis=0) # scale each column independently
691 array([[0., 1., 1.],
692 [1., 0., 0.]])
693 >>> minmax_scale(X, axis=1) # scale each row independently
694 array([[0. , 0.75, 1. ],
695 [0. , 0.5 , 1. ]])
696 """
697 # Unlike the scaler object, this function allows 1d input.
698 # If copy is required, it will be done inside the scaler object.
699 X = check_array(
700 X,
701 copy=False,
702 ensure_2d=False,
703 dtype=FLOAT_DTYPES,
704 ensure_all_finite="allow-nan",
705 )
706 original_ndim = X.ndim
707
708 if original_ndim == 1:
709 X = X.reshape(X.shape[0], 1)
710
711 s = MinMaxScaler(feature_range=feature_range, copy=copy)
712 if axis == 0:
713 X = s.fit_transform(X)
714 else:
715 X = s.fit_transform(X.T).T
716
717 if original_ndim == 1:
718 X = X.ravel()
719
720 return X
721
722
723class StandardScaler(OneToOneFeatureMixin, TransformerMixin, BaseEstimator):
724 """Standardize features by removing the mean and scaling to unit variance.
725
726 The standard score of a sample `x` is calculated as:
727
728 .. code-block:: text
729
730 z = (x - u) / s
731
732 where `u` is the mean of the training samples or zero if `with_mean=False`,
733 and `s` is the standard deviation of the training samples or one if
734 `with_std=False`.
735
736 Centering and scaling happen independently on each feature by computing
737 the relevant statistics on the samples in the training set. Mean and
738 standard deviation are then stored to be used on later data using
739 :meth:`transform`.
740
741 Standardization of a dataset is a common requirement for many
742 machine learning estimators: they might behave badly if the
743 individual features do not more or less look like standard normally
744 distributed data (e.g. Gaussian with 0 mean and unit variance).
745
746 For instance many elements used in the objective function of
747 a learning algorithm (such as the RBF kernel of Support Vector
748 Machines or the L1 and L2 regularizers of linear models) assume that
749 all features are centered around 0 and have variance in the same
750 order. If a feature has a variance that is orders of magnitude larger
751 than others, it might dominate the objective function and make the
752 estimator unable to learn from other features correctly as expected.
753
754 `StandardScaler` is sensitive to outliers, and the features may scale
755 differently from each other in the presence of outliers. For an example
756 visualization, refer to :ref:`Compare StandardScaler with other scalers
757 <plot_all_scaling_standard_scaler_section>`.
758
759 This scaler can also be applied to sparse CSR or CSC matrices by passing
760 `with_mean=False` to avoid breaking the sparsity structure of the data.
761
762 Read more in the :ref:`User Guide <preprocessing_scaler>`.
763
764 Parameters
765 ----------
766 copy : bool, default=True
767 If False, try to avoid a copy and do inplace scaling instead.
768 This is not guaranteed to always work inplace; e.g. if the data is
769 not a NumPy array or scipy.sparse CSR matrix, a copy may still be
770 returned.
771
772 with_mean : bool, default=True
773 If True, center the data before scaling.
774 This does not work (and will raise an exception) when attempted on
775 sparse matrices, because centering them entails building a dense
776 matrix which in common use cases is likely to be too large to fit in
777 memory.
778
779 with_std : bool, default=True
780 If True, scale the data to unit variance (or equivalently,
781 unit standard deviation).
782
783 Attributes
784 ----------
785 scale_ : ndarray of shape (n_features,) or None
786 Per feature relative scaling of the data to achieve zero mean and unit
787 variance. Generally this is calculated using `np.sqrt(var_)`. If a
788 variance is zero, we can't achieve unit variance, and the data is left
789 as-is, giving a scaling factor of 1. `scale_` is equal to `None`
790 when `with_std=False`.
791
792 .. versionadded:: 0.17
793 *scale_*
794
795 mean_ : ndarray of shape (n_features,) or None
796 The mean value for each feature in the training set.
797 Equal to ``None`` when ``with_mean=False`` and ``with_std=False``.
798
799 var_ : ndarray of shape (n_features,) or None
800 The variance for each feature in the training set. Used to compute
801 `scale_`. Equal to ``None`` when ``with_mean=False`` and
802 ``with_std=False``.
803
804 n_features_in_ : int
805 Number of features seen during :term:`fit`.
806
807 .. versionadded:: 0.24
808
809 feature_names_in_ : ndarray of shape (`n_features_in_`,)
810 Names of features seen during :term:`fit`. Defined only when `X`
811 has feature names that are all strings.
812
813 .. versionadded:: 1.0
814
815 n_samples_seen_ : int or ndarray of shape (n_features,)
816 The number of samples processed by the estimator for each feature.
817 If there are no missing samples, the ``n_samples_seen`` will be an
818 integer, otherwise it will be an array of dtype int. If
819 `sample_weights` are used it will be a float (if no missing data)
820 or an array of dtype float that sums the weights seen so far.
821 Will be reset on new calls to fit, but increments across
822 ``partial_fit`` calls.
823
824 See Also
825 --------
826 scale : Equivalent function without the estimator API.
827
828 :class:`~sklearn.decomposition.PCA` : Further removes the linear
829 correlation across features with 'whiten=True'.
830
831 Notes
832 -----
833 NaNs are treated as missing values: disregarded in fit, and maintained in
834 transform.
835
836 We use a biased estimator for the standard deviation, equivalent to
837 `numpy.std(x, ddof=0)`. Note that the choice of `ddof` is unlikely to
838 affect model performance.
839
840 Examples
841 --------
842 >>> from sklearn.preprocessing import StandardScaler
843 >>> data = [[0, 0], [0, 0], [1, 1], [1, 1]]
844 >>> scaler = StandardScaler()
845 >>> print(scaler.fit(data))
846 StandardScaler()
847 >>> print(scaler.mean_)
848 [0.5 0.5]
849 >>> print(scaler.transform(data))
850 [[-1. -1.]
851 [-1. -1.]
852 [ 1. 1.]
853 [ 1. 1.]]
854 >>> print(scaler.transform([[2, 2]]))
855 [[3. 3.]]
856 """
857
858 _parameter_constraints: dict = {
859 "copy": ["boolean"],
860 "with_mean": ["boolean"],
861 "with_std": ["boolean"],
862 }
863
864 def __init__(self, *, copy=True, with_mean=True, with_std=True):
865 self.with_mean = with_mean
866 self.with_std = with_std
867 self.copy = copy
868
869 def _reset(self):
870 """Reset internal data-dependent state of the scaler, if necessary.
871
872 __init__ parameters are not touched.
873 """
874 # Checking one attribute is enough, because they are all set together
875 # in partial_fit
876 if hasattr(self, "scale_"):
877 del self.scale_
878 del self.n_samples_seen_
879 del self.mean_
880 del self.var_
881
882 def fit(self, X, y=None, sample_weight=None):
883 """Compute the mean and std to be used for later scaling.
884
885 Parameters
886 ----------
887 X : {array-like, sparse matrix} of shape (n_samples, n_features)
888 The data used to compute the mean and standard deviation
889 used for later scaling along the features axis.
890
891 y : None
892 Ignored.
893
894 sample_weight : array-like of shape (n_samples,), default=None
895 Individual weights for each sample.
896
897 .. versionadded:: 0.24
898 parameter *sample_weight* support to StandardScaler.
899
900 Returns
901 -------
902 self : object
903 Fitted scaler.
904 """
905 # Reset internal state before fitting
906 self._reset()
907 return self.partial_fit(X, y, sample_weight)
908
909 @_fit_context(prefer_skip_nested_validation=True)
910 def partial_fit(self, X, y=None, sample_weight=None):
911 """Online computation of mean and std on X for later scaling.
912
913 All of X is processed as a single batch. This is intended for cases
914 when :meth:`fit` is not feasible due to very large number of
915 `n_samples` or because X is read from a continuous stream.
916
917 The algorithm for incremental mean and std is given in Equation 1.5a,b
918 in Chan, Tony F., Gene H. Golub, and Randall J. LeVeque. "Algorithms
919 for computing the sample variance: Analysis and recommendations."
920 The American Statistician 37.3 (1983): 242-247:
921
922 Parameters
923 ----------
924 X : {array-like, sparse matrix} of shape (n_samples, n_features)
925 The data used to compute the mean and standard deviation
926 used for later scaling along the features axis.
927
928 y : None
929 Ignored.
930
931 sample_weight : array-like of shape (n_samples,), default=None
932 Individual weights for each sample.
933
934 .. versionadded:: 0.24
935 parameter *sample_weight* support to StandardScaler.
936
937 Returns
938 -------
939 self : object
940 Fitted scaler.
941 """
942 first_call = not hasattr(self, "n_samples_seen_")
943 X = validate_data(
944 self,
945 X,
946 accept_sparse=("csr", "csc"),
947 dtype=FLOAT_DTYPES,
948 ensure_all_finite="allow-nan",
949 reset=first_call,
950 )
951 n_features = X.shape[1]
952
953 if sample_weight is not None:
954 sample_weight = _check_sample_weight(sample_weight, X, dtype=X.dtype)
955
956 # Even in the case of `with_mean=False`, we update the mean anyway
957 # This is needed for the incremental computation of the var
958 # See incr_mean_variance_axis and _incremental_mean_variance_axis
959
960 # if n_samples_seen_ is an integer (i.e. no missing values), we need to
961 # transform it to a NumPy array of shape (n_features,) required by
962 # incr_mean_variance_axis and _incremental_variance_axis
963 dtype = np.int64 if sample_weight is None else X.dtype
964 if not hasattr(self, "n_samples_seen_"):
965 self.n_samples_seen_ = np.zeros(n_features, dtype=dtype)
966 elif np.size(self.n_samples_seen_) == 1:
967 self.n_samples_seen_ = np.repeat(self.n_samples_seen_, X.shape[1])
968 self.n_samples_seen_ = self.n_samples_seen_.astype(dtype, copy=False)
969
970 if sparse.issparse(X):
971 if self.with_mean:
972 raise ValueError(
973 "Cannot center sparse matrices: pass `with_mean=False` "
974 "instead. See docstring for motivation and alternatives."
975 )
976 sparse_constructor = (
977 sparse.csr_matrix if X.format == "csr" else sparse.csc_matrix
978 )
979
980 if self.with_std:
981 # First pass
982 if not hasattr(self, "scale_"):
983 self.mean_, self.var_, self.n_samples_seen_ = mean_variance_axis(
984 X, axis=0, weights=sample_weight, return_sum_weights=True
985 )
986 # Next passes
987 else:
988 (
989 self.mean_,
990 self.var_,
991 self.n_samples_seen_,
992 ) = incr_mean_variance_axis(
993 X,
994 axis=0,
995 last_mean=self.mean_,
996 last_var=self.var_,
997 last_n=self.n_samples_seen_,
998 weights=sample_weight,
999 )
1000 # We force the mean and variance to float64 for large arrays
1001 # See https://github.com/scikit-learn/scikit-learn/pull/12338
1002 self.mean_ = self.mean_.astype(np.float64, copy=False)
1003 self.var_ = self.var_.astype(np.float64, copy=False)
1004 else:
1005 self.mean_ = None # as with_mean must be False for sparse
1006 self.var_ = None
1007 weights = _check_sample_weight(sample_weight, X)
1008 sum_weights_nan = weights @ sparse_constructor(
1009 (np.isnan(X.data), X.indices, X.indptr), shape=X.shape
1010 )
1011 self.n_samples_seen_ += (np.sum(weights) - sum_weights_nan).astype(
1012 dtype
1013 )
1014 else:
1015 # First pass
1016 if not hasattr(self, "scale_"):
1017 self.mean_ = 0.0
1018 if self.with_std:
1019 self.var_ = 0.0
1020 else:
1021 self.var_ = None
1022
1023 if not self.with_mean and not self.with_std:
1024 self.mean_ = None
1025 self.var_ = None
1026 self.n_samples_seen_ += X.shape[0] - np.isnan(X).sum(axis=0)
1027
1028 else:
1029 self.mean_, self.var_, self.n_samples_seen_ = _incremental_mean_and_var(
1030 X,
1031 self.mean_,
1032 self.var_,
1033 self.n_samples_seen_,
1034 sample_weight=sample_weight,
1035 )
1036
1037 # for backward-compatibility, reduce n_samples_seen_ to an integer
1038 # if the number of samples is the same for each feature (i.e. no
1039 # missing values)
1040 if np.ptp(self.n_samples_seen_) == 0:
1041 self.n_samples_seen_ = self.n_samples_seen_[0]
1042
1043 if self.with_std:
1044 # Extract the list of near constant features on the raw variances,
1045 # before taking the square root.
1046 constant_mask = _is_constant_feature(
1047 self.var_, self.mean_, self.n_samples_seen_
1048 )
1049 self.scale_ = _handle_zeros_in_scale(
1050 np.sqrt(self.var_), copy=False, constant_mask=constant_mask
1051 )
1052 else:
1053 self.scale_ = None
1054
1055 return self
1056
1057 def transform(self, X, copy=None):
1058 """Perform standardization by centering and scaling.
1059
1060 Parameters
1061 ----------
1062 X : {array-like, sparse matrix of shape (n_samples, n_features)
1063 The data used to scale along the features axis.
1064 copy : bool, default=None
1065 Copy the input X or not.
1066
1067 Returns
1068 -------
1069 X_tr : {ndarray, sparse matrix} of shape (n_samples, n_features)
1070 Transformed array.
1071 """
1072 check_is_fitted(self)
1073
1074 copy = copy if copy is not None else self.copy
1075 X = validate_data(
1076 self,
1077 X,
1078 reset=False,
1079 accept_sparse="csr",
1080 copy=copy,
1081 dtype=FLOAT_DTYPES,
1082 force_writeable=True,
1083 ensure_all_finite="allow-nan",
1084 )
1085
1086 if sparse.issparse(X):
1087 if self.with_mean:
1088 raise ValueError(
1089 "Cannot center sparse matrices: pass `with_mean=False` "
1090 "instead. See docstring for motivation and alternatives."
1091 )
1092 if self.scale_ is not None:
1093 inplace_column_scale(X, 1 / self.scale_)
1094 else:
1095 if self.with_mean:
1096 X -= self.mean_
1097 if self.with_std:
1098 X /= self.scale_
1099 return X
1100
1101 def inverse_transform(self, X, copy=None):
1102 """Scale back the data to the original representation.
1103
1104 Parameters
1105 ----------
1106 X : {array-like, sparse matrix} of shape (n_samples, n_features)
1107 The data used to scale along the features axis.
1108
1109 copy : bool, default=None
1110 Copy the input `X` or not.
1111
1112 Returns
1113 -------
1114 X_original : {ndarray, sparse matrix} of shape (n_samples, n_features)
1115 Transformed array.
1116 """
1117 check_is_fitted(self)
1118
1119 copy = copy if copy is not None else self.copy
1120 X = check_array(
1121 X,
1122 accept_sparse="csr",
1123 copy=copy,
1124 dtype=FLOAT_DTYPES,
1125 force_writeable=True,
1126 ensure_all_finite="allow-nan",
1127 )
1128
1129 if sparse.issparse(X):
1130 if self.with_mean:
1131 raise ValueError(
1132 "Cannot uncenter sparse matrices: pass `with_mean=False` "
1133 "instead See docstring for motivation and alternatives."
1134 )
1135 if self.scale_ is not None:
1136 inplace_column_scale(X, self.scale_)
1137 else:
1138 if self.with_std:
1139 X *= self.scale_
1140 if self.with_mean:
1141 X += self.mean_
1142 return X
1143
1144 def __sklearn_tags__(self):
1145 tags = super().__sklearn_tags__()
1146 tags.input_tags.allow_nan = True
1147 tags.input_tags.sparse = not self.with_mean
1148 tags.transformer_tags.preserves_dtype = ["float64", "float32"]
1149 return tags
1150
1151
1152class MaxAbsScaler(OneToOneFeatureMixin, TransformerMixin, BaseEstimator):
1153 """Scale each feature by its maximum absolute value.
1154
1155 This estimator scales and translates each feature individually such
1156 that the maximal absolute value of each feature in the
1157 training set will be 1.0. It does not shift/center the data, and
1158 thus does not destroy any sparsity.
1159
1160 This scaler can also be applied to sparse CSR or CSC matrices.
1161
1162 `MaxAbsScaler` doesn't reduce the effect of outliers; it only linearly
1163 scales them down. For an example visualization, refer to :ref:`Compare
1164 MaxAbsScaler with other scalers <plot_all_scaling_max_abs_scaler_section>`.
1165
1166 .. versionadded:: 0.17
1167
1168 Parameters
1169 ----------
1170 copy : bool, default=True
1171 Set to False to perform inplace scaling and avoid a copy (if the input
1172 is already a numpy array).
1173
1174 Attributes
1175 ----------
1176 scale_ : ndarray of shape (n_features,)
1177 Per feature relative scaling of the data.
1178
1179 .. versionadded:: 0.17
1180 *scale_* attribute.
1181
1182 max_abs_ : ndarray of shape (n_features,)
1183 Per feature maximum absolute value.
1184
1185 n_features_in_ : int
1186 Number of features seen during :term:`fit`.
1187
1188 .. versionadded:: 0.24
1189
1190 feature_names_in_ : ndarray of shape (`n_features_in_`,)
1191 Names of features seen during :term:`fit`. Defined only when `X`
1192 has feature names that are all strings.
1193
1194 .. versionadded:: 1.0
1195
1196 n_samples_seen_ : int
1197 The number of samples processed by the estimator. Will be reset on
1198 new calls to fit, but increments across ``partial_fit`` calls.
1199
1200 See Also
