管道OrdinalEncoder值错误,发现未知类别

22

请不要过于苛刻,我正在转行进入数据科学领域,没有计算机科学或编程背景 - 所以我可能在做某些极其愚蠢的事情。我已经研究了几个小时,但没有成功。

目标:使用OrdinalEncoder运行Pipeline。

问题:使用OrdinalEncoder调用时代码无法运行,而不使用OrdinalEncoder可以运行。就我所知,我可以传递两个参数,即categoriesdtype。 两者都没有帮助。

我正在向模型传递公共糖尿病数据集。这是问题吗?换句话说,在构建模型后,将高基数特征传递给OrdinalEncoder是否会导致训练/测试数据之间出现问题,例如测试分裂具有训练集没有的值?

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OrdinalEncoder
from sklearn.ensemble import RandomForestClassifier

pipe = Pipeline([
    ('imputer', SimpleImputer()),
    ('ordinal_encoder', OrdinalEncoder()),
    ('classifier', RandomForestClassifier(criterion='gini', n_estimators=100))])

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# Construct model
model = pipe.fit(X_train, y_train)

# Show results
print("Hold-out AUC score: %.3f" %roc_auc_score(model.predict_proba(X_test),y_test))

这里是我遇到的错误信息:

ValueError: Found unknown categories [17.0] in column 0 during transform

我做错了什么?

设置:

The scikit-learn version is 0.20.2.
3.7.2 (v3.7.2:9a3ffc0492, Dec 24 2018, 02:44:43) 
[Clang 6.0 (clang-600.0.57)]
sys.version_info(major=3, minor=7, micro=2, releaselevel='final', serial=0)
10个回答

24
你的问题在于模型在测试数据中遇到了训练数据中未见过的值。这没关系,你只需要在编码器中添加“handle_unknown”参数即可。
你应该将编码器和缩放器拟合到训练数据(而不是测试数据),然后使用它们来转换训练和测试数据。因此,你必须考虑到测试数据中可能出现意外值的情况。

11
这将解决你的问题:
OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1)

请注意,要使此功能正常工作,您必须使用Scikit Learn版本0.24或更高版本。

由于某种原因,在unknown_values中使用-1而不是np.nan - Ryan

8
我曾经遇到过完全相同的问题,我只是使用了OneHotEncoder(handle_unknown='ignore')而不是OneHotEncoder(),这样问题就解决了。

请注意,要使此功能正常工作,您必须使用Scikit Learn版本0.24或更高版本。 - piedpiper

5

虽然我来晚了,但我看到这个页面还是想回复一下。

你在评论中说过:"对于给定的测试/训练拆分,糖尿病数据集的许多特征具有太多的值。"

当测试集包含未在训练期间出现的数据时,编码器会发生此错误。


1
我认为在这种情况下,OrdinalEncoder 不是正确的选择。糖尿病数据集由连续特征组成,而不是分类特征。如 OrdinalEncoder 的文档中所述:

这个转换器的输入应该是整数或字符串的类别(离散)特征值数组。

话虽如此,在没有来自 traceback 或您的设置的其他输出的情况下,我无法确定您为什么会收到错误。我能够成功地使用 load_diabetes 函数加载的数据拆分并执行上面的代码。我的猜测是,在您的情况下,您确实错过了将编码器与类别“17.0”配对,但我仍然不建议在这种情况下使用分类编码器。

0

我在使用ColumnTransformer()操作时遇到了与'OneHoteEncoder()'相同的错误,我怀疑是因为编码器需要将2D数组输入到编码器中。

您可以尝试.apply(lambda x: [x])或类似函数来增加有序值的条件性。


0
在“Scikit Learn版本0.24”中,Ordinal Encoder有两个新参数: handle_unknown{‘error’, ‘use_encoded_value’}, default=’error’unknown_value:{int or np.nan}, default=None 关于这两个参数,scikit-learn文档说明如下:
当参数handle_unknown设置为'use_encoded_value'时,需要使用此参数,并设置未知类别的编码值。它必须与fit中编码任何类别所使用的值不同。如果设置为np.nan,则dtype参数必须是float dtype。

0

我会考虑在训练期间将序数值存储为json文件,并在测试时读取相同的编码。任何进入测试数据集中未见过的新值都应在测试或评估之前进行处理。


0

我遇到了同样的问题,对于我来说,在将数据分成训练集和测试集之前进行编码是必要的。在分割数据之前进行编码可以使得所有类别都被注册。


5
这样做会影响到训练集和测试集的整体意义。测试集应该是一个“真实世界”的场景,在这个场景中你不知道新数值是什么。因此,当遇到新的分类时,你应该将其作为编码的边缘情况加入进来。在看到它们之前,你无法“预见未来”并知道哪些分类存在。我认为这是一个经典的数据泄漏错误。 - shuminizer
是的,我最近意识到了这一点,除非您在列中有预定义的类别,否则不应该进行独热编码。 - Gustavo Zantut

-1

使用fit_transform代替fittransform。这对我很有效。


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Blue Robin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接