From f8d7bd43fd2dfae3be938ac8561663f0d7c8fe72 Mon Sep 17 00:00:00 2001 From: Lars Buitinck Date: Wed, 7 Nov 2012 13:32:16 +0100 Subject: [PATCH] ENH OneHotEncoder docs + TypeError + test active_features_ --- doc/modules/preprocessing.rst | 11 +++--- sklearn/preprocessing.py | 53 +++++++++++++++++------------ sklearn/tests/test_preprocessing.py | 6 ++-- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/doc/modules/preprocessing.rst b/doc/modules/preprocessing.rst index 37812a0595a..47874cc130f 100644 --- a/doc/modules/preprocessing.rst +++ b/doc/modules/preprocessing.rst @@ -258,13 +258,16 @@ to be used when the transformer API is not necessary. representation upstream. -Encoding Categorial Features -============================ +Encoding categorical features +============================= Often features are not given as continuous values but categorical. -For example a person could have features ``["male", "female"]``, ``["from Europe", "from US", "from Asia"]``, +For example a person could have features ``["male", "female"]``, +``["from Europe", "from US", "from Asia"]``, ``["uses Firefox", "uses Chrome", "uses Safari", "uses Internet Explorer"]``. Such features can be efficiently coded as integers, for instance -``["male", "from US", "uses Internet Explorer"]`` could be expressed as ``[0, 1, 3]``. +``["male", "from US", "uses Internet Explorer"]`` could be expressed as +``[0, 1, 3]`` while ``["female", "from Asia", "uses Chrome"]`` would be +``[1, 2, 1]``. Such integer representation can not be used directly with scikit-learn estimators, as these expect continuous input, and would interpret the categories as being ordered, which is often diff --git a/sklearn/preprocessing.py b/sklearn/preprocessing.py index 564bff277e9..e3a4cd07a67 100644 --- a/sklearn/preprocessing.py +++ b/sklearn/preprocessing.py @@ -608,41 +608,43 @@ def _is_multilabel(y): class OneHotEncoder(BaseEstimator, TransformerMixin): """Encode categorical integer features using a one-hot aka one-of-K scheme. - The input is assumed to be integer and name categorical - features numbered from ``0`` to ``n_values - 1``. + 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. Parameters ---------- n_values : 'auto', int or array of int Number of values per feature. - 'auto' : determine feature range from training data. + 'auto' : determine value range from training data. int : maximum value for all features. array : maximum value per feature. dtype : number type, default=np.float Desired dtype of output. - Attributes ---------- `active_features_` : array - Indices for active features, meaning values that - actually occur in the training dataset. Only available - if n_values is ``'auto'``. + Indices for active features, meaning values that actually occur in the + training set. Only available when n_values is ``'auto'``. `feature_indices_` : array of shape (n_features,) - Indices to feature ranges. Feature ``i`` in the - original data is mapped to features - ``feature_indices_[i]`` to ``feature_indices_[i+1]`` + Indices to feature ranges. Feature ``i`` in the original data is mapped + to features ``feature_indices_[i]`` to ``feature_indices_[i+1]`` (and potentially masked by `active_features_` afterwards) `n_values_` : array of shape (n_features,) Maximum number of values per feature. - Examples -------- - Given a dataset with three features and two - data points, we find the maximum value per feature - and transform the data to a binary one-hot encoding. + 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. >>> from sklearn.preprocessing import OneHotEncoder >>> enc = OneHotEncoder() @@ -659,23 +661,32 @@ class OneHotEncoder(BaseEstimator, TransformerMixin): -------- LabelEncoder : performs a one-hot encoding on arbitrary class labels. sklearn.feature_extraction.DictVectorizer : performs a one-hot encoding of - dictionary items. + dictionary items (also handles string-valued features). """ def __init__(self, n_values="auto", dtype=np.float): self.n_values = n_values self.dtype = dtype def fit(self, X, y=None): - self.fit_transform(X) - return self - - def fit_transform(self, X, y=None): """Fit OneHotEncoder to X. Parameters ---------- X : array-like, shape=(n_samples, n_feature) Input array of type int. + + 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. """ X, = check_arrays(X, sparse_format='dense', dtype=np.int) n_samples, n_features = X.shape @@ -688,8 +699,8 @@ class OneHotEncoder(BaseEstimator, TransformerMixin): try: n_values = np.asarray(self.n_values, dtype=int) except (ValueError, TypeError): - raise ValueError("Wrong type for parameter `n_values`." - " Expected 'auto', int or array of ints, got %s" + raise TypeError("Wrong type for parameter `n_values`." + " Expected 'auto', int or array of ints, got %r" % type(X)) if n_values.ndim < 1 or n_values.shape[0] != X.shape[1]: raise ValueError("Shape mismatch: if n_values is " diff --git a/sklearn/tests/test_preprocessing.py b/sklearn/tests/test_preprocessing.py index 9a82b05bd5b..4795de777de 100644 --- a/sklearn/tests/test_preprocessing.py +++ b/sklearn/tests/test_preprocessing.py @@ -456,6 +456,8 @@ def test_one_hot_encoder(): # discover max values automatically X_trans = enc.fit_transform(X).toarray() assert_equal(X_trans.shape, (2, 5)) + assert_array_equal(enc.active_features_, + np.where([1, 0, 0, 1, 0, 1, 1, 0, 1])[0]) assert_array_equal(enc.feature_indices_, [0, 4, 7, 9]) # check outcome @@ -488,8 +490,8 @@ def test_one_hot_encoder(): # test that error is raised when wrong number of features in fit # with prespecified n_values assert_raises(ValueError, enc.fit, X[:, :-1]) - # test value error on wrong init param - assert_raises(ValueError, OneHotEncoder(n_values=np.int).fit, X) + # test exception on wrong init param + assert_raises(TypeError, OneHotEncoder(n_values=np.int).fit, X) def test_label_encoder():