2012-01-21 04:29:42 +08:00
|
|
|
# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>
|
|
|
|
|
# Mathieu Blondel <mathieu@mblondel.org>
|
|
|
|
|
# Olivier Grisel <olivier.grisel@ensta.org>
|
2012-10-26 04:07:02 +08:00
|
|
|
# Andreas Mueller <amueller@ais.uni-bonn.de>
|
2012-01-21 04:29:42 +08:00
|
|
|
# License: BSD
|
2012-10-26 04:07:02 +08:00
|
|
|
|
2012-05-18 15:25:21 +08:00
|
|
|
from collections import Sequence
|
2012-09-08 04:52:25 +08:00
|
|
|
import warnings
|
2012-10-26 04:07:02 +08:00
|
|
|
import numbers
|
2012-05-18 15:25:21 +08:00
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
import numpy as np
|
|
|
|
|
import scipy.sparse as sp
|
|
|
|
|
|
2012-11-19 17:18:59 +08:00
|
|
|
from .utils import check_arrays, array2d, atleast2d_or_csr, safe_asarray
|
2012-01-21 13:21:40 +08:00
|
|
|
from .utils import warn_if_not_float
|
2012-08-18 00:28:39 +08:00
|
|
|
from .utils.fixes import unique
|
2012-01-21 13:21:40 +08:00
|
|
|
from .base import BaseEstimator, TransformerMixin
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-01-21 13:21:40 +08:00
|
|
|
from .utils.sparsefuncs import inplace_csr_row_normalize_l1
|
|
|
|
|
from .utils.sparsefuncs import inplace_csr_row_normalize_l2
|
|
|
|
|
from .utils.sparsefuncs import inplace_csr_column_scale
|
|
|
|
|
from .utils.sparsefuncs import mean_variance_axis0
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-08-24 05:51:37 +08:00
|
|
|
__all__ = ['Binarizer',
|
|
|
|
|
'KernelCenterer',
|
|
|
|
|
'LabelBinarizer',
|
|
|
|
|
'LabelEncoder',
|
|
|
|
|
'Normalizer',
|
2012-09-23 19:58:12 +08:00
|
|
|
'StandardScaler',
|
2012-08-24 05:51:37 +08:00
|
|
|
'binarize',
|
|
|
|
|
'normalize',
|
|
|
|
|
'scale']
|
|
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
def _mean_and_std(X, axis=0, with_mean=True, with_std=True):
|
2012-12-03 05:10:36 +08:00
|
|
|
"""Compute mean and std deviation for centering, scaling.
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
Zero valued std components are reset to 1.0 to avoid NaNs when scaling.
|
|
|
|
|
"""
|
|
|
|
|
X = np.asarray(X)
|
|
|
|
|
Xr = np.rollaxis(X, axis)
|
|
|
|
|
|
|
|
|
|
if with_mean:
|
|
|
|
|
mean_ = Xr.mean(axis=0)
|
|
|
|
|
else:
|
|
|
|
|
mean_ = None
|
|
|
|
|
|
|
|
|
|
if with_std:
|
|
|
|
|
std_ = Xr.std(axis=0)
|
|
|
|
|
if isinstance(std_, np.ndarray):
|
|
|
|
|
std_[std_ == 0.0] = 1.0
|
|
|
|
|
elif std_ == 0.:
|
|
|
|
|
std_ = 1.
|
|
|
|
|
else:
|
|
|
|
|
std_ = None
|
|
|
|
|
|
|
|
|
|
return mean_, std_
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def scale(X, axis=0, with_mean=True, with_std=True, copy=True):
|
|
|
|
|
"""Standardize a dataset along any axis
|
|
|
|
|
|
|
|
|
|
Center to the mean and component wise scale to unit variance.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like or CSR matrix.
|
|
|
|
|
The data to center and scale.
|
|
|
|
|
|
|
|
|
|
axis : int (0 by default)
|
|
|
|
|
axis used to compute the means and standard deviations along. If 0,
|
|
|
|
|
independently standardize each feature, otherwise (if 1) standardize
|
|
|
|
|
each sample.
|
|
|
|
|
|
|
|
|
|
with_mean : boolean, True by default
|
|
|
|
|
If True, center the data before scaling.
|
|
|
|
|
|
|
|
|
|
with_std : boolean, True by default
|
|
|
|
|
If True, scale the data to unit variance (or equivalently,
|
|
|
|
|
unit standard deviation).
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
|
|
|
|
set to False to perform inplace row normalization and avoid a
|
|
|
|
|
copy (if the input is already a numpy array or a scipy.sparse
|
|
|
|
|
CSR matrix and if axis is 1).
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
This implementation will refuse to center scipy.sparse matrices
|
|
|
|
|
since it would make them non-sparse and would potentially crash the
|
|
|
|
|
program with memory exhaustion problems.
|
|
|
|
|
|
|
|
|
|
Instead the caller is expected to either set explicitly
|
|
|
|
|
`with_mean=False` (in that case, only variance scaling will be
|
|
|
|
|
performed on the features of the CSR matrix) or to call `X.toarray()`
|
|
|
|
|
if he/she expects the materialized dense array to fit in memory.
|
|
|
|
|
|
|
|
|
|
To avoid memory copy the caller should pass a CSR matrix.
|
|
|
|
|
|
|
|
|
|
See also
|
|
|
|
|
--------
|
2012-09-23 19:58:12 +08:00
|
|
|
:class:`sklearn.preprocessing.StandardScaler` to perform centering and
|
2012-01-21 04:29:42 +08:00
|
|
|
scaling using the ``Transformer`` API (e.g. as part of a preprocessing
|
|
|
|
|
:class:`sklearn.pipeline.Pipeline`)
|
|
|
|
|
"""
|
|
|
|
|
if sp.issparse(X):
|
|
|
|
|
if with_mean:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Cannot center sparse matrices: pass `with_mean=False` instead"
|
|
|
|
|
" See docstring for motivation and alternatives.")
|
|
|
|
|
if axis != 0:
|
|
|
|
|
raise ValueError("Can only scale sparse matrix on axis=0, "
|
|
|
|
|
" got axis=%d" % axis)
|
|
|
|
|
warn_if_not_float(X, estimator='The scale function')
|
|
|
|
|
if not sp.isspmatrix_csr(X):
|
|
|
|
|
X = X.tocsr()
|
|
|
|
|
copy = False
|
|
|
|
|
if copy:
|
|
|
|
|
X = X.copy()
|
|
|
|
|
_, var = mean_variance_axis0(X)
|
|
|
|
|
var[var == 0.0] = 1.0
|
|
|
|
|
inplace_csr_column_scale(X, 1 / np.sqrt(var))
|
|
|
|
|
else:
|
|
|
|
|
X = np.asarray(X)
|
|
|
|
|
warn_if_not_float(X, estimator='The scale function')
|
|
|
|
|
mean_, std_ = _mean_and_std(
|
|
|
|
|
X, axis, with_mean=with_mean, with_std=with_std)
|
|
|
|
|
if copy:
|
|
|
|
|
X = X.copy()
|
2012-05-05 05:46:38 +08:00
|
|
|
# Xr is a view on the original array that enables easy use of
|
|
|
|
|
# broadcasting on the axis in which we are interested in
|
2012-01-21 04:29:42 +08:00
|
|
|
Xr = np.rollaxis(X, axis)
|
|
|
|
|
if with_mean:
|
|
|
|
|
Xr -= mean_
|
|
|
|
|
if with_std:
|
|
|
|
|
Xr /= std_
|
|
|
|
|
return X
|
|
|
|
|
|
|
|
|
|
|
2012-09-08 04:52:25 +08:00
|
|
|
class MinMaxScaler(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Standardizes features by scaling each feature to a given range.
|
|
|
|
|
|
|
|
|
|
This estimator scales and translates each feature individually such
|
|
|
|
|
that it is in the given range on the training set, i.e. between
|
|
|
|
|
zero and one.
|
|
|
|
|
|
2012-09-18 06:38:02 +08:00
|
|
|
The standardization is given by::
|
|
|
|
|
X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
|
|
|
|
|
X_scaled = X_std / (max - min) + min
|
|
|
|
|
where min, max = feature_range.
|
|
|
|
|
|
2012-09-08 04:52:25 +08:00
|
|
|
This standardization is often used as an alternative to zero mean,
|
|
|
|
|
unit variance scaling.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
feature_range: tuple (min, max), default=(0, 1)
|
|
|
|
|
Desired range of transformed data.
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
|
|
|
|
Set to False to perform inplace row normalization and avoid a
|
2012-09-23 19:58:12 +08:00
|
|
|
copy (if the input is already a numpy array).
|
2012-09-08 04:52:25 +08:00
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
min_ : ndarray, shape (n_features,)
|
2012-09-18 06:38:02 +08:00
|
|
|
Per feature adjustment for minimum.
|
2012-09-08 04:52:25 +08:00
|
|
|
|
|
|
|
|
scale_ : ndarray, shape (n_features,)
|
2012-09-18 06:38:02 +08:00
|
|
|
Per feature relative scaling of the data.
|
2012-09-08 04:52:25 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, feature_range=(0, 1), copy=True):
|
|
|
|
|
self.feature_range = feature_range
|
|
|
|
|
self.copy = copy
|
|
|
|
|
|
|
|
|
|
def fit(self, X, y=None):
|
2012-09-21 19:38:21 +08:00
|
|
|
"""Compute the minimum and maximum to be used for later scaling.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like, shape [n_samples, n_features]
|
|
|
|
|
The data used to compute the per-feature minimum and maximum
|
|
|
|
|
used for later scaling along the features axis.
|
|
|
|
|
"""
|
2012-09-18 06:38:02 +08:00
|
|
|
X = check_arrays(X, sparse_format="dense", copy=self.copy)[0]
|
2012-12-28 05:38:19 +08:00
|
|
|
warn_if_not_float(X, estimator=self)
|
2012-09-08 04:52:25 +08:00
|
|
|
feature_range = self.feature_range
|
|
|
|
|
if feature_range[0] >= feature_range[1]:
|
|
|
|
|
raise ValueError("Minimum of desired feature range must be smaller"
|
|
|
|
|
" than maximum. Got %s." % str(feature_range))
|
2012-09-18 06:38:02 +08:00
|
|
|
min_ = np.min(X, axis=0)
|
|
|
|
|
scale_ = np.max(X, axis=0) - min_
|
2012-12-27 09:02:26 +08:00
|
|
|
# Do not scale constant features
|
2012-12-27 08:59:50 +08:00
|
|
|
scale_[scale_ == 0.0] = 1.0
|
2012-09-18 06:38:02 +08:00
|
|
|
self.scale_ = (feature_range[1] - feature_range[0]) / scale_
|
|
|
|
|
self.min_ = feature_range[0] - min_ / scale_
|
2012-09-08 04:52:25 +08:00
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def transform(self, X):
|
2012-09-21 19:38:21 +08:00
|
|
|
"""Scaling features of X according to feature_range.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like with shape [n_samples, n_features]
|
|
|
|
|
Input data that will be transformed.
|
|
|
|
|
"""
|
2012-09-18 06:38:02 +08:00
|
|
|
X = check_arrays(X, sparse_format="dense", copy=self.copy)[0]
|
|
|
|
|
X *= self.scale_
|
|
|
|
|
X += self.min_
|
2012-09-08 04:52:25 +08:00
|
|
|
return X
|
|
|
|
|
|
2013-01-11 05:32:19 +08:00
|
|
|
def inverse_transform(self, X):
|
|
|
|
|
"""Undo the scaling of X according to feature_range.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like with shape [n_samples, n_features]
|
|
|
|
|
Input data that will be transformed.
|
|
|
|
|
"""
|
|
|
|
|
X = check_arrays(X, sparse_format="dense", copy=self.copy)[0]
|
|
|
|
|
X -= self.min_
|
|
|
|
|
X /= self.scale_
|
|
|
|
|
return X
|
|
|
|
|
|
2012-09-08 04:52:25 +08:00
|
|
|
|
2012-09-18 06:38:02 +08:00
|
|
|
class StandardScaler(BaseEstimator, TransformerMixin):
|
2012-01-21 04:29:42 +08:00
|
|
|
"""Standardize features by removing the mean and scaling to unit variance
|
|
|
|
|
|
|
|
|
|
Centering and scaling happen indepently on each feature by computing
|
|
|
|
|
the relevant statistics on the samples in the training set. Mean and
|
|
|
|
|
standard deviation are then stored to be used on later data using the
|
|
|
|
|
`transform` method.
|
|
|
|
|
|
|
|
|
|
Standardization of a dataset is a common requirement for many
|
|
|
|
|
machine learning estimators: they might behave badly if the
|
|
|
|
|
individual feature do not more or less look like standard normally
|
|
|
|
|
distributed data (e.g. Gaussian with 0 mean and unit variance).
|
|
|
|
|
|
|
|
|
|
For instance many elements used in the objective function of
|
|
|
|
|
a learning algorithm (such as the RBF kernel of Support Vector
|
|
|
|
|
Machines or the L1 and L2 regularizers of linear models) assume that
|
|
|
|
|
all features are centered around 0 and have variance in the same
|
|
|
|
|
order. If a feature has a variance that is orders of magnitude larger
|
|
|
|
|
that others, it might dominate the objective function and make the
|
|
|
|
|
estimator unable to learn from other features correctly as expected.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
with_mean : boolean, True by default
|
|
|
|
|
If True, center the data before scaling.
|
|
|
|
|
|
|
|
|
|
with_std : boolean, True by default
|
|
|
|
|
If True, scale the data to unit variance (or equivalently,
|
|
|
|
|
unit standard deviation).
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
2012-09-08 04:52:25 +08:00
|
|
|
Set to False to perform inplace row normalization and avoid a
|
2012-01-21 04:29:42 +08:00
|
|
|
copy (if the input is already a numpy array or a scipy.sparse
|
|
|
|
|
CSR matrix and if axis is 1).
|
|
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
`mean_` : array of floats with shape [n_features]
|
|
|
|
|
The mean value for each feature in the training set.
|
|
|
|
|
|
|
|
|
|
`std_` : array of floats with shape [n_features]
|
|
|
|
|
The standard deviation for each feature in the training set.
|
|
|
|
|
|
|
|
|
|
See also
|
|
|
|
|
--------
|
|
|
|
|
:func:`sklearn.preprocessing.scale` to perform centering and
|
|
|
|
|
scaling without using the ``Transformer`` object oriented API
|
|
|
|
|
|
|
|
|
|
:class:`sklearn.decomposition.RandomizedPCA` with `whiten=True`
|
|
|
|
|
to further remove the linear correlation across features.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, copy=True, with_mean=True, with_std=True):
|
|
|
|
|
self.with_mean = with_mean
|
|
|
|
|
self.with_std = with_std
|
|
|
|
|
self.copy = copy
|
|
|
|
|
|
|
|
|
|
def fit(self, X, y=None):
|
2012-09-21 19:38:21 +08:00
|
|
|
"""Compute the mean and std to be used for later scaling.
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like or CSR matrix with shape [n_samples, n_features]
|
|
|
|
|
The data used to compute the mean and standard deviation
|
|
|
|
|
used for later scaling along the features axis.
|
|
|
|
|
"""
|
2012-11-10 22:05:39 +08:00
|
|
|
X = check_arrays(X, copy=self.copy, sparse_format="csr")[0]
|
2012-01-21 04:29:42 +08:00
|
|
|
if sp.issparse(X):
|
|
|
|
|
if self.with_mean:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Cannot center sparse matrices: pass `with_mean=False` "
|
|
|
|
|
"instead See docstring for motivation and alternatives.")
|
|
|
|
|
warn_if_not_float(X, estimator=self)
|
|
|
|
|
self.mean_ = None
|
2012-11-10 22:05:39 +08:00
|
|
|
var = mean_variance_axis0(X)[1]
|
2012-01-21 04:29:42 +08:00
|
|
|
self.std_ = np.sqrt(var)
|
|
|
|
|
self.std_[var == 0.0] = 1.0
|
|
|
|
|
return self
|
|
|
|
|
else:
|
|
|
|
|
warn_if_not_float(X, estimator=self)
|
|
|
|
|
self.mean_, self.std_ = _mean_and_std(
|
|
|
|
|
X, axis=0, with_mean=self.with_mean, with_std=self.with_std)
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def transform(self, X, y=None, copy=None):
|
|
|
|
|
"""Perform standardization by centering and scaling
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like with shape [n_samples, n_features]
|
|
|
|
|
The data used to scale along the features axis.
|
|
|
|
|
"""
|
|
|
|
|
copy = copy if copy is not None else self.copy
|
2012-11-10 22:05:39 +08:00
|
|
|
X = check_arrays(X, copy=copy, sparse_format="csr")[0]
|
2012-01-21 04:29:42 +08:00
|
|
|
if sp.issparse(X):
|
|
|
|
|
if self.with_mean:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Cannot center sparse matrices: pass `with_mean=False` "
|
|
|
|
|
"instead See docstring for motivation and alternatives.")
|
|
|
|
|
warn_if_not_float(X, estimator=self)
|
|
|
|
|
inplace_csr_column_scale(X, 1 / self.std_)
|
|
|
|
|
else:
|
|
|
|
|
warn_if_not_float(X, estimator=self)
|
|
|
|
|
if self.with_mean:
|
|
|
|
|
X -= self.mean_
|
|
|
|
|
if self.with_std:
|
|
|
|
|
X /= self.std_
|
|
|
|
|
return X
|
|
|
|
|
|
|
|
|
|
def inverse_transform(self, X, copy=None):
|
|
|
|
|
"""Scale back the data to the original representation
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like with shape [n_samples, n_features]
|
|
|
|
|
The data used to scale along the features axis.
|
|
|
|
|
"""
|
|
|
|
|
copy = copy if copy is not None else self.copy
|
|
|
|
|
if sp.issparse(X):
|
|
|
|
|
if self.with_mean:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Cannot uncenter sparse matrices: pass `with_mean=False` "
|
|
|
|
|
"instead See docstring for motivation and alternatives.")
|
|
|
|
|
if not sp.isspmatrix_csr(X):
|
|
|
|
|
X = X.tocsr()
|
|
|
|
|
copy = False
|
|
|
|
|
if copy:
|
|
|
|
|
X = X.copy()
|
|
|
|
|
inplace_csr_column_scale(X, self.std_)
|
|
|
|
|
else:
|
|
|
|
|
X = np.asarray(X)
|
|
|
|
|
if copy:
|
|
|
|
|
X = X.copy()
|
|
|
|
|
if self.with_std:
|
|
|
|
|
X *= self.std_
|
|
|
|
|
if self.with_mean:
|
|
|
|
|
X += self.mean_
|
|
|
|
|
return X
|
|
|
|
|
|
|
|
|
|
|
2012-09-18 06:38:02 +08:00
|
|
|
class Scaler(StandardScaler):
|
2012-09-08 04:52:25 +08:00
|
|
|
def __init__(self, copy=True, with_mean=True, with_std=True):
|
2012-09-18 06:38:02 +08:00
|
|
|
warnings.warn("Scaler was renamed to StandardScaler. The old name "
|
2012-12-17 06:28:10 +08:00
|
|
|
" will be removed in 0.15.", DeprecationWarning)
|
2012-09-08 04:52:25 +08:00
|
|
|
super(Scaler, self).__init__(copy, with_mean, with_std)
|
|
|
|
|
|
|
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
def normalize(X, norm='l2', axis=1, copy=True):
|
|
|
|
|
"""Normalize a dataset along any axis
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array or scipy.sparse matrix with shape [n_samples, n_features]
|
|
|
|
|
The data to normalize, element by element.
|
|
|
|
|
scipy.sparse matrices should be in CSR format to avoid an
|
|
|
|
|
un-necessary copy.
|
|
|
|
|
|
|
|
|
|
norm : 'l1' or 'l2', optional ('l2' by default)
|
|
|
|
|
The norm to use to normalize each non zero sample (or each non-zero
|
|
|
|
|
feature if axis is 0).
|
|
|
|
|
|
|
|
|
|
axis : 0 or 1, optional (1 by default)
|
|
|
|
|
axis used to normalize the data along. If 1, independently normalize
|
|
|
|
|
each sample, otherwise (if 0) normalize each feature.
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
|
|
|
|
set to False to perform inplace row normalization and avoid a
|
|
|
|
|
copy (if the input is already a numpy array or a scipy.sparse
|
|
|
|
|
CSR matrix and if axis is 1).
|
|
|
|
|
|
|
|
|
|
See also
|
|
|
|
|
--------
|
|
|
|
|
:class:`sklearn.preprocessing.Normalizer` to perform normalization
|
|
|
|
|
using the ``Transformer`` API (e.g. as part of a preprocessing
|
|
|
|
|
:class:`sklearn.pipeline.Pipeline`)
|
|
|
|
|
"""
|
|
|
|
|
if norm not in ('l1', 'l2'):
|
|
|
|
|
raise ValueError("'%s' is not a supported norm" % norm)
|
|
|
|
|
|
|
|
|
|
if axis == 0:
|
|
|
|
|
sparse_format = 'csc'
|
|
|
|
|
elif axis == 1:
|
|
|
|
|
sparse_format = 'csr'
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError("'%d' is not a supported axis" % axis)
|
|
|
|
|
|
|
|
|
|
X = check_arrays(X, sparse_format=sparse_format, copy=copy)[0]
|
|
|
|
|
warn_if_not_float(X, 'The normalize function')
|
|
|
|
|
if axis == 0:
|
|
|
|
|
X = X.T
|
|
|
|
|
|
|
|
|
|
if sp.issparse(X):
|
|
|
|
|
if norm == 'l1':
|
|
|
|
|
inplace_csr_row_normalize_l1(X)
|
|
|
|
|
elif norm == 'l2':
|
|
|
|
|
inplace_csr_row_normalize_l2(X)
|
|
|
|
|
else:
|
|
|
|
|
if norm == 'l1':
|
|
|
|
|
norms = np.abs(X).sum(axis=1)[:, np.newaxis]
|
|
|
|
|
norms[norms == 0.0] = 1.0
|
|
|
|
|
elif norm == 'l2':
|
|
|
|
|
norms = np.sqrt(np.sum(X ** 2, axis=1))[:, np.newaxis]
|
|
|
|
|
norms[norms == 0.0] = 1.0
|
|
|
|
|
X /= norms
|
|
|
|
|
|
|
|
|
|
if axis == 0:
|
|
|
|
|
X = X.T
|
|
|
|
|
|
|
|
|
|
return X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Normalizer(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Normalize samples individually to unit norm
|
|
|
|
|
|
|
|
|
|
Each sample (i.e. each row of the data matrix) with at least one
|
|
|
|
|
non zero component is rescaled independently of other samples so
|
|
|
|
|
that its norm (l1 or l2) equals one.
|
|
|
|
|
|
|
|
|
|
This transformer is able to work both with dense numpy arrays and
|
|
|
|
|
scipy.sparse matrix (use CSR format if you want to avoid the burden of
|
|
|
|
|
a copy / conversion).
|
|
|
|
|
|
|
|
|
|
Scaling inputs to unit norms is a common operation for text
|
|
|
|
|
classification or clustering for instance. For instance the dot
|
|
|
|
|
product of two l2-normalized TF-IDF vectors is the cosine similarity
|
|
|
|
|
of the vectors and is the base similarity metric for the Vector
|
|
|
|
|
Space Model commonly used by the Information Retrieval community.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
norm : 'l1' or 'l2', optional ('l2' by default)
|
|
|
|
|
The norm to use to normalize each non zero sample.
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
|
|
|
|
set to False to perform inplace row normalization and avoid a
|
|
|
|
|
copy (if the input is already a numpy array or a scipy.sparse
|
|
|
|
|
CSR matrix).
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
This estimator is stateless (besides constructor parameters), the
|
|
|
|
|
fit method does nothing but is useful when used in a pipeline.
|
|
|
|
|
|
|
|
|
|
See also
|
|
|
|
|
--------
|
|
|
|
|
:func:`sklearn.preprocessing.normalize` equivalent function
|
|
|
|
|
without the object oriented API
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, norm='l2', copy=True):
|
|
|
|
|
self.norm = norm
|
|
|
|
|
self.copy = copy
|
|
|
|
|
|
|
|
|
|
def fit(self, X, y=None):
|
|
|
|
|
"""Do nothing and return the estimator unchanged
|
|
|
|
|
|
|
|
|
|
This method is just there to implement the usual API and hence
|
|
|
|
|
work in pipelines.
|
|
|
|
|
"""
|
2012-11-02 01:40:48 +08:00
|
|
|
atleast2d_or_csr(X)
|
2012-01-21 04:29:42 +08:00
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def transform(self, X, y=None, copy=None):
|
|
|
|
|
"""Scale each non zero row of X to unit norm
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array or scipy.sparse matrix with shape [n_samples, n_features]
|
|
|
|
|
The data to normalize, row by row. scipy.sparse matrices should be
|
|
|
|
|
in CSR format to avoid an un-necessary copy.
|
|
|
|
|
"""
|
|
|
|
|
copy = copy if copy is not None else self.copy
|
2012-11-02 01:40:48 +08:00
|
|
|
atleast2d_or_csr(X)
|
2012-01-21 04:29:42 +08:00
|
|
|
return normalize(X, norm=self.norm, axis=1, copy=copy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def binarize(X, threshold=0.0, copy=True):
|
|
|
|
|
"""Boolean thresholding of array-like or scipy.sparse matrix
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array or scipy.sparse matrix with shape [n_samples, n_features]
|
|
|
|
|
The data to binarize, element by element.
|
2013-01-08 15:19:14 +08:00
|
|
|
scipy.sparse matrices should be in CSR or CSC format to avoid an
|
2012-01-21 04:29:42 +08:00
|
|
|
un-necessary copy.
|
|
|
|
|
|
|
|
|
|
threshold : float, optional (0.0 by default)
|
|
|
|
|
The lower bound that triggers feature values to be replaced by 1.0.
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
|
|
|
|
set to False to perform inplace binarization and avoid a copy
|
2013-01-08 15:19:14 +08:00
|
|
|
(if the input is already a numpy array or a scipy.sparse CSR / CSC
|
2012-01-21 04:29:42 +08:00
|
|
|
matrix and if axis is 1).
|
|
|
|
|
|
|
|
|
|
See also
|
|
|
|
|
--------
|
|
|
|
|
:class:`sklearn.preprocessing.Binarizer` to perform binarization
|
|
|
|
|
using the ``Transformer`` API (e.g. as part of a preprocessing
|
|
|
|
|
:class:`sklearn.pipeline.Pipeline`)
|
|
|
|
|
"""
|
2013-01-08 15:19:14 +08:00
|
|
|
sparse_format = "csr" # We force sparse format to be either csr or csc.
|
|
|
|
|
if hasattr(X, "format"):
|
|
|
|
|
if X.format in ["csr", "csc"]:
|
|
|
|
|
sparse_format = X.format
|
|
|
|
|
|
|
|
|
|
X = check_arrays(X, sparse_format=sparse_format, copy=copy)[0]
|
2012-01-21 04:29:42 +08:00
|
|
|
if sp.issparse(X):
|
|
|
|
|
cond = X.data > threshold
|
|
|
|
|
not_cond = np.logical_not(cond)
|
|
|
|
|
X.data[cond] = 1
|
|
|
|
|
X.data[not_cond] = 0
|
2013-01-09 03:25:04 +08:00
|
|
|
X.eliminate_zeros()
|
2012-01-21 04:29:42 +08:00
|
|
|
else:
|
|
|
|
|
cond = X > threshold
|
|
|
|
|
not_cond = np.logical_not(cond)
|
|
|
|
|
X[cond] = 1
|
|
|
|
|
X[not_cond] = 0
|
|
|
|
|
return X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Binarizer(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Binarize data (set feature values to 0 or 1) according to a threshold
|
|
|
|
|
|
|
|
|
|
The default threshold is 0.0 so that any non-zero values are set to 1.0
|
|
|
|
|
and zeros are left untouched.
|
|
|
|
|
|
|
|
|
|
Binarization is a common operation on text count data where the
|
|
|
|
|
analyst can decide to only consider the presence or absence of a
|
|
|
|
|
feature rather than a quantified number of occurences for instance.
|
|
|
|
|
|
|
|
|
|
It can also be used as a pre-processing step for estimators that
|
|
|
|
|
consider boolean random variables (e.g. modeled using the Bernoulli
|
|
|
|
|
distribution in a Bayesian setting).
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
threshold : float, optional (0.0 by default)
|
|
|
|
|
The lower bound that triggers feature values to be replaced by 1.0.
|
|
|
|
|
|
|
|
|
|
copy : boolean, optional, default is True
|
|
|
|
|
set to False to perform inplace binarization and avoid a copy (if
|
|
|
|
|
the input is already a numpy array or a scipy.sparse CSR matrix).
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
If the input is a sparse matrix, only the non-zero values are subject
|
|
|
|
|
to update by the Binarizer class.
|
|
|
|
|
|
|
|
|
|
This estimator is stateless (besides constructor parameters), the
|
|
|
|
|
fit method does nothing but is useful when used in a pipeline.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, threshold=0.0, copy=True):
|
|
|
|
|
self.threshold = threshold
|
|
|
|
|
self.copy = copy
|
|
|
|
|
|
|
|
|
|
def fit(self, X, y=None):
|
|
|
|
|
"""Do nothing and return the estimator unchanged
|
|
|
|
|
|
|
|
|
|
This method is just there to implement the usual API and hence
|
|
|
|
|
work in pipelines.
|
|
|
|
|
"""
|
2012-11-02 01:40:48 +08:00
|
|
|
atleast2d_or_csr(X)
|
2012-01-21 04:29:42 +08:00
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def transform(self, X, y=None, copy=None):
|
|
|
|
|
"""Binarize each element of X
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array or scipy.sparse matrix with shape [n_samples, n_features]
|
|
|
|
|
The data to binarize, element by element.
|
|
|
|
|
scipy.sparse matrices should be in CSR format to avoid an
|
|
|
|
|
un-necessary copy.
|
|
|
|
|
"""
|
|
|
|
|
copy = copy if copy is not None else self.copy
|
|
|
|
|
return binarize(X, threshold=self.threshold, copy=copy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _is_label_indicator_matrix(y):
|
|
|
|
|
return hasattr(y, "shape") and len(y.shape) == 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _is_multilabel(y):
|
2012-05-18 15:25:21 +08:00
|
|
|
# the explicit check for ndarray is for forward compatibility; future
|
|
|
|
|
# versions of Numpy might want to register ndarray as a Sequence
|
2012-12-17 06:28:10 +08:00
|
|
|
return (not isinstance(y[0], np.ndarray) and isinstance(y[0], Sequence) and
|
|
|
|
|
not isinstance(y[0], basestring) or _is_label_indicator_matrix(y))
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-05-07 17:51:45 +08:00
|
|
|
|
2012-10-26 04:07:02 +08:00
|
|
|
class OneHotEncoder(BaseEstimator, TransformerMixin):
|
2012-10-26 04:57:53 +08:00
|
|
|
"""Encode categorical integer features using a one-hot aka one-of-K scheme.
|
2012-10-26 04:07:02 +08:00
|
|
|
|
2012-11-07 20:32:16 +08:00
|
|
|
The input to this transformer should be a matrix of integers, denoting
|
|
|
|
|
the values taken on by categorical (discrete) features. The output will be
|
|
|
|
|
a sparse matrix were each column corresponds to one possible value of one
|
|
|
|
|
feature. It is assumed that input features take on values in the range
|
|
|
|
|
[0, n_values).
|
|
|
|
|
|
|
|
|
|
This encoding is needed for feeding categorical data to scikit-learn
|
|
|
|
|
estimators.
|
2012-10-26 04:07:02 +08:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
n_values : 'auto', int or array of int
|
|
|
|
|
Number of values per feature.
|
2012-11-07 20:32:16 +08:00
|
|
|
'auto' : determine value range from training data.
|
2012-10-26 04:07:02 +08:00
|
|
|
int : maximum value for all features.
|
|
|
|
|
array : maximum value per feature.
|
|
|
|
|
|
2012-10-26 05:11:53 +08:00
|
|
|
dtype : number type, default=np.float
|
|
|
|
|
Desired dtype of output.
|
|
|
|
|
|
2012-10-26 04:07:02 +08:00
|
|
|
Attributes
|
|
|
|
|
----------
|
2012-10-27 01:46:16 +08:00
|
|
|
`active_features_` : array
|
2012-11-07 20:32:16 +08:00
|
|
|
Indices for active features, meaning values that actually occur in the
|
|
|
|
|
training set. Only available when n_values is ``'auto'``.
|
2012-10-26 04:07:02 +08:00
|
|
|
`feature_indices_` : array of shape (n_features,)
|
2012-11-07 20:32:16 +08:00
|
|
|
Indices to feature ranges. Feature ``i`` in the original data is mapped
|
|
|
|
|
to features ``feature_indices_[i]`` to ``feature_indices_[i+1]``
|
2012-10-27 01:46:16 +08:00
|
|
|
(and potentially masked by `active_features_` afterwards)
|
2012-10-26 04:07:02 +08:00
|
|
|
`n_values_` : array of shape (n_features,)
|
|
|
|
|
Maximum number of values per feature.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
2012-11-07 20:32:16 +08:00
|
|
|
Given a dataset with three features and two samples, we let the encoder
|
|
|
|
|
find the maximum value per feature and transform the data to a binary
|
|
|
|
|
one-hot encoding.
|
2012-10-26 04:07:02 +08:00
|
|
|
|
|
|
|
|
>>> from sklearn.preprocessing import OneHotEncoder
|
|
|
|
|
>>> enc = OneHotEncoder()
|
2012-10-27 02:08:42 +08:00
|
|
|
>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])
|
2012-10-28 01:19:57 +08:00
|
|
|
OneHotEncoder(dtype=<type 'float'>, n_values='auto')
|
2012-10-26 04:07:02 +08:00
|
|
|
>>> enc.n_values_
|
2012-10-27 02:08:42 +08:00
|
|
|
array([2, 3, 4])
|
2012-10-26 04:07:02 +08:00
|
|
|
>>> enc.feature_indices_
|
2012-10-27 02:08:42 +08:00
|
|
|
array([0, 2, 5, 9])
|
2012-10-26 04:07:02 +08:00
|
|
|
>>> enc.transform([[0, 1, 1]]).toarray()
|
2012-10-27 02:08:42 +08:00
|
|
|
array([[ 1., 0., 0., 1., 0., 0., 1., 0., 0.]])
|
2012-10-26 04:07:02 +08:00
|
|
|
|
|
|
|
|
See also
|
|
|
|
|
--------
|
2012-10-26 05:11:53 +08:00
|
|
|
LabelEncoder : performs a one-hot encoding on arbitrary class labels.
|
2012-10-26 04:57:53 +08:00
|
|
|
sklearn.feature_extraction.DictVectorizer : performs a one-hot encoding of
|
2012-11-07 20:32:16 +08:00
|
|
|
dictionary items (also handles string-valued features).
|
2012-10-26 04:07:02 +08:00
|
|
|
"""
|
2012-10-28 01:19:57 +08:00
|
|
|
def __init__(self, n_values="auto", dtype=np.float):
|
2012-10-26 04:07:02 +08:00
|
|
|
self.n_values = n_values
|
2012-10-26 05:11:53 +08:00
|
|
|
self.dtype = dtype
|
2012-10-26 04:07:02 +08:00
|
|
|
|
|
|
|
|
def fit(self, X, y=None):
|
|
|
|
|
"""Fit OneHotEncoder to X.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like, shape=(n_samples, n_feature)
|
|
|
|
|
Input array of type int.
|
2012-11-07 20:32:16 +08:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
self
|
|
|
|
|
"""
|
|
|
|
|
self.fit_transform(X)
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def fit_transform(self, X, y=None):
|
|
|
|
|
"""Fit OneHotEncoder to X, then transform X.
|
|
|
|
|
|
|
|
|
|
Equivalent to self.fit(X).transform(X), but more convenient and more
|
|
|
|
|
efficient. See fit for the parameters, transform for the return value.
|
2012-10-26 04:07:02 +08:00
|
|
|
"""
|
2012-11-14 04:45:14 +08:00
|
|
|
X = check_arrays(X, sparse_format='dense', dtype=np.int)[0]
|
|
|
|
|
if np.any(X < 0):
|
|
|
|
|
raise ValueError("X needs to contain only non-negative integers.")
|
2012-10-27 01:46:16 +08:00
|
|
|
n_samples, n_features = X.shape
|
2012-10-26 04:07:02 +08:00
|
|
|
if self.n_values == 'auto':
|
|
|
|
|
n_values = np.max(X, axis=0) + 1
|
|
|
|
|
elif isinstance(self.n_values, numbers.Integral):
|
|
|
|
|
n_values = np.empty(n_features, dtype=np.int)
|
|
|
|
|
n_values.fill(self.n_values)
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
n_values = np.asarray(self.n_values, dtype=int)
|
|
|
|
|
except (ValueError, TypeError):
|
2012-12-17 06:28:10 +08:00
|
|
|
raise TypeError("Wrong type for parameter `n_values`. Expected"
|
|
|
|
|
" 'auto', int or array of ints, got %r"
|
|
|
|
|
% type(X))
|
2012-10-26 05:57:53 +08:00
|
|
|
if n_values.ndim < 1 or n_values.shape[0] != X.shape[1]:
|
2012-12-17 06:28:10 +08:00
|
|
|
raise ValueError("Shape mismatch: if n_values is an array,"
|
|
|
|
|
" it has to be of shape (n_features,).")
|
2012-10-26 04:07:02 +08:00
|
|
|
self.n_values_ = n_values
|
|
|
|
|
n_values = np.hstack([[0], n_values])
|
2012-10-27 01:46:16 +08:00
|
|
|
indices = np.cumsum(n_values)
|
|
|
|
|
self.feature_indices_ = indices
|
|
|
|
|
|
|
|
|
|
column_indices = (X + indices[:-1]).ravel()
|
|
|
|
|
row_indices = np.repeat(np.arange(n_samples, dtype=np.int32),
|
|
|
|
|
n_features)
|
|
|
|
|
data = np.ones(n_samples * n_features)
|
|
|
|
|
out = sp.coo_matrix((data, (row_indices, column_indices)),
|
2012-12-17 06:28:10 +08:00
|
|
|
shape=(n_samples, indices[-1]),
|
|
|
|
|
dtype=self.dtype).tocsr()
|
2012-10-27 01:46:16 +08:00
|
|
|
|
2012-10-28 01:19:57 +08:00
|
|
|
if self.n_values == 'auto':
|
2012-10-27 01:46:16 +08:00
|
|
|
mask = np.array(out.sum(axis=0)).ravel() != 0
|
|
|
|
|
active_features = np.where(mask)[0]
|
|
|
|
|
out = out[:, active_features]
|
|
|
|
|
self.active_features_ = active_features
|
|
|
|
|
|
|
|
|
|
return out
|
2012-10-26 04:07:02 +08:00
|
|
|
|
|
|
|
|
def transform(self, X):
|
|
|
|
|
"""Transform X using one-hot encoding.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like, shape=(n_samples, feature_indices_[-1])
|
|
|
|
|
Input array of type int.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
X_out : sparse matrix, dtype=int
|
|
|
|
|
Transformed input.
|
|
|
|
|
"""
|
2012-11-14 04:45:14 +08:00
|
|
|
X = check_arrays(X, sparse_format='dense', dtype=np.int)[0]
|
|
|
|
|
if np.any(X < 0):
|
|
|
|
|
raise ValueError("X needs to contain only non-negative integers.")
|
2012-10-26 04:07:02 +08:00
|
|
|
n_samples, n_features = X.shape
|
|
|
|
|
|
|
|
|
|
indices = self.feature_indices_
|
|
|
|
|
if n_features != indices.shape[0] - 1:
|
2012-10-26 04:57:53 +08:00
|
|
|
raise ValueError("X has different shape than during fitting."
|
2012-10-26 04:07:02 +08:00
|
|
|
" Expected %d, got %d."
|
|
|
|
|
% (indices.shape[0] - 1, n_features))
|
2012-10-26 05:57:53 +08:00
|
|
|
|
|
|
|
|
n_values_check = np.max(X, axis=0) + 1
|
|
|
|
|
if (n_values_check > self.n_values_).any():
|
|
|
|
|
raise ValueError("Feature out of bounds. Try setting n_values.")
|
|
|
|
|
|
2012-10-26 05:11:53 +08:00
|
|
|
column_indices = (X + indices[:-1]).ravel()
|
|
|
|
|
row_indices = np.repeat(np.arange(n_samples, dtype=np.int32),
|
|
|
|
|
n_features)
|
2012-10-26 04:07:02 +08:00
|
|
|
data = np.ones(n_samples * n_features)
|
|
|
|
|
out = sp.coo_matrix((data, (row_indices, column_indices)),
|
2012-12-17 06:28:10 +08:00
|
|
|
shape=(n_samples, indices[-1]),
|
|
|
|
|
dtype=self.dtype).tocsr()
|
2012-10-28 01:19:57 +08:00
|
|
|
if self.n_values == 'auto':
|
2012-10-27 01:46:16 +08:00
|
|
|
out = out[:, self.active_features_]
|
|
|
|
|
return out
|
2012-10-26 04:07:02 +08:00
|
|
|
|
|
|
|
|
|
2012-05-09 20:24:22 +08:00
|
|
|
class LabelEncoder(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Encode labels with value between 0 and n_classes-1.
|
2012-05-07 17:27:15 +08:00
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
`classes_`: array of shape [n_class]
|
|
|
|
|
Holds the label for each class.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
2012-05-09 20:43:21 +08:00
|
|
|
`LabelEncoder` can be used to normalize labels.
|
|
|
|
|
|
2012-05-07 17:27:15 +08:00
|
|
|
>>> from sklearn import preprocessing
|
2012-05-09 20:24:22 +08:00
|
|
|
>>> le = preprocessing.LabelEncoder()
|
|
|
|
|
>>> le.fit([1, 2, 2, 6])
|
|
|
|
|
LabelEncoder()
|
|
|
|
|
>>> le.classes_
|
2012-05-07 17:27:15 +08:00
|
|
|
array([1, 2, 6])
|
2012-10-08 05:12:56 +08:00
|
|
|
>>> le.transform([1, 1, 2, 6]) #doctest: +ELLIPSIS
|
|
|
|
|
array([0, 0, 1, 2]...)
|
2012-05-09 20:24:22 +08:00
|
|
|
>>> le.inverse_transform([0, 0, 1, 2])
|
2012-05-07 17:27:15 +08:00
|
|
|
array([1, 1, 2, 6])
|
|
|
|
|
|
2012-05-09 20:43:21 +08:00
|
|
|
It can also be used to transform non-numerical labels (as long as they are
|
|
|
|
|
hashable and comparable) to numerical labels.
|
|
|
|
|
|
|
|
|
|
>>> le = preprocessing.LabelEncoder()
|
|
|
|
|
>>> le.fit(["paris", "paris", "tokyo", "amsterdam"])
|
|
|
|
|
LabelEncoder()
|
|
|
|
|
>>> list(le.classes_)
|
|
|
|
|
['amsterdam', 'paris', 'tokyo']
|
2012-10-08 05:12:56 +08:00
|
|
|
>>> le.transform(["tokyo", "tokyo", "paris"]) #doctest: +ELLIPSIS
|
|
|
|
|
array([2, 2, 1]...)
|
2012-05-09 20:43:21 +08:00
|
|
|
>>> list(le.inverse_transform([2, 2, 1]))
|
|
|
|
|
['tokyo', 'tokyo', 'paris']
|
|
|
|
|
|
2012-05-07 17:27:15 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def _check_fitted(self):
|
|
|
|
|
if not hasattr(self, "classes_"):
|
|
|
|
|
raise ValueError("LabelNormalizer was not fitted yet.")
|
|
|
|
|
|
|
|
|
|
def fit(self, y):
|
2012-08-08 17:50:37 +08:00
|
|
|
"""Fit label encoder
|
2012-05-07 17:27:15 +08:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2012-05-09 20:02:33 +08:00
|
|
|
y : array-like of shape [n_samples]
|
2012-05-07 17:27:15 +08:00
|
|
|
Target values.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
self : returns an instance of self.
|
|
|
|
|
"""
|
|
|
|
|
self.classes_ = np.unique(y)
|
|
|
|
|
return self
|
|
|
|
|
|
2012-08-08 17:50:37 +08:00
|
|
|
def fit_transform(self, y):
|
|
|
|
|
"""Fit label encoder and return encoded labels
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
y : array-like of shape [n_samples]
|
|
|
|
|
Target values.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
y : array-like of shape [n_samples]
|
|
|
|
|
"""
|
2012-08-18 00:28:39 +08:00
|
|
|
self.classes_, y = unique(y, return_inverse=True)
|
|
|
|
|
return y
|
2012-08-08 17:50:37 +08:00
|
|
|
|
2012-05-07 17:27:15 +08:00
|
|
|
def transform(self, y):
|
|
|
|
|
"""Transform labels to normalized encoding.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2012-05-09 20:02:33 +08:00
|
|
|
y : array-like of shape [n_samples]
|
2012-05-07 17:27:15 +08:00
|
|
|
Target values.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
2012-05-09 20:02:33 +08:00
|
|
|
y : array-like of shape [n_samples]
|
2012-05-07 17:27:15 +08:00
|
|
|
"""
|
|
|
|
|
self._check_fitted()
|
|
|
|
|
|
|
|
|
|
classes = np.unique(y)
|
2012-05-10 18:41:29 +08:00
|
|
|
if len(np.intersect1d(classes, self.classes_)) < len(classes):
|
|
|
|
|
diff = np.setdiff1d(classes, self.classes_)
|
|
|
|
|
raise ValueError("y contains new labels: %s" % str(diff))
|
2012-05-07 17:27:15 +08:00
|
|
|
|
2012-08-26 11:25:52 +08:00
|
|
|
return np.searchsorted(self.classes_, y)
|
2012-05-07 17:27:15 +08:00
|
|
|
|
|
|
|
|
def inverse_transform(self, y):
|
|
|
|
|
"""Transform labels back to original encoding.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
y : numpy array of shape [n_samples]
|
|
|
|
|
Target values.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
y : numpy array of shape [n_samples]
|
|
|
|
|
"""
|
|
|
|
|
self._check_fitted()
|
|
|
|
|
|
|
|
|
|
y = np.asarray(y)
|
2012-08-17 23:31:53 +08:00
|
|
|
return self.classes_[y]
|
2012-05-07 17:27:15 +08:00
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
class LabelBinarizer(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Binarize labels in a one-vs-all fashion
|
|
|
|
|
|
|
|
|
|
Several regression and binary classification algorithms are
|
|
|
|
|
available in the scikit. A simple way to extend these algorithms
|
|
|
|
|
to the multi-class classification case is to use the so-called
|
|
|
|
|
one-vs-all scheme.
|
|
|
|
|
|
|
|
|
|
At learning time, this simply consists in learning one regressor
|
|
|
|
|
or binary classifier per class. In doing so, one needs to convert
|
|
|
|
|
multi-class labels to binary labels (belong or does not belong
|
|
|
|
|
to the class). LabelBinarizer makes this process easy with the
|
|
|
|
|
transform method.
|
|
|
|
|
|
|
|
|
|
At prediction time, one assigns the class for which the corresponding
|
|
|
|
|
model gave the greatest confidence. LabelBinarizer makes this easy
|
|
|
|
|
with the inverse_transform method.
|
|
|
|
|
|
2012-02-28 15:27:46 +08:00
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
neg_label: int (default: 0)
|
|
|
|
|
Value with which negative labels must be encoded.
|
|
|
|
|
|
|
|
|
|
pos_label: int (default: 1)
|
|
|
|
|
Value with which positive labels must be encoded.
|
|
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
`classes_`: array of shape [n_class]
|
|
|
|
|
Holds the label for each class.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
>>> from sklearn import preprocessing
|
2012-05-07 18:04:16 +08:00
|
|
|
>>> lb = preprocessing.LabelBinarizer()
|
|
|
|
|
>>> lb.fit([1, 2, 6, 4, 2])
|
2012-02-28 15:27:46 +08:00
|
|
|
LabelBinarizer(neg_label=0, pos_label=1)
|
2012-05-07 18:04:16 +08:00
|
|
|
>>> lb.classes_
|
2012-01-21 04:29:42 +08:00
|
|
|
array([1, 2, 4, 6])
|
2012-05-07 18:04:16 +08:00
|
|
|
>>> lb.transform([1, 6])
|
2012-07-25 15:32:50 +08:00
|
|
|
array([[1, 0, 0, 0],
|
|
|
|
|
[0, 0, 0, 1]])
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-05-07 18:04:16 +08:00
|
|
|
>>> lb.fit_transform([(1, 2), (3,)])
|
2012-07-25 15:32:50 +08:00
|
|
|
array([[1, 1, 0],
|
|
|
|
|
[0, 0, 1]])
|
2012-05-07 18:04:16 +08:00
|
|
|
>>> lb.classes_
|
2012-01-21 04:29:42 +08:00
|
|
|
array([1, 2, 3])
|
|
|
|
|
"""
|
|
|
|
|
|
2012-02-28 15:27:46 +08:00
|
|
|
def __init__(self, neg_label=0, pos_label=1):
|
2012-02-28 15:56:25 +08:00
|
|
|
if neg_label >= pos_label:
|
|
|
|
|
raise ValueError("neg_label must be strictly less than pos_label.")
|
2012-02-28 15:44:42 +08:00
|
|
|
|
2012-02-28 15:27:46 +08:00
|
|
|
self.neg_label = neg_label
|
|
|
|
|
self.pos_label = pos_label
|
|
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
def _check_fitted(self):
|
|
|
|
|
if not hasattr(self, "classes_"):
|
|
|
|
|
raise ValueError("LabelBinarizer was not fitted yet.")
|
|
|
|
|
|
|
|
|
|
def fit(self, y):
|
|
|
|
|
"""Fit label binarizer
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
y : numpy array of shape [n_samples] or sequence of sequences
|
|
|
|
|
Target values. In the multilabel case the nested sequences can
|
|
|
|
|
have variable lengths.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
self : returns an instance of self.
|
|
|
|
|
"""
|
|
|
|
|
self.multilabel = _is_multilabel(y)
|
|
|
|
|
if self.multilabel:
|
|
|
|
|
self.indicator_matrix_ = _is_label_indicator_matrix(y)
|
|
|
|
|
if self.indicator_matrix_:
|
|
|
|
|
self.classes_ = np.arange(y.shape[1])
|
|
|
|
|
else:
|
|
|
|
|
self.classes_ = np.array(sorted(set.union(*map(set, y))))
|
|
|
|
|
else:
|
|
|
|
|
self.classes_ = np.unique(y)
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def transform(self, y):
|
|
|
|
|
"""Transform multi-class labels to binary labels
|
|
|
|
|
|
|
|
|
|
The output of transform is sometimes referred to by some authors as the
|
|
|
|
|
1-of-K coding scheme.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
y : numpy array of shape [n_samples] or sequence of sequences
|
|
|
|
|
Target values. In the multilabel case the nested sequences can
|
|
|
|
|
have variable lengths.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
Y : numpy array of shape [n_samples, n_classes]
|
|
|
|
|
"""
|
|
|
|
|
self._check_fitted()
|
|
|
|
|
|
|
|
|
|
if self.multilabel or len(self.classes_) > 2:
|
|
|
|
|
if _is_label_indicator_matrix(y):
|
|
|
|
|
# nothing to do as y is already a label indicator matrix
|
|
|
|
|
return y
|
|
|
|
|
|
2012-07-25 06:17:37 +08:00
|
|
|
Y = np.zeros((len(y), len(self.classes_)), dtype=np.int)
|
2012-01-21 04:29:42 +08:00
|
|
|
else:
|
2012-07-25 06:17:37 +08:00
|
|
|
Y = np.zeros((len(y), 1), dtype=np.int)
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-02-28 15:27:46 +08:00
|
|
|
Y += self.neg_label
|
|
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
y_is_multilabel = _is_multilabel(y)
|
|
|
|
|
|
|
|
|
|
if y_is_multilabel and not self.multilabel:
|
2012-12-17 06:28:10 +08:00
|
|
|
raise ValueError("The object was not fitted with multilabel"
|
|
|
|
|
" input!")
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
elif self.multilabel:
|
|
|
|
|
if not _is_multilabel(y):
|
|
|
|
|
raise ValueError("y should be a list of label lists/tuples,"
|
|
|
|
|
"got %r" % (y,))
|
|
|
|
|
|
|
|
|
|
# inverse map: label => column index
|
|
|
|
|
imap = dict((v, k) for k, v in enumerate(self.classes_))
|
|
|
|
|
|
|
|
|
|
for i, label_tuple in enumerate(y):
|
|
|
|
|
for label in label_tuple:
|
2012-02-28 15:27:46 +08:00
|
|
|
Y[i, imap[label]] = self.pos_label
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
return Y
|
|
|
|
|
|
2012-05-24 22:08:07 +08:00
|
|
|
else:
|
|
|
|
|
y = np.asarray(y)
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-05-24 22:08:07 +08:00
|
|
|
if len(self.classes_) == 2:
|
|
|
|
|
Y[y == self.classes_[1], 0] = self.pos_label
|
|
|
|
|
return Y
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-05-24 22:08:07 +08:00
|
|
|
elif len(self.classes_) >= 2:
|
|
|
|
|
for i, k in enumerate(self.classes_):
|
|
|
|
|
Y[y == k, i] = self.pos_label
|
|
|
|
|
return Y
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
# Only one class, returns a matrix with all negative labels.
|
|
|
|
|
return Y
|
2012-01-21 04:29:42 +08:00
|
|
|
|
2012-02-28 15:44:42 +08:00
|
|
|
def inverse_transform(self, Y, threshold=None):
|
2012-01-21 04:29:42 +08:00
|
|
|
"""Transform binary labels back to multi-class labels
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
Y : numpy array of shape [n_samples, n_classes]
|
|
|
|
|
Target values.
|
|
|
|
|
|
2012-02-28 15:44:42 +08:00
|
|
|
threshold : float or None
|
2012-01-21 04:29:42 +08:00
|
|
|
Threshold used in the binary and multi-label cases.
|
2012-02-28 15:44:42 +08:00
|
|
|
|
|
|
|
|
Use 0 when:
|
|
|
|
|
- Y contains the output of decision_function (classifier)
|
|
|
|
|
Use 0.5 when:
|
|
|
|
|
- Y contains the output of predict_proba
|
|
|
|
|
|
|
|
|
|
If None, the threshold is assumed to be half way between
|
|
|
|
|
neg_label and pos_label.
|
2012-01-21 04:29:42 +08:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
y : numpy array of shape [n_samples] or sequence of sequences
|
|
|
|
|
Target values. In the multilabel case the nested sequences can
|
|
|
|
|
have variable lengths.
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
In the case when the binary labels are fractional
|
|
|
|
|
(probabilistic), inverse_transform chooses the class with the
|
|
|
|
|
greatest value. Typically, this allows to use the output of a
|
|
|
|
|
linear model's decision_function method directly as the input
|
|
|
|
|
of inverse_transform.
|
|
|
|
|
"""
|
|
|
|
|
self._check_fitted()
|
|
|
|
|
|
2012-02-28 15:44:42 +08:00
|
|
|
if threshold is None:
|
|
|
|
|
half = (self.pos_label - self.neg_label) / 2.0
|
|
|
|
|
threshold = self.neg_label + half
|
|
|
|
|
|
2012-01-21 04:29:42 +08:00
|
|
|
if self.multilabel:
|
|
|
|
|
Y = np.array(Y > threshold, dtype=int)
|
|
|
|
|
# Return the predictions in the same format as in fit
|
|
|
|
|
if self.indicator_matrix_:
|
|
|
|
|
# Label indicator matrix format
|
|
|
|
|
return Y
|
|
|
|
|
else:
|
|
|
|
|
# Lists of tuples format
|
|
|
|
|
return [tuple(self.classes_[np.flatnonzero(Y[i])])
|
|
|
|
|
for i in range(Y.shape[0])]
|
|
|
|
|
|
|
|
|
|
if len(Y.shape) == 1 or Y.shape[1] == 1:
|
|
|
|
|
y = np.array(Y.ravel() > threshold, dtype=int)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
y = Y.argmax(axis=1)
|
|
|
|
|
|
|
|
|
|
return self.classes_[y]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class KernelCenterer(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Center a kernel matrix
|
|
|
|
|
|
2012-11-19 15:53:03 +08:00
|
|
|
Let K(x_i, x_j) be a kernel defined by K(x_i, x_j) = phi(x_i)^T phi(x_j),
|
2012-12-17 06:28:10 +08:00
|
|
|
where phi(x) is a function mapping x to a hilbert space. KernelCenterer is
|
|
|
|
|
a class to center (i.e., normalize to have zero-mean) the data without
|
|
|
|
|
explicitly computing phi(x). It is equivalent equivalent to centering
|
|
|
|
|
phi(x) with sklearn.preprocessing.StandardScaler(with_std=False).
|
2012-01-21 04:29:42 +08:00
|
|
|
"""
|
|
|
|
|
|
2012-08-26 22:00:53 +08:00
|
|
|
def fit(self, K, y=None):
|
2012-01-21 04:29:42 +08:00
|
|
|
"""Fit KernelCenterer
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
K : numpy array of shape [n_samples, n_samples]
|
|
|
|
|
Kernel matrix.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
self : returns an instance of self.
|
|
|
|
|
"""
|
2012-08-26 22:00:53 +08:00
|
|
|
K = array2d(K)
|
2012-01-21 04:29:42 +08:00
|
|
|
n_samples = K.shape[0]
|
|
|
|
|
self.K_fit_rows_ = np.sum(K, axis=0) / n_samples
|
|
|
|
|
self.K_fit_all_ = self.K_fit_rows_.sum() / n_samples
|
|
|
|
|
return self
|
|
|
|
|
|
2012-08-26 22:00:53 +08:00
|
|
|
def transform(self, K, y=None, copy=True):
|
2012-01-21 04:29:42 +08:00
|
|
|
"""Center kernel
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
K : numpy array of shape [n_samples1, n_samples2]
|
|
|
|
|
Kernel matrix.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
K_new : numpy array of shape [n_samples1, n_samples2]
|
|
|
|
|
"""
|
2012-08-26 22:00:53 +08:00
|
|
|
K = array2d(K)
|
2012-01-21 04:29:42 +08:00
|
|
|
if copy:
|
|
|
|
|
K = K.copy()
|
|
|
|
|
|
|
|
|
|
K_pred_cols = (np.sum(K, axis=1) /
|
|
|
|
|
self.K_fit_rows_.shape[0])[:, np.newaxis]
|
|
|
|
|
|
|
|
|
|
K -= self.K_fit_rows_
|
|
|
|
|
K -= K_pred_cols
|
|
|
|
|
K += self.K_fit_all_
|
|
|
|
|
|
|
|
|
|
return K
|
2012-11-19 17:18:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_dummy_feature(X, value=1.0):
|
|
|
|
|
"""Augment dataset with an additional dummy feature.
|
|
|
|
|
|
|
|
|
|
This is useful for fitting an intercept term with implementations which
|
|
|
|
|
cannot otherwise fit it directly.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array or scipy.sparse matrix with shape [n_samples, n_features]
|
|
|
|
|
Data.
|
|
|
|
|
|
|
|
|
|
value : float
|
|
|
|
|
Value to use for the dummy feature.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
|
|
|
|
|
X : array or scipy.sparse matrix with shape [n_samples, n_features + 1]
|
2012-11-22 01:58:20 +08:00
|
|
|
Same data with dummy feature added as first column.
|
|
|
|
|
|
|
|
|
|
Example
|
|
|
|
|
--------
|
|
|
|
|
|
|
|
|
|
>>> from sklearn.preprocessing import add_dummy_feature
|
|
|
|
|
>>> add_dummy_feature([[0, 1], [1, 0]])
|
|
|
|
|
array([[ 1., 0., 1.],
|
|
|
|
|
[ 1., 1., 0.]])
|
2012-11-19 17:18:59 +08:00
|
|
|
"""
|
|
|
|
|
X = safe_asarray(X)
|
|
|
|
|
n_samples, n_features = X.shape
|
|
|
|
|
shape = (n_samples, n_features + 1)
|
|
|
|
|
if sp.issparse(X):
|
|
|
|
|
if sp.isspmatrix_coo(X):
|
2012-11-19 22:33:15 +08:00
|
|
|
# Shift columns to the right.
|
2012-11-19 17:18:59 +08:00
|
|
|
col = X.col + 1
|
2012-11-19 22:33:15 +08:00
|
|
|
# Column indices of dummy feature are 0 everywhere.
|
2012-11-19 17:18:59 +08:00
|
|
|
col = np.concatenate((np.zeros(n_samples), col))
|
2012-11-19 22:33:15 +08:00
|
|
|
# Row indices of dummy feature are 0, ..., n_samples-1.
|
2012-11-19 17:18:59 +08:00
|
|
|
row = np.concatenate((np.arange(n_samples), X.row))
|
2012-11-19 22:33:15 +08:00
|
|
|
# Prepend the dummy feature n_samples times.
|
2012-11-19 17:18:59 +08:00
|
|
|
data = np.concatenate((np.ones(n_samples) * value, X.data))
|
|
|
|
|
return sp.coo_matrix((data, (row, col)), shape)
|
|
|
|
|
elif sp.isspmatrix_csc(X):
|
2012-11-19 22:33:15 +08:00
|
|
|
# Shift index pointers since we need to add n_samples elements.
|
2012-11-19 17:18:59 +08:00
|
|
|
indptr = X.indptr + n_samples
|
2012-11-19 22:33:15 +08:00
|
|
|
# indptr[0] must be 0.
|
2012-11-19 17:18:59 +08:00
|
|
|
indptr = np.concatenate((np.array([0]), indptr))
|
2012-11-19 22:33:15 +08:00
|
|
|
# Row indices of dummy feature are 0, ..., n_samples-1.
|
2012-11-19 17:18:59 +08:00
|
|
|
indices = np.concatenate((np.arange(n_samples), X.indices))
|
2012-11-19 22:33:15 +08:00
|
|
|
# Prepend the dummy feature n_samples times.
|
2012-11-19 17:18:59 +08:00
|
|
|
data = np.concatenate((np.ones(n_samples) * value, X.data))
|
|
|
|
|
return sp.csc_matrix((data, indices, indptr), shape)
|
|
|
|
|
else:
|
|
|
|
|
klass = X.__class__
|
|
|
|
|
X = klass(add_dummy_feature(X.tocoo(), value))
|
|
|
|
|
return klass(X)
|
|
|
|
|
else:
|
|
|
|
|
return np.hstack((np.ones((n_samples, 1)) * value, X))
|
2012-12-23 20:13:21 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def balance_weights(y):
|
|
|
|
|
"""Compute sample weights such that the class distribution of y becomes
|
|
|
|
|
balanced.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
y : array-like
|
|
|
|
|
Labels for the samples.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
weights : array-like
|
|
|
|
|
The sample weights.
|
|
|
|
|
"""
|
|
|
|
|
y = safe_asarray(y)
|
|
|
|
|
y = np.searchsorted(np.unique(y), y)
|
|
|
|
|
bins = np.bincount(y)
|
|
|
|
|
|
|
|
|
|
weights = 1. / bins.take(y)
|
|
|
|
|
weights *= bins.min()
|
|
|
|
|
|
|
|
|
|
return weights
|