为啥 sklearn 预处理 LabelEncoder inverse_transform 只适用于一列?

Posted

技术标签:

【中文标题】为啥 sklearn 预处理 LabelEncoder inverse_transform 只适用于一列?【英文标题】:Why does sklearn preprocessing LabelEncoder inverse_transform apply from only one column?为什么 sklearn 预处理 LabelEncoder inverse_transform 只适用于一列? 【发布时间】:2017-08-24 23:28:30 【问题描述】:

我有一个用 sklearn 构建的随机森林模型。该模型构建在一个文件中,我有第二个文件,我使用 joblib 加载模型并将其应用于新数据。数据具有通过 sklearn 的预处理 LabelEncoder.fit_transform 转换的分类字段。一旦做出预测,我将尝试使用LabelEncoder.inverse_transform 来反转此转换。

代码如下:

 #transform the categorical rf inputs
 df["method"] = le.fit_transform(df["method"])
 df["vendor"] = le.fit_transform(df["vendor"])
 df["type"] = le.fit_transform(df["type"])
 df["name"] = le.fit_transform(df["name"])
 dups["address"] = le.fit_transform(df["address"])

 #designate inputs for rf model
 inputs = ["amt","vendor","type","name","address","method"]

 #load rf model and run it on new data
 from sklearn.externals import joblib
 rf = joblib.load('rf.pkl')
 predict = rf.predict(df[inputs])

 #reverse LabelEncoder fit_transform
 df["method"] = le.inverse_transform(df["method"])
 df["vendor"] = le.inverse_transform(df["vendor"])
 df["type"] = le.inverse_transform(df["type"])
 df["name"] = le.inverse_transform(df["name"])
 df["address"] = le.inverse_transform(df["address"])

 #convert target to numeric to make it play nice with SQL Server
 predict = pd.to_numeric(predict)

 #add target field to df
 df["prediction"] = predict

 #write results to SQL Server table
 import sqlalchemy
 engine = sqlalchemy.create_engine("mssql+pyodbc://<username>:<password>@UserDSN")
 df.to_sql('TABLE_NAME', engine, schema='SCHEMANAME', if_exists='replace', index=False)

没有inverse_transform 部分,结果如预期:数字代码代替分类值。使用inverse_transform 部分,结果很奇怪:所有 分类字段返回与“地址”字段对应的分类值。

因此,如果将宾夕法尼亚大道 1600 号编码为数字 1,所有 编码为数字 1 的分类值(无论字段如何)现在都会返回宾夕法尼亚大道 1600 号。为什么inverse_transform 选择一列来反转所有fit_transform 代码?

【问题讨论】:

【参考方案1】:

这是预期的行为。

当您调用le.fit_transform() 时,LabelEncoder 的内部参数(学习的类)会重新初始化。 le 对象适合您提供的列的值。

在上面的代码中,您使用同一个对象来转换所有列,您提供的最后一列是address。因此,le 忘记了之前调用fit()(或在本例中为fit_transform())的所有信息,并再次学习新数据。所以当你在它上面调用inverse_transform()时,它只返回与address相关的值。希望我很清楚。

要对所有列进行编码,您需要初始化不同的对象,每列一个。如下所示:

 df["method"] = le_method.fit_transform(df["method"])
 df["vendor"] = le_vendor.fit_transform(df["vendor"])
 df["type"] = le_type.fit_transform(df["type"])
 df["name"] = le_name.fit_transform(df["name"])
 df["address"] = le_address.fit_transform(df["address"])

然后在适当的编码器上调用inverse_transform()

【讨论】:

行得通-谢谢!不过似乎有点臃肿,特别是因为它必须包含在测试/训练文件预测文件中。有什么办法可以减少吗? @CameronTaylor 不幸的是,在我看来,scikit 中没有工具可以实现这一点。也许其他科学图书馆也有。但是制作一个并不难。只需使用 dict 来保存它们。 @CameronTaylor 像你一样编码分类特征也不是一个好习惯。你应该做的是为它们创建虚拟变量。详细了解将分类数据转换为有序数值数据的优缺点。如果您还有任何疑问,请来这里。 @CameronTaylor 看看analyticsvidhya.com/blog/2015/11/… 谢谢你,Vivek - 我非常感谢。下次我有时间回到这个模型时,我会看看那篇文章。抱歉,我没有为您的答案投票的声誉!【参考方案2】:

我知道这是一个老问题,但是对于喜欢方便的每个人来说:

应用,再加上lambda 可以轻松转换多个/所有列

df = df.apply(lambda col: le.fit_transform(col))

我鄙视像这样的非别名、非动态代码(你也应该这样做),除非真的有必要:

 df["method"] = le_method.fit_transform(df["method"])
 df["vendor"] = le_vendor.fit_transform(df["vendor"])
 df["type"] = le_type.fit_transform(df["type"])
 df["name"] = le_name.fit_transform(df["name"])
 df["address"] = le_address.fit_transform(df["address"])

【讨论】:

以上是关于为啥 sklearn 预处理 LabelEncoder inverse_transform 只适用于一列?的主要内容,如果未能解决你的问题,请参考以下文章

sklearn LabelEncoder 和 pd.get_dummies 有啥区别?

为啥我在 Sklearn 管道中的 OneHotEncoding 后得到的列比预期的多?

为啥 sklearn 中逻辑回归的等效 class_weights 会产生不同的结果?

为啥这段代码不用import sklearn就可以使用sklearn函数?

为啥 SKlearn 和 WEKA 结果不匹配?

为啥 SKLearn 距离度量类没有余弦距离?