scikit-learn/scikit-learn — pull request #2514 by untom (2013-10-11 10:34:15+00:00)
Title: [MRG]  Refactoring and expanding sklearn.preprocessing scaling

Hi there!

This PR refactors the data-scaling code from `sklearn.preprocessing` to remove some duplicated code, and adds some new features. More specifically:

* adds `RobustScaler` and `robust_scale` functions that use robust estimates of data center/scale (median & interquartile range), which should work better for outliers.

* Adds possibility to scale by sample instead of by feature (the `scale` function could already do this, now the *Scaler classes can, too!)

* adds `minmax_scale` function (requrested by @ogrisel)

* Reuses the code common to `StandardScaler`, `RobustScaler` and `MinMaxScaler` by putting it in an abstract base class. Essentially the *Scaler classes are only responsible for estimating the necessary statistics in `fit`, the rest of the `Transformer` API (transform/inverse_transform, handling sparseness/different axis-parameters) is implemented in the BaseClass, as this code is common to all scalers.
This caused some parameters to be renamed and some attributes to be renamed: `with_centering` and `with_scaling` are the parameters that control if scaling/centering is performed and the `center_` and `scale_` attributes are used to store the centering/scaling values.

* `*_scale` functions now simply reuse the `*Scaler` classes internally to avoid code duplication.

Notes and Caveats
* StandardScaler had parameters `with_mean` and `with_std` which are renamed to `with_centering` and `with_scaling` to fall in line with the other Scalers. I wasn't sure how to handle deprecating the old parameter names in `__init__` -- what's the protocol here?

* `RobustScaler` cannot be fitted on sparse matrices:
- centering doesn't make sense because it risks destroying sparsity (similar to what StandardScaler does)
- Scaling doesn't work because there is no decent code to calculate the IQR of sparse matrices available in scipy
As an alternative, we could advice people to scale using the MinMaxScaler instead to scale features to the same range with::

>>> scaler = MinMaxScaler()
>>> scaler.with_centering=False
>>> scaler.fit_transform(X)

(`MinMaxScaler` doesn't support the `with_centering` parameter directly because I wasn't sure if this would lead to confusion).