sklearn.compose.ColumnTransformer:fit_transform() 接受 2 个位置参数,但给出了 3 个

Posted

技术标签:

【中文标题】sklearn.compose.ColumnTransformer:fit_transform() 接受 2 个位置参数,但给出了 3 个【英文标题】:sklearn.compose.ColumnTransformer: fit_transform() takes 2 positional arguments but 3 were given 【发布时间】:2019-09-21 00:35:56 【问题描述】:

我正在研究一个使用ColumnTransformerLabelEncoder 预处理著名的泰坦尼克号数据集X 的示例:

    Age Embarked    Fare    Sex
0   22.0    S      7.2500   male
1   38.0    C      71.2833  female
2   26.0    S      7.9250   female
3   35.0    S      53.1000  female
4   35.0    S      8.0500   male

像这样调用转换器:

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import LabelEncoder
ColumnTransformer(
    transformers=[
        ("label-encode categorical", LabelEncoder(), ["Sex", "Embarked"])
    ]
).fit(X).transform(X)

结果:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-54-fd5a05b7e47e> in <module>
      4         ("label-encode categorical", LabelEncoder(), ["Sex", "Embarked"])
      5     ]
----> 6 ).fit(X).transform(X)

~/anaconda3/lib/python3.7/site-packages/sklearn/compose/_column_transformer.py in fit(self, X, y)
    418         # we use fit_transform to make sure to set sparse_output_ (for which we
    419         # need the transformed data) to have consistent output type in predict
--> 420         self.fit_transform(X, y=y)
    421         return self
    422 

~/anaconda3/lib/python3.7/site-packages/sklearn/compose/_column_transformer.py in fit_transform(self, X, y)
    447         self._validate_remainder(X)
    448 
--> 449         result = self._fit_transform(X, y, _fit_transform_one)
    450 
    451         if not result:

~/anaconda3/lib/python3.7/site-packages/sklearn/compose/_column_transformer.py in _fit_transform(self, X, y, func, fitted)
    391                               _get_column(X, column), y, weight)
    392                 for _, trans, column, weight in self._iter(
--> 393                     fitted=fitted, replace_strings=True))
    394         except ValueError as e:
    395             if "Expected 2D array, got 1D array instead" in str(e):

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/parallel.py in __call__(self, iterable)
    915             # remaining jobs.
    916             self._iterating = False
--> 917             if self.dispatch_one_batch(iterator):
    918                 self._iterating = self._original_iterator is not None
    919 

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/parallel.py in dispatch_one_batch(self, iterator)
    757                 return False
    758             else:
--> 759                 self._dispatch(tasks)
    760                 return True
    761 

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/parallel.py in _dispatch(self, batch)
    714         with self._lock:
    715             job_idx = len(self._jobs)
--> 716             job = self._backend.apply_async(batch, callback=cb)
    717             # A job can complete so quickly than its callback is
    718             # called before we get here, causing self._jobs to

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/_parallel_backends.py in apply_async(self, func, callback)
    180     def apply_async(self, func, callback=None):
    181         """Schedule a func to be run"""
--> 182         result = ImmediateResult(func)
    183         if callback:
    184             callback(result)

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/_parallel_backends.py in __init__(self, batch)
    547         # Don't delay the application, to avoid keeping the input
    548         # arguments in memory
--> 549         self.results = batch()
    550 
    551     def get(self):

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/parallel.py in __call__(self)
    223         with parallel_backend(self._backend, n_jobs=self._n_jobs):
    224             return [func(*args, **kwargs)
--> 225                     for func, args, kwargs in self.items]
    226 
    227     def __len__(self):

~/anaconda3/lib/python3.7/site-packages/sklearn/externals/joblib/parallel.py in <listcomp>(.0)
    223         with parallel_backend(self._backend, n_jobs=self._n_jobs):
    224             return [func(*args, **kwargs)
--> 225                     for func, args, kwargs in self.items]
    226 
    227     def __len__(self):

