使用pandas和scikit进行逻辑回归的分类变量虚拟化处理(OneHotEncoder)

我读了一篇关于scikit新功能的博客OneHotEncoder能够处理字符串看起来是一个很有用的功能。下面是我尝试使用它的代码

import pandas as pdfrom sklearn.linear_model import LogisticRegressionfrom sklearn.preprocessing import OneHotEncoderfrom sklearn.compose import ColumnTransformercols = ['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']train_df = pd.read_csv('../../data/train.csv', usecols=cols)test_df = pd.read_csv('../../data/test.csv', usecols=[e for e in cols if e != 'Survived'])train_df.dropna(inplace=True)test_df.dropna(inplace=True)X_train = train_df.drop("Survived", axis=1)Y_train = train_df["Survived"]X_test = test_df.copy()ct = ColumnTransformer([("onehot", OneHotEncoder(sparse=False), ['Sex', 'Embarked'])], remainder='passthrough')X_train_t = ct.fit_transform(train_df)X_test_t  = ct.fit_transform(test_df)print(X_train_t[0])print(X_test_t[0])# [ 0.    1.    0.    0.    1.    0.    3.   22.    1.    0.    7.25]# [ 0.    1.    0.    1.    0.          3. 34.5     0.    0.  7.8292]logreg = LogisticRegression(max_iter=5000)logreg.fit(X_train_t, Y_train)Y_pred = logreg.predict(X_test_t) # ValueError: X has 10 features per sample; expecting 11acc_log = round(logreg.score(X_train, Y_train) * 100, 2)print(acc_log)

使用这段代码时我遇到了下面的Python错误,同时我还有一些额外的担忧。

ValueError: X has 10 features per sample; expecting 11

从头开始说起…这个脚本是为Kaggle的“泰坦尼克号”数据集编写的。我们有五个数值列PclassAgeSibSpParchFare。列SexEmbarked是分类变量,分别为male/femaleQ/S/C(这是城市名称的缩写)。

我从OneHotEncoder中了解到,它通过添加额外的列来创建虚拟变量。实际上,ct.fit_transform()的输出不再是一个pandas数据框,而是一个numpy数组。但如调试打印语句所示,现在的列数超过了原来的7列。

我遇到了三个问题:

  1. 不知何故,test.csv少了一列。这表明其中一个分类变量的选项少了一个。为了解决这个问题,我需要在训练和测试数据中找到所有可用的分类选项。然后使用这些选项(如male/female)分别转换训练和测试数据。我不知道如何使用我正在使用的工具(pandasscikit等)来实现这一点。再次思考后…在检查数据后,我在test.csv中找不到缺失的选项..

  2. 我想避免“虚拟变量陷阱”。目前似乎创建了太多列。我原本期望为Sex创建1列(总选项2 – 1以避免陷阱),为Embarked创建2列。加上额外的5个数值列,总共应该是8列。

  3. 我不再认识转换后的输出。我更希望得到一个新的数据框,其中新的虚拟列有自己的名称,比如Sex_male (1/0) Embarked_Q (1/0) 和 Embarked_S(1/0)

我只习惯使用gretl,在那里对变量进行虚拟化处理并省略一个选项是非常自然的。我不知道在Python中我是否做错了,或者这种情况是否不在标准的scikit工具包范围内。有什么建议吗?也许我应该为此编写一个自定义编码器?


回答:

我将尝试逐个回答您的所有问题。

问题1的回答

在您的代码中,您在训练和测试数据上都使用了fit_transform方法,这不是正确的方法。通常,fit_transform只应用于您的训练数据集,它返回一个转换器,然后只用于transform您的测试数据集。当您在测试数据上应用fit_transform时,您只是使用测试数据集中可用的分类变量的选项/级别来转换您的测试数据,测试数据可能并不包含所有分类变量的所有选项/级别,因此您的训练和测试数据集的维度会有所不同,从而导致您遇到的错误。

所以正确的方法应该是:

X_train_t = ct.fit_transform(X_train)X_test_t  = ct.transform(X_test)

问题2的回答

如果您想避免“虚拟变量陷阱”,您可以在创建ColumnTransformer中的OneHotEncoder对象时使用drop参数(将其设置为first),这将导致为sex创建一列,为Embarked创建两列,因为它们分别有两个和三个选项/级别。

所以正确的方法应该是:

ct = ColumnTransformer([("onehot", OneHotEncoder(sparse=False, drop="first"), ['Sex','Embarked'])], remainder='passthrough')

问题3的回答

目前,get_feature_names方法尚未在sklearn中实现,该方法可以重建包含新虚拟列的数据框。解决这个问题的一个方法是将remainder更改为drop,并单独构建您的数据框,如下所示:

ct = ColumnTransformer([("onehot", OneHotEncoder(sparse=False, drop="first"), ['Sex', 'Embarked'])], remainder='drop')A = pd.concat([X_train.drop(["Sex", "Embarked"], axis=1), pd.DataFrame(X_train_t, columns=ct.get_feature_names())], axis=1) A.head()

这将得到如下结果:

enter image description here

您的最终代码将如下所示:

import pandas as pdfrom sklearn.linear_model import LogisticRegressionfrom sklearn.preprocessing import OneHotEncoderfrom sklearn.compose import ColumnTransformercols = ['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']train_df = pd.read_csv('train.csv', usecols=cols)test_df = pd.read_csv('test.csv', usecols=[e for e in cols if e != 'Survived'])cols = ['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']train_df = train_df.dropna()test_df = test_df.dropna()train_df = train_df.reset_index(drop=True)test_df = test_df.reset_index(drop=True)X_train = train_df.drop("Survived", axis=1)Y_train = train_df["Survived"]X_test = test_df.copy()categorical_values = ['Sex', 'Embarked']X_train_cont = X_train.drop(categorical_values, axis=1)X_test_cont = X_test.drop(categorical_values, axis=1)ct = ColumnTransformer([("onehot", OneHotEncoder(sparse=False, drop="first"), categorical_values)], remainder='drop')X_train_categorical = ct.fit_transform(X_train)X_test_categorical  = ct.transform(X_test)X_train_t = pd.concat([X_train_cont, pd.DataFrame(X_train_categorical, columns=ct.get_feature_names())], axis=1)X_test_t = pd.concat([X_test_cont, pd.DataFrame(X_test_categorical, columns=ct.get_feature_names())], axis=1)logreg = LogisticRegression(max_iter=5000)logreg.fit(X_train_t, Y_train)Y_pred = logreg.predict(X_test_t)acc_log = round(logreg.score(X_train_t, Y_train) * 100, 2)print(acc_log)80.34

当您执行X_train_t.head()时,您会得到

enter image description here

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注