scikit learn - scikit - fit_transform() toma 2 argumentos posicionales pero 3 se dieron con LabelBinarizer
onehotencoder pandas (11)
Como LabelBinarizer no permite más de 2 argumentos posicionales, debe crear su binarizador personalizado como
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)
Soy totalmente nuevo en Machine Learning y he estado trabajando con técnicas de aprendizaje sin supervisión.
La imagen muestra mis datos de muestra (después de toda limpieza) Captura de pantalla: Datos de muestra
Tengo estos dos Pipline construidos para limpiar los datos:
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())
])
Luego hice la unión de estas dos tuberías y el código de la misma se muestra a continuación:
from sklearn.pipeline import FeatureUnion
full_pipeline = FeatureUnion(transformer_list=[
("num_pipeline", num_pipeline),
("cat_pipeline", cat_pipeline),
])
Ahora estoy tratando de hacer fit_transform en los Data pero me muestra el error.
Código para la transformación:
housing_prepared = full_pipeline.fit_transform(housing)
housing_prepared
Mensaje de error: fit_transform () toma 2 argumentos posicionales pero se dieron 3
Creo que está analizando los ejemplos del libro: Aprendizaje práctico con Scikit Learn y Tensorflow . Me encontré con el mismo problema al leer el ejemplo en el Capítulo 2.
Como lo mencionaron otras personas, el problema está relacionado con LabelBinarizer de sklearn. Toma menos args en su método fit_transform en comparación con otros transformadores en la tubería. (solo y cuando otros transformadores normalmente toman X e y, vea here para más detalles). Es por eso que cuando ejecutamos pipeline.fit_transform, alimentamos más args en este transformador de lo requerido.
Una solución fácil que utilicé es usar OneHotEncoder y establecer "sparse" en False para asegurar que la salida sea una matriz numpy igual que la salida num_pipeline. (de esta manera no necesita codificar su propio codificador personalizado)
tu cat_pipeline original:
cat_pipeline = Pipeline([
(''selector'', DataFrameSelector(cat_attribs)),
(''label_binarizer'', LabelBinarizer())
])
simplemente puede cambiar esta parte a:
cat_pipeline = Pipeline([
(''selector'', DataFrameSelector(cat_attribs)),
(''one_hot_encoder'', OneHotEncoder(sparse=False))
])
Puedes ir desde aquí y todo debería funcionar.
Creo que su ejemplo es del libro
Aprendizaje automático práctico con Scikit-Learn y TensorFlow
.
Desafortunadamente, me encontré con este problema también.
Un cambio reciente en
scikit-learn
(
0.19.0
) cambió el método
fit_transform
.
Desafortunadamente,
LabelBinarizer
nunca tuvo la intención de funcionar como ese ejemplo lo usa.
Puede ver información sobre el cambio
here
y
here
.
Hasta que encuentren una solución para esto, puede instalar la versión anterior (
0.18.0
) de la siguiente manera:
$ pip install scikit-learn==0.18.0
Después de ejecutar eso, su código debería ejecutarse sin problemas.
En el futuro, parece que la solución correcta puede ser usar una clase
CategoricalEncoder
o algo similar.
Han estado tratando de resolver este problema durante años aparentemente.
Puede ver la nueva clase
here
y más discusión sobre el problema
here
.
Me encontré con el mismo problema y lo puse a trabajar aplicando la solución especificada en el repositorio de Github del libro .
Advertencia: las versiones anteriores del libro usaban la clase LabelBinarizer en este punto. Nuevamente, esto era incorrecto: al igual que la clase LabelEncoder, la clase LabelBinarizer fue diseñada para preprocesar etiquetas, no funciones de entrada. Una mejor solución es usar la próxima clase CategoricalEncoder de Scikit-Learn: pronto se agregará a Scikit-Learn, y mientras tanto puede usar el código a continuación (copiado de la Solicitud de here ).
Para ahorrarle un poco de grepping, esta es la solución, simplemente péguela y ejecútela en una celda anterior:
# 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
Olvídese de LaberBinarizer y use OneHotEncoder en su lugar.
En caso de que use un LabelEncoder antes de OneHotEncoder para convertir categorías a enteros, ahora puede usar OneHotEncoder directamente.
Para realizar una codificación única para múltiples características categóricas, podemos crear una nueva clase que personalice nuestro propio binarizador de múltiples características categóricas y conectarlo a la canalización categórica de la siguiente manera.
Supongamos que
CAT_FEATURES = [''cat_feature1'', ''cat_feature2'']
es una lista de características categóricas.
Los siguientes scripts resolverán el problema y producirán lo que queramos.
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin
class CustomLabelBinarizer(BaseEstimator, TransformerMixin):
"""Perform one-hot encoding to categorical features."""
def __init__(self, cat_features):
self.cat_features = cat_features
def fit(self, X_cat, y=None):
return self
def transform(self, X_cat):
X_cat_df = pd.DataFrame(X_cat, columns=self.cat_features)
X_onehot_df = pd.get_dummies(X_cat_df, columns=self.cat_features)
return X_onehot_df.values
# Pipeline for categorical features.
cat_pipeline = Pipeline([
(''selector'', DataFrameSelector(CAT_FEATURES)),
(''onehot_encoder'', CustomLabelBinarizer(CAT_FEATURES))
])
Puede crear un transformador personalizado más que haga la codificación por usted.
class CustomLabelEncode(BaseEstimator, TransformerMixin):
def fit(self, X, y=None):
return self
def transform(self, X):
return LabelEncoder().fit_transform(X);
En este ejemplo, hemos realizado LabelEncoding pero también puede usar LabelBinarizer
Simplemente, lo que puede hacer es definir la siguiente clase justo antes de su canalización:
from sklearn_pandas import DataFrameMapper
cat_pipeline = Pipeline([
(''label_binarizer'', DataFrameMapper([(cat_attribs, LabelBinarizer())])),
])
Luego, el resto del código es como el que se menciona en el libro con una pequeña modificación en
cat_pipeline
antes de la concatenación de la tubería: siga estos pasos:
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)
¡Lo hiciste!
Terminé rodando la mía
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
Parece funcionar
lbn = LabelBinarizer()
thingy = np.array([[''male'',''male'',''female'', ''male''], [''A'', ''B'', ''A'', ''C'']]).T
lbn.fit(thingy)
lbn.transform(thingy)
devoluciones
array([[1, 1, 0, 0],
[1, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 1]])
Tuve el mismo problema y me resolví usando DataFrameMapper (necesito instalar sklearn_pandas):
cat_pipeline = Pipeline([
("selector", DataFrameSelector(cat_attribs)),
("label_binarizer", NewLabelBinarizer())])
El problema:
La canalización supone que el método fit_transform de
fit_transform
está definido para tomar tres argumentos posicionales:
def fit_transform(self, x, y)
...rest of the code
mientras se define para tomar solo dos:
def fit_transform(self, x):
...rest of the code
Solución posible:
Esto se puede resolver haciendo un transformador personalizado que pueda manejar 3 argumentos posicionales:
-
Importa y crea una nueva clase:
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)
-
Mantenga su código igual solo en lugar de usar LabelBinarizer (), use la clase que creamos: MyLabelBinarizer ().
Nota: Si desea acceder a los atributos de LabelBinarizer (por ejemplo, clases_), agregue la siguiente línea al método de
fit
:
self.classes_, self.y_type_, self.sparse_input_ = self.encoder.classes_, self.encoder.y_type_, self.encoder.sparse_input_