python - 使用 pandas 和 scikit (OneHotEncoder) 虚拟化逻辑回归的分类变量

标签 python pandas numpy machine-learning scikit-learn

我读到this关于 scikit 中新事物的博客。 OneHotEncoder 获取字符串似乎是一个有用的功能。下面是我尝试使用这个的

import pandas as pd

from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer

cols = ['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 11
acc_log = round(logreg.score(X_train, Y_train) * 100, 2)

print(acc_log)

我在这段代码中遇到了以下 python 错误,并且我还有一些其他问题。

ValueError: X has 10 features per sample; expecting 11

从头开始..这个脚本是为 kaggle 的“泰坦尼克号”数据集编写的。我们有五个数值列 PclassAgeSibSpParchFareSexEmbarked 列是类别 male/femaleQ/S/C(它是城市名称)。

我从 OneHotEncoder 了解到的是,它通过放置额外的列来创建虚拟变量。实际上,ct.fit_transform() 的输出现在不再是 pandas 数据帧,而是 numpy 数组。但从打印调试语句中可以看出,现在比原来的 7 列多了。

我遇到三个问题:

  1. 由于某种原因,test.csv 少了一列。这对我来说表明其中一个类别的选择较少。为了解决这个问题,我必须在训练+测试数据的类别中找到所有可用的选项。然后使用这些选项(例如male/female)分别对训练数据和测试数据进行转换。我不知道如何使用我正在使用的工具(pandasscikit 等)来执行此操作。再想一想..检查数据后我找不到 test.csv 中缺少的选项..

  2. 我想避免“dummy variable trap ”。现在看来创建的列太多了。我本来期待 1 栏用于性(总共选项 2 - 1 以避免陷阱)和 2 栏用于启动。加上额外的 5 个数字列,总共 8 个。

  3. 我不再识别转换的输出。我更喜欢一个新的数据框,其中新的虚拟列给出了自己的名称,例如 Sex_male (1/0) Embarked_Q (1/0) 和 Embarked_S(1/0)

我只习惯使用gretl,虚拟化一个变量并省略一个选项是很自然的。我不知道在 python 中我是否做错了,或者这个场景是否不是标准 scikit 工具包的一部分。有什么建议吗?也许我应该为此编写一个自定义编码器?

最佳答案

我会尝试单独回答您的所有问题。

Answer for Question 1

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

所以正确的做法是:

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

Answer for Question 2

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

所以正确的做法是:

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

Answer for Question 3

到目前为止,sklearn 中尚未实现可以使用新虚拟列重建数据框的 get_feature_names 方法。解决此问题的一种解决方法是将 ColumnTransformer 构造中的 reminder 更改为 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 pd
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer

cols = ['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

关于python - 使用 pandas 和 scikit (OneHotEncoder) 虚拟化逻辑回归的分类变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59481354/

相关文章:

Python 数据框删除出现频率较低的行

Python Pandas - 如果某些值为空则合并行

python - 对 Pandas DataFrame 列表求和

python - 用两个物体做螺旋

python - Numpy 拆分多维数组

python - 使用列表推导式使函数更像 pythonic

python - 从 7.1.2 安装 pip 8.1.1 时出现问题

python - 获取函数的参数数量

python - 如何在Python中将字符串中的特定数字转换为字符串 "NUM"

python - 由于数组大小的微小差异,创建 numpy.zeros 的时间差异很大