web-dev-qa-db-fra.com

fit_transform () prend 2 arguments de position mais 3 ont été donnés avec LabelBinarizer

Je suis totalement novice en apprentissage machine et je travaille avec une technique d'apprentissage non supervisée.

L'image montre mes exemples de données (après tout le nettoyage) Capture d'écran: Exemple de données

J'ai deux Pipline construit pour nettoyer les données:

num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]

print(type(num_attribs))

num_pipeline = Pipeline([
    ('selector', DataFrameSelector(num_attribs)),
    ('imputer', Imputer(strategy="median")),
    ('attribs_adder', CombinedAttributesAdder()),
    ('std_scaler', StandardScaler()),
])

cat_pipeline = Pipeline([
    ('selector', DataFrameSelector(cat_attribs)),
    ('label_binarizer', LabelBinarizer())
])

Ensuite, j'ai fait l'union de ces deux pipelines et le code correspondant est affiché ci-dessous:

from sklearn.pipeline import FeatureUnion

full_pipeline = FeatureUnion(transformer_list=[
        ("num_pipeline", num_pipeline),
        ("cat_pipeline", cat_pipeline),
    ])

Maintenant j'essaye de faire fit_transform sur le Data mais sa montre moi l'erreur.

Code de transformation:

housing_prepared = full_pipeline.fit_transform(housing)
housing_prepared

Message d'erreur: Fit_transform () prend 2 arguments de position mais 3 ont été donnés

45
Viral Parmar

Le problème:

Le pipeline suppose que la méthode fit_transform de LabelBinarizer est définie pour prendre trois arguments de position: 

def fit_transform(self, x, y)
    ...rest of the code

alors qu'il est défini pour ne prendre que deux: 

def fit_transform(self, x):
    ...rest of the code

Solution possible:

Cela peut être résolu en créant un transformateur personnalisé pouvant gérer 3 arguments de position:

  1. Importer et créer une nouvelle classe:

    from sklearn.base import TransformerMixin #gives fit_transform method for free
    class MyLabelBinarizer(TransformerMixin):
        def __init__(self, *args, **kwargs):
            self.encoder = LabelBinarizer(*args, **kwargs)
        def fit(self, x, y=0):
            self.encoder.fit(x)
            return self
        def transform(self, x, y=0):
            return self.encoder.transform(x)
    
  2. Gardez votre code identique au lieu d'utiliser LabelBinarizer (), utilisez la classe que nous avons créée: MyLabelBinarizer ().


Remarque: Si vous souhaitez accéder aux attributs LabelBinarizer (par exemple, classes_), ajoutez la ligne suivante à la méthode fit:

    self.classes_, self.y_type_, self.sparse_input_ = self.encoder.classes_, self.encoder.y_type_, self.encoder.sparse_input_
47
Zaid E.

Je crois que votre exemple est tiré du livre Hands-On Machine Learning avec Scikit-Learn & TensorFlow. Malheureusement, j'ai rencontré ce problème aussi. Une modification récente de scikit-learn (0.19.0) a modifié la méthode LabelBinarizer de fit_transform. Malheureusement, LabelBinarizer n'a jamais été conçu pour que cet exemple l'utilise. Vous pouvez voir des informations sur le changement ici et ici .

Jusqu'à ce qu'ils trouvent une solution à cela, vous pouvez installer la version précédente (0.18.0) comme suit:


$ pip install scikit-learn==0.18.0

Après avoir exécuté cela, votre code devrait être exécuté sans problème.

À l'avenir, il semblerait que la solution correcte consisterait à utiliser une classe CategoricalEncoder ou quelque chose de similaire. Ils tentent apparemment de résoudre ce problème depuis des années. Vous pouvez voir la nouvelle classe ici et une discussion plus approfondie du problème ici .

42
Steven Oxley

LabelBinarizer n'autorisant pas plus de 2 arguments de position, vous devez créer votre binariseur personnalisé comme

class CustomLabelBinarizer(BaseEstimator, TransformerMixin):
    def __init__(self, sparse_output=False):
        self.sparse_output = sparse_output
    def fit(self, X, y=None):
        return self
    def transform(self, X, y=None):
        enc = LabelBinarizer(sparse_output=self.sparse_output)
        return enc.fit_transform(X)

num_attribs = list(housing_num)
cat_attribs = ['ocean_proximity']

num_pipeline = Pipeline([
    ('selector', DataFrameSelector(num_attribs)),
    ('imputer', Imputer(strategy='median')),
    ('attribs_adder', CombinedAttributesAdder()),
    ('std_scalar', StandardScaler())
])

cat_pipeline = Pipeline([
    ('selector', DataFrameSelector(cat_attribs)),
    ('label_binarizer', CustomLabelBinarizer())
])

full_pipeline = FeatureUnion(transformer_list=[
    ('num_pipeline', num_pipeline),
    ('cat_pipeline', cat_pipeline)
])

