@@ -52,15 +52,14 @@ def _mean_and_std(X, axis=0, with_mean=True, with_std=True):
52
52
Zero valued std components are reset to 1.0 to avoid NaNs when scaling.
53
53
"""
54
54
X = np .asarray (X )
55
- Xr = np .rollaxis (X , axis )
56
55
57
56
if with_mean :
58
- mean_ = Xr .mean (axis = 0 )
57
+ mean_ = X .mean (axis = axis )
59
58
else :
60
59
mean_ = None
61
60
62
61
if with_std :
63
- std_ = Xr .std (axis = 0 )
62
+ std_ = X .std (axis = axis )
64
63
std_ = _handle_zeros_in_scale (std_ )
65
64
else :
66
65
std_ = None
@@ -95,10 +94,10 @@ def scale(X, axis=0, with_mean=True, with_std=True, copy=True):
95
94
X : array-like or CSR matrix.
96
95
The data to center and scale.
97
96
98
- axis : int (0 by default)
97
+ axis : int or None (0 by default)
99
98
axis used to compute the means and standard deviations along. If 0,
100
- independently standardize each feature, otherwise ( if 1) standardize
101
- each sample.
99
+ independently standardize each feature, if 1 standardize
100
+ each sample, if None standardize by all data taken together .
102
101
103
102
with_mean : boolean, True by default
104
103
If True, center the data before scaling.
@@ -158,10 +157,11 @@ def scale(X, axis=0, with_mean=True, with_std=True, copy=True):
158
157
X = X .copy ()
159
158
# Xr is a view on the original array that enables easy use of
160
159
# broadcasting on the axis in which we are interested in
161
- Xr = np .rollaxis (X , axis )
160
+ Xr = np .rollaxis (X , (0 if axis is None else axis ))
161
+ _check_axis = None if axis is None else 0
162
162
if with_mean :
163
163
Xr -= mean_
164
- mean_1 = Xr .mean (axis = 0 )
164
+ mean_1 = Xr .mean (axis = _check_axis )
165
165
# Verify that mean_1 is 'close to zero'. If X contains very
166
166
# large values, mean_1 can also be very large, due to a lack of
167
167
# precision of mean_. In this case, a pre-scaling of the
@@ -177,7 +177,7 @@ def scale(X, axis=0, with_mean=True, with_std=True, copy=True):
177
177
if with_std :
178
178
Xr /= std_
179
179
if with_mean :
180
- mean_2 = Xr .mean (axis = 0 )
180
+ mean_2 = Xr .mean (axis = _check_axis )
181
181
# If mean_2 is not 'close to zero', it comes from the fact that
182
182
# std_ is very small so that mean_2 = mean_1/std_ > 0, even if
183
183
# mean_1 was close to zero. The problem is thus essentially due
@@ -196,9 +196,9 @@ def scale(X, axis=0, with_mean=True, with_std=True, copy=True):
196
196
class MinMaxScaler (BaseEstimator , TransformerMixin ):
197
197
"""Standardizes features by scaling each feature to a given range.
198
198
199
- This estimator scales and translates each feature individually such
200
- that it is in the given range on the training set, i.e. between
201
- zero and one.
199
+ This estimator scales and translates each feature individually (unless
200
+ initialized with "per_feature=False") such that it is in the given
201
+ range on the training set, i.e. between zero and one.
202
202
203
203
The standardization is given by::
204
204
@@ -221,6 +221,11 @@ class MinMaxScaler(BaseEstimator, TransformerMixin):
221
221
Set to False to perform inplace row normalization and avoid a
222
222
copy (if the input is already a numpy array).
223
223
224
+ per_feature : boolean, optional, default True
225
+ Set to False to scale features based on the maximum and minimum
226
+ values over the entire input array, rather than the maximum
227
+ and minimum values by feature.
228
+
224
229
Attributes
225
230
----------
226
231
min_ : ndarray, shape (n_features,)
@@ -230,9 +235,10 @@ class MinMaxScaler(BaseEstimator, TransformerMixin):
230
235
Per feature relative scaling of the data.
231
236
"""
232
237
233
- def __init__ (self , feature_range = (0 , 1 ), copy = True ):
238
+ def __init__ (self , feature_range = (0 , 1 ), copy = True , per_feature = True ):
234
239
self .feature_range = feature_range
235
240
self .copy = copy
241
+ self .per_feature = per_feature
236
242
237
243
def fit (self , X , y = None ):
238
244
"""Compute the minimum and maximum to be used for later scaling.
@@ -249,8 +255,9 @@ def fit(self, X, y=None):
249
255
if feature_range [0 ] >= feature_range [1 ]:
250
256
raise ValueError ("Minimum of desired feature range must be smaller"
251
257
" than maximum. Got %s." % str (feature_range ))
252
- data_min = np .min (X , axis = 0 )
253
- data_range = np .max (X , axis = 0 ) - data_min
258
+ axis = 0 if self .per_feature else None
259
+ data_min = np .min (X , axis = axis )
260
+ data_range = np .max (X , axis = axis ) - data_min
254
261
data_range = _handle_zeros_in_scale (data_range )
255
262
self .scale_ = (feature_range [1 ] - feature_range [0 ]) / data_range
256
263
self .min_ = feature_range [0 ] - data_min * self .scale_
@@ -292,10 +299,10 @@ def inverse_transform(self, X):
292
299
class StandardScaler (BaseEstimator , TransformerMixin ):
293
300
"""Standardize features by removing the mean and scaling to unit variance
294
301
295
- Centering and scaling happen independently on each feature by computing
296
- the relevant statistics on the samples in the training set. Mean and
297
- standard deviation are then stored to be used on later data using the
298
- `transform` method.
302
+ Centering and scaling happen independently on each feature (unless
303
+ initialized with "per_feature=False") by computing the relevant statistics
304
+ on the samples in the training set. Mean and standard deviation are
305
+ then stored to be used on later data using the `transform` method.
299
306
300
307
Standardization of a dataset is a common requirement for many
301
308
machine learning estimators: they might behave badly if the
@@ -331,13 +338,17 @@ class StandardScaler(BaseEstimator, TransformerMixin):
331
338
not a NumPy array or scipy.sparse CSR matrix, a copy may still be
332
339
returned.
333
340
341
+ per_feature : boolean, optional, default True
342
+ Set to False to scale features based on the mean and variance taken
343
+ over the entire input array, rather than per feature.
344
+
334
345
Attributes
335
346
----------
336
- mean_ : array of floats with shape [n_features]
347
+ mean_ : array of floats with shape [n_features] or scalar float
337
348
The mean value for each feature in the training set.
338
349
339
- std_ : array of floats with shape [n_features]
340
- The standard deviation for each feature in the training set.
350
+ std_ : array of floats with shape [n_features] or scalar float
351
+ The standard deviation for each feature in the training set.
341
352
Set to one if the standard deviation is zero for a given feature.
342
353
343
354
See also
@@ -349,10 +360,12 @@ class StandardScaler(BaseEstimator, TransformerMixin):
349
360
to further remove the linear correlation across features.
350
361
"""
351
362
352
- def __init__ (self , copy = True , with_mean = True , with_std = True ):
363
+ def __init__ (self , copy = True , with_mean = True , with_std = True ,
364
+ per_feature = True ):
353
365
self .with_mean = with_mean
354
366
self .with_std = with_std
355
367
self .copy = copy
368
+ self .per_feature = per_feature
356
369
357
370
def fit (self , X , y = None ):
358
371
"""Compute the mean and std to be used for later scaling.
@@ -366,6 +379,7 @@ def fit(self, X, y=None):
366
379
X = check_array (X , accept_sparse = 'csr' , copy = self .copy ,
367
380
ensure_2d = False , warn_on_dtype = True ,
368
381
estimator = self , dtype = FLOAT_DTYPES )
382
+ axis = 0 if self .per_feature else None
369
383
if sparse .issparse (X ):
370
384
if self .with_mean :
371
385
raise ValueError (
@@ -374,15 +388,15 @@ def fit(self, X, y=None):
374
388
self .mean_ = None
375
389
376
390
if self .with_std :
377
- var = mean_variance_axis (X , axis = 0 )[1 ]
391
+ var = mean_variance_axis (X , axis = axis )[1 ]
378
392
self .std_ = np .sqrt (var )
379
393
self .std_ = _handle_zeros_in_scale (self .std_ )
380
394
else :
381
395
self .std_ = None
382
396
return self
383
397
else :
384
398
self .mean_ , self .std_ = _mean_and_std (
385
- X , axis = 0 , with_mean = self .with_mean , with_std = self .with_std )
399
+ X , axis = axis , with_mean = self .with_mean , with_std = self .with_std )
386
400
return self
387
401
388
402
def transform (self , X , y = None , copy = None ):
@@ -450,10 +464,11 @@ def inverse_transform(self, X, copy=None):
450
464
class MaxAbsScaler (BaseEstimator , TransformerMixin ):
451
465
"""Scale each feature by its maximum absolute value.
452
466
453
- This estimator scales and translates each feature individually such
454
- that the maximal absolute value of each feature in the
455
- training set will be 1.0. It does not shift/center the data, and
456
- thus does not destroy any sparsity.
467
+ This estimator scales and translates each feature individually
468
+ (unless "per_feature=False", in which case all features will be
469
+ considered together) such that the maximal absolute value of each
470
+ feature in the training set will be 1.0. It does not shift/center
471
+ the data, and thus does not destroy any sparsity.
457
472
458
473
This scaler can also be applied to sparse CSR or CSC matrices.
459
474
@@ -463,14 +478,19 @@ class MaxAbsScaler(BaseEstimator, TransformerMixin):
463
478
Set to False to perform inplace scaling and avoid a copy (if the input
464
479
is already a numpy array).
465
480
481
+ per_feature : boolean, optional, default True
482
+ Set to False to scale features based on the maximum absolute value
483
+ in the entire input array, rather than per feature.
484
+
466
485
Attributes
467
486
----------
468
- scale_ : ndarray, shape (n_features,)
487
+ scale_ : ndarray, shape (n_features,) or scalar float
469
488
Per feature relative scaling of the data.
470
489
"""
471
490
472
- def __init__ (self , copy = True ):
491
+ def __init__ (self , copy = True , per_feature = True ):
473
492
self .copy = copy
493
+ self .per_feature = per_feature
474
494
475
495
def fit (self , X , y = None ):
476
496
"""Compute the minimum and maximum to be used for later scaling.
@@ -483,13 +503,15 @@ def fit(self, X, y=None):
483
503
"""
484
504
X = check_array (X , accept_sparse = ('csr' , 'csc' ), copy = self .copy ,
485
505
ensure_2d = False , estimator = self , dtype = FLOAT_DTYPES )
506
+ axis = 0 if self .per_feature else None
486
507
if sparse .issparse (X ):
487
- mins , maxs = min_max_axis (X , axis = 0 )
508
+ mins , maxs = min_max_axis (X , axis = axis )
488
509
scales = np .maximum (np .abs (mins ), np .abs (maxs ))
489
510
else :
490
- scales = np .abs (X ).max (axis = 0 )
491
- scales = np .array (scales )
492
- scales = scales .reshape (- 1 )
511
+ scales = np .abs (X ).max (axis = axis )
512
+ if self .per_feature :
513
+ scales = np .array (scales )
514
+ scales = scales .reshape (- 1 )
493
515
self .scale_ = _handle_zeros_in_scale (scales )
494
516
return self
495
517
@@ -545,19 +567,21 @@ def maxabs_scale(X, axis=0, copy=True):
545
567
546
568
Parameters
547
569
----------
548
- axis : int (0 by default)
570
+ axis : int or None (0 by default)
549
571
axis used to scale along. If 0, independently scale each feature,
550
- otherwise ( if 1) scale each sample.
572
+ if 1 scale each sample, and if None, scale by all data together .
551
573
552
574
copy : boolean, optional, default is True
553
575
Set to False to perform inplace scaling and avoid a copy (if the input
554
576
is already a numpy array).
555
577
"""
556
- s = MaxAbsScaler (copy = copy )
557
- if axis == 0 :
578
+ s = MaxAbsScaler (copy = copy , per_feature = ( axis is not None ) )
579
+ if axis == 0 or axis is None :
558
580
return s .fit_transform (X )
559
- else :
581
+ elif axis == 1 :
560
582
return s .fit_transform (X .T ).T
583
+ else :
584
+ raise ValueError ('"axis" must be 0, 1, or None.' )
561
585
562
586
563
587
class RobustScaler (BaseEstimator , TransformerMixin ):
@@ -567,8 +591,8 @@ class RobustScaler(BaseEstimator, TransformerMixin):
567
591
the Interquartile Range (IQR). The IQR is the range between the 1st
568
592
quartile (25th quantile) and the 3rd quartile (75th quantile).
569
593
570
- Centering and scaling happen independently on each feature (or each
571
- sample, depending on the `axis` argument ) by computing the relevant
594
+ Centering and scaling happen independently on each feature (unless
595
+ initialized with "per_feature=False" ) by computing the relevant
572
596
statistics on the samples in the training set. Median and interquartile
573
597
range are then stored to be used on later data using the `transform`
574
598
method.
@@ -599,12 +623,16 @@ class RobustScaler(BaseEstimator, TransformerMixin):
599
623
not a NumPy array or scipy.sparse CSR matrix, a copy may still be
600
624
returned.
601
625
626
+ per_feature : boolean, optional, default True
627
+ Set to False to scale features based on values over the entire input
628
+ array, rather than by feature.
629
+
602
630
Attributes
603
631
----------
604
- center_ : array of floats
632
+ center_ : array of floats or scalar float
605
633
The median value for each feature in the training set.
606
634
607
- scale_ : array of floats
635
+ scale_ : array of floats or scalar float
608
636
The (scaled) interquartile range for each feature in the training set.
609
637
610
638
See also
@@ -623,10 +651,12 @@ class RobustScaler(BaseEstimator, TransformerMixin):
623
651
http://en.wikipedia.org/wiki/Interquartile_range
624
652
"""
625
653
626
- def __init__ (self , with_centering = True , with_scaling = True , copy = True ):
654
+ def __init__ (self , with_centering = True , with_scaling = True ,
655
+ copy = True , per_feature = True ):
627
656
self .with_centering = with_centering
628
657
self .with_scaling = with_scaling
629
658
self .copy = copy
659
+ self .per_feature = per_feature
630
660
631
661
def _check_array (self , X , copy ):
632
662
"""Makes sure centering is not enabled for sparse matrices."""
@@ -652,11 +682,12 @@ def fit(self, X, y=None):
652
682
raise TypeError ("RobustScaler cannot be fitted on sparse inputs" )
653
683
654
684
X = self ._check_array (X , self .copy )
685
+ axis = 0 if self .per_feature else None
655
686
if self .with_centering :
656
- self .center_ = np .median (X , axis = 0 )
687
+ self .center_ = np .median (X , axis = axis )
657
688
658
689
if self .with_scaling :
659
- q = np .percentile (X , (25 , 75 ), axis = 0 )
690
+ q = np .percentile (X , (25 , 75 ), axis = axis )
660
691
self .scale_ = (q [1 ] - q [0 ])
661
692
self .scale_ = _handle_zeros_in_scale (self .scale_ )
662
693
return self
@@ -727,10 +758,10 @@ def robust_scale(X, axis=0, with_centering=True, with_scaling=True, copy=True):
727
758
X : array-like.
728
759
The data to center and scale.
729
760
730
- axis : int (0 by default)
761
+ axis : int or None (0 by default)
731
762
axis used to compute the medians and IQR along. If 0,
732
- independently scale each feature, otherwise ( if 1) scale
733
- each sample.
763
+ independently scale each feature, if 1, scale
764
+ each sample, and if None, scale all data together .
734
765
735
766
with_centering : boolean, True by default
736
767
If True, center the data before scaling.
@@ -764,11 +795,13 @@ def robust_scale(X, axis=0, with_centering=True, with_scaling=True, copy=True):
764
795
:class:`sklearn.pipeline.Pipeline`)
765
796
"""
766
797
s = RobustScaler (with_centering = with_centering , with_scaling = with_scaling ,
767
- copy = copy )
768
- if axis == 0 :
798
+ copy = copy , per_feature = ( axis is not None ) )
799
+ if axis == 0 or axis is None :
769
800
return s .fit_transform (X )
770
- else :
801
+ elif axis == 1 :
771
802
return s .fit_transform (X .T ).T
803
+ else :
804
+ raise ValueError ('"axis" must be 0, 1, or None.' )
772
805
773
806
774
807
class PolynomialFeatures (BaseEstimator , TransformerMixin ):
0 commit comments