~/anaconda3/lib/python3.7/site-packages/sklearn/pipeline.py in _fit_transform_one(transformer, X, y, weight, **fit_params)
    612 def _fit_transform_one(transformer, X, y, weight, **fit_params):
    613     if hasattr(transformer, 'fit_transform'):
--> 614         res = transformer.fit_transform(X, y, **fit_params)
    615     else:
    616         res = transformer.fit(X, y, **fit_params).transform(X)

TypeError: fit_transform() takes 2 positional arguments but 3 were given

**fit_params 这里有什么问题?对我来说,这看起来像是 sklearn 中的一个错误,或者至少是不兼容。

【问题讨论】:

我知道有很多变通方法,但我特意寻找一种解决方案,它使用单个管道对象进行整个预处理并应用标签编码 【参考方案1】:

我相信这实际上是LabelEncoder 的问题。 LabelEncoder.fit 方法只接受 selfy 作为参数(这很奇怪,因为大多数转换器对象都有 fit(X, y=None, **fit_params) 的范例)。无论如何,在管道中,无论您通过了什么,都会使用fit_params 调用转换器。在这种特殊情况下,传递给LabelEncoder.fit 的确切参数是X 和一个空字典。从而引发错误。

在我看来,这是 LabelEncoder 中的一个错误,但您应该与 sklearn 人员一起解决这个问题,因为他们可能有某些原因以不同的方式实现 fit 方法。

【讨论】:

这是正确答案。没有理由不能/不应该将 LabelEncoder 用于功能。 OneHotEncoding 与标签编码不同。在某些情况下,使用大型稀疏结构或序数编码可能没有意义。 但是你的解决方案是什么?【参考方案2】:

这对您的目的不起作用有两个主要原因。

    LabelEncoder() 旨在用于目标变量 (y)。这就是在columnTransformer() 尝试提供X, y=None, fit_params= 时出现位置参数错误的原因。

来自Documentation:

使用 0 和 n_classes-1 之间的值对标签进行编码。

适合(y) 拟合标签编码器

参数:y : 类似数组的形状 (n_samples,) 目标值。

    即使您采取了一种解决方法来删除空字典,那么 LabelEncoder() 也不能采用 2D 数组(基本上一次使用多个特征),因为它只采用 1D y 值。

简短回答 - 我们不应该将 LabelEncoder() 用于输入功能。

现在,对输入特征进行编码的解决方案是什么?

如果您的特征是有序特征,请使用OrdinalEncoder(),如果是名义特征,请使用OneHotEncoder()

例子:

>>> from sklearn.compose import ColumnTransformer
>>> from sklearn.preprocessing import OrdinalEncoder, OneHotEncoder
>>> X = np.array([[1000., 100., 'apple', 'green'],
...               [1100., 100., 'orange', 'blue']])
>>> ct = ColumnTransformer(
...     [("ordinal", OrdinalEncoder(), [0, 1]),
         ("nominal", OneHotEncoder(), [2, 3])])
>>> ct.fit_transform(X)   
array([[0., 0., 1., 0., 0., 1.],
       [1., 0., 0., 1., 1., 0.]]) 

【讨论】:

这就解释了。我经常看到 LabelEncoder 被“误用”于特征编码,以至于我认为它就是为此而设计的。 只是为了清楚空字典实际上是fit_params。如果您浏览代码,您将看到sklearn.pipeline._fit_transform_oneLabelEncoder 实例、数据框(X)、Noney)和fit_params)调用。 【参考方案3】:

它被称为 label 编码器,因为它旨在与数据集的 labels 一起使用,即y 值。这门课让我很困惑,直到我意识到这一点。

这令人困惑,因为在文献中,我们要么对我们的特征进行 One-Hot 编码,要么对它们进行标签编码。从这个意义上说,Sklearn 对新手并不是超级友好。

请改用OrdinalEncoder,它旨在使用功能。

【讨论】:

以上是关于sklearn.compose.ColumnTransformer:fit_transform() 接受 2 个位置参数,但给出了 3 个的主要内容,如果未能解决你的问题,请参考以下文章