housing_prepared = full_pipeline.fit_transform(new_housing)
7
shyam padia

J'ai rencontré le même problème et je l'ai obtenu en appliquant la solution de contournement spécifiée dans le rapport Github du livre .

Avertissement: les versions précédentes du livre utilisaient la classe LabelBinarizer à ce point. Encore une fois, c'était incorrect: tout comme le LabelEncoder classe, la classe LabelBinarizer a été conçue pour pré-traiter les étiquettes, pas caractéristiques d'entrée. Une meilleure solution consiste à utiliser la prochaine version de Scikit-Learn Classe CategoricalEncoder: elle sera bientôt ajoutée à Scikit-Learn et à entre-temps, vous pouvez utiliser le code ci-dessous (copié à partir de Pull Request # 9151 ).

Pour contourner le problème, voici la solution: collez-le et exécutez-le dans une cellule précédente:

# Definition of the CategoricalEncoder class, copied from PR #9151.
# Just run this cell, or copy it to your code, do not try to understand it (yet).

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils import check_array
from sklearn.preprocessing import LabelEncoder
from scipy import sparse

class CategoricalEncoder(BaseEstimator, TransformerMixin):
    def __init__(self, encoding='onehot', categories='auto', dtype=np.float64,
                 handle_unknown='error'):
        self.encoding = encoding
        self.categories = categories
        self.dtype = dtype
        self.handle_unknown = handle_unknown

    def fit(self, X, y=None):
        """Fit the CategoricalEncoder to X.
        Parameters
        ----------
        X : array-like, shape [n_samples, n_feature]
            The data to determine the categories of each feature.
        Returns
        -------
        self
        """

        if self.encoding not in ['onehot', 'onehot-dense', 'ordinal']:
            template = ("encoding should be either 'onehot', 'onehot-dense' "
                        "or 'ordinal', got %s")
            raise ValueError(template % self.handle_unknown)

        if self.handle_unknown not in ['error', 'ignore']:
            template = ("handle_unknown should be either 'error' or "
                        "'ignore', got %s")
            raise ValueError(template % self.handle_unknown)

        if self.encoding == 'ordinal' and self.handle_unknown == 'ignore':
            raise ValueError("handle_unknown='ignore' is not supported for"
                             " encoding='ordinal'")

        X = check_array(X, dtype=np.object, accept_sparse='csc', copy=True)
        n_samples, n_features = X.shape

        self._label_encoders_ = [LabelEncoder() for _ in range(n_features)]

        for i in range(n_features):
            le = self._label_encoders_[i]
            Xi = X[:, i]
            if self.categories == 'auto':
                le.fit(Xi)
            else:
                valid_mask = np.in1d(Xi, self.categories[i])
                if not np.all(valid_mask):
                    if self.handle_unknown == 'error':
                        diff = np.unique(Xi[~valid_mask])
                        msg = ("Found unknown categories {0} in column {1}"
                               " during fit".format(diff, i))
                        raise ValueError(msg)
                le.classes_ = np.array(np.sort(self.categories[i]))

        self.categories_ = [le.classes_ for le in self._label_encoders_]

        return self

    def transform(self, X):
        """Transform X using one-hot encoding.
        Parameters
        ----------
        X : array-like, shape [n_samples, n_features]
            The data to encode.
        Returns
        -------
        X_out : sparse matrix or a 2-d array
            Transformed input.
        """
        X = check_array(X, accept_sparse='csc', dtype=np.object, copy=True)
        n_samples, n_features = X.shape
        X_int = np.zeros_like(X, dtype=np.int)
        X_mask = np.ones_like(X, dtype=np.bool)

        for i in range(n_features):
            valid_mask = np.in1d(X[:, i], self.categories_[i])

            if not np.all(valid_mask):
                if self.handle_unknown == 'error':
                    diff = np.unique(X[~valid_mask, i])
                    msg = ("Found unknown categories {0} in column {1}"
                           " during transform".format(diff, i))
                    raise ValueError(msg)
                else:
                    # Set the problematic rows to an acceptable value and
                    # continue `The rows are marked `X_mask` and will be
                    # removed later.
                    X_mask[:, i] = valid_mask
                    X[:, i][~valid_mask] = self.categories_[i][0]
            X_int[:, i] = self._label_encoders_[i].transform(X[:, i])

        if self.encoding == 'ordinal':
            return X_int.astype(self.dtype, copy=False)

        mask = X_mask.ravel()
        n_values = [cats.shape[0] for cats in self.categories_]
        n_values = np.array([0] + n_values)
        indices = np.cumsum(n_values)

        column_indices = (X_int + indices[:-1]).ravel()[mask]
        row_indices = np.repeat(np.arange(n_samples, dtype=np.int32),
                                n_features)[mask]
        data = np.ones(n_samples * n_features)[mask]

        out = sparse.csc_matrix((data, (row_indices, column_indices)),
                                shape=(n_samples, indices[-1]),
                                dtype=self.dtype).tocsr()
        if self.encoding == 'onehot-dense':
            return out.toarray()
        else:
            return out
5
Rui Liu

Simplement, vous pouvez définir la classe suivante juste avant votre pipeline:

class NewLabelBinarizer(LabelBinarizer):
    def fit(self, X, y=None):
        return super(NewLabelBinarizer, self).fit(X)
    def transform(self, X, y=None):
        return super(NewLabelBinarizer, self).transform(X)
    def fit_transform(self, X, y=None):
        return super(NewLabelBinarizer, self).fit(X).transform(X)

Ensuite, le reste du code est semblable à celui mentionné dans le livre avec une modification minime dans cat_pipeline avant la concaténation de pipeline - procédez comme suit:

cat_pipeline = Pipeline([
    ("selector", DataFrameSelector(cat_attribs)),
    ("label_binarizer", NewLabelBinarizer())])

Vous avez fait!

2
Navid

J'ai eu le même problème et résolu en utilisant DataFrameMapper (besoin d'installer sklearn_pandas):

from sklearn_pandas import DataFrameMapper
cat_pipeline = Pipeline([
    ('label_binarizer', DataFrameMapper([(cat_attribs, LabelBinarizer())])),
])
1
user3871008

Je pense que vous êtes en train de parcourir les exemples du livre: Apprentissage automatique avec Scikit Learn et Tensorflow . J'ai rencontré le même problème en reprenant l'exemple du chapitre 2. 

Comme mentionné par d'autres personnes, le problème est lié à LabelBinarizer de Sklearn. Sa méthode fit_transform nécessite moins d'arguments que d'autres transformateurs en cours de développement. (seulement y lorsque d'autres transformateurs prennent normalement X et y, voir ici pour plus de détails). C'est pourquoi, lorsque nous exécutons pipeline.fit_transform, nous avons introduit plus d'arguments dans ce transformateur que nécessaire.

Une solution simple que j'ai utilisée consiste simplement à utiliser OneHotEncoder et à définir la valeur "sparse" sur False pour garantir que la sortie est un tableau numpy identique à la sortie num_pipeline. (De cette façon, vous n'avez pas besoin de coder votre propre encodeur personnalisé)

votre cat_pipeline d'origine:

cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('label_binarizer', LabelBinarizer())
])

vous pouvez simplement changer cette partie en:

cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('one_hot_encoder', OneHotEncoder(sparse=False))
])

Vous pouvez partir d'ici et tout devrait fonctionner.

0
Norman Yan

J'ai fini par rouler le mien 

class LabelBinarizer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        X = self.prep(X)
        unique_vals = []
        for column in X.T:
            unique_vals.append(np.unique(column))
        self.unique_vals = unique_vals
    def transform(self, X, y=None):
        X = self.prep(X)
        unique_vals = self.unique_vals
        new_columns = []
        for i, column in enumerate(X.T):
            num_uniq_vals = len(unique_vals[i])
            encoder_ring = dict(Zip(unique_vals[i], range(len(unique_vals[i]))))
            f = lambda val: encoder_ring[val]
            f = np.vectorize(f, otypes=[np.int])
            new_column = np.array([f(column)])
            if num_uniq_vals <= 2:
                new_columns.append(new_column)
            else:
                one_hots = np.zeros([num_uniq_vals, len(column)], np.int)
                one_hots[new_column, range(len(column))]=1
                new_columns.append(one_hots)
        new_columns = np.concatenate(new_columns, axis=0).T        
        return new_columns

    def fit_transform(self, X, y=None):
        self.fit(X)
        return self.transform(X)

    @staticmethod
    def prep(X):
        shape = X.shape
        if len(shape) == 1:
            X = X.values.reshape(shape[0], 1)
        return X

Semble travailler

lbn = LabelBinarizer()
thingy = np.array([['male','male','female', 'male'], ['A', 'B', 'A', 'C']]).T
lbn.fit(thingy)
lbn.transform(thingy)

résultats

array([[1, 1, 0, 0],
       [1, 0, 1, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 1]])
0
JnBrymn

Vous pouvez créer un autre Transformer personnalisé qui effectue l’encodage à votre place.

class CustomLabelEncode(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return LabelEncoder().fit_transform(X);

Dans cet exemple, nous avons fait LabelEncoding mais vous pouvez aussi utiliser LabelBinarizer

0
Rupinder Singh

Oubliez LaberBinarizer et utilisez plutôt OneHotEncoder. 

Si vous utilisez un LabelEncoder avant OneHotEncoder pour convertir des catégories en entiers, vous pouvez maintenant utiliser directement OneHotEncoder.

0
blacksheep