有很多非数字数据。下面是如何将其用于机器学习。
在本教程中,您将了解什么是分类变量,以及处理此类数据的三种方法。
分类变量只接受有限数量的值。
如果试图将这些变量插入Python中的大多数机器学习模型,而不首先对它们进行预处理,则会出现错误。在本教程中,我们将比较三种可用于准备分类数据的方法。
处理分类变量最简单的方法就是从数据集中删除它们。只有在列中不包含有用信息的情况下,这种方法才能很好地工作。
顺序编码将每个唯一值分配给不同的整数。
这种方法假设类别的顺序是:“从不”(0)<“很少”(1)<“大多数天”(2)<“每天”(3)。
在这个例子中,这个假设是有道理的,因为分类有一个无可争议的排名。并不是所有的分类变量在值中都有明确的顺序,但我们把那些有顺序的变量称为顺序变量。对于基于树的模型(如决策树和随机森林),可以预期顺序编码可以很好地处理顺序变量。
one-hot
编码创建新列,指示原始数据中每个可能值的存在(或不存在)。为了理解这一点,我们将通过一个例子来学习。
在原始数据集中,“颜色”是一个分类变量,有三个类别:“红色”、“黄色”和“绿色”。对应的one-hot
编码为每个可能的值包含一列,为原始数据集中的每一行包含一行。如果原始值为“红色”,我们在“红色”列中输入1;如果原始值为“黄色”,则在“黄色”列中输入1,依此类推。
与顺序编码不同,one-hot
编码并不假定类别的顺序。因此,如果在分类数据中没有明确的顺序(例如,“红色”既不大于也不小于“黄色”),您可以期望这种方法工作得特别好。我们把没有内在排名的分类变量称为名义变量。
如果类别变量具有大量值(即,通常不会将其用于具有超过15个不同值的变量),则one-hot
编码通常不会运行良好。
与上一篇教程一样,我们将使用墨尔本住房数据集。
我们将不关注数据加载步骤。相反,您可以想象,您已经在X_train、X_valid、y_train和y_valid中拥有了训练和验证数据。
import pandas as pd
from sklearn.model_selection import train_test_split
# Read the data
data = pd.read_csv('../input/melbourne-housing-snapshot/melb_data.csv')
# Separate target from predictors
y = data.Price
X = data.drop(['Price'], axis=1)
# Divide data into training and validation subsets
X_train_full, X_valid_full, y_train, y_valid = train_test_split(X, y, train_size=0.8, test_size=0.2,
random_state=0)
# Drop columns with missing values (simplest approach)
cols_with_missing = [col for col in X_train_full.columns if X_train_full[col].isnull().any()]
X_train_full.drop(cols_with_missing, axis=1, inplace=True)
X_valid_full.drop(cols_with_missing, axis=1, inplace=True)
# "Cardinality" means the number of unique values in a column
# Select categorical columns with relatively low cardinality (convenient but arbitrary)
low_cardinality_cols = [cname for cname in X_train_full.columns if X_train_full[cname].nunique() < 10 and
X_train_full[cname].dtype == "object"]
# Select numerical columns
numerical_cols = [cname for cname in X_train_full.columns if X_train_full[cname].dtype in ['int64', 'float64']]
# Keep selected columns only
my_cols = low_cardinality_cols + numerical_cols
X_train = X_train_full[my_cols].copy()
X_valid = X_valid_full[my_cols].copy()
我们用下面的head()方法查看一下训练数据。
X_train.head()
'''
Type Method Regionname Rooms Distance Postcode Bedroom2 Bathroom Landsize Lattitude Longtitude Propertycount
12167 u S Southern Metropolitan 1 5.0 3182.0 1.0 1.0 0.0 -37.85984 144.9867 13240.0
6524 h SA Western Metropolitan 2 8.0 3016.0 2.0 2.0 193.0 -37.85800 144.9005 6380.0
8413 h S Western Metropolitan 3 12.6 3020.0 3.0 1.0 555.0 -37.79880 144.8220 3755.0
2919 u SP Northern Metropolitan 3 13.0 3046.0 3.0 1.0 265.0 -37.70830 144.9158 8870.0
6043 h S Western Metropolitan 3 13.3 3020.0 3.0 1.0 673.0 -37.76230 144.8272 4217.0
'''
接下来,我们将获得训练数据中所有分类变量的列表。
我们通过检查每列的数据类型(或数据类型)来实现这一点。object dtype表示一个列有文本(理论上可能有其他内容,但这对我们来说并不重要)。对于该数据集,带有文本的列表示分类变量。
# Get list of categorical variables
s = (X_train.dtypes == 'object')
object_cols = list(s[s].index)
print("Categorical variables:")
print(object_cols)
Categorical variables:
[‘Type’, ‘Method’, ‘Regionname’]
我们定义了一个函数score_dataset()来比较处理分类变量的三种不同方法。此函数报告随机森林模型的平均绝对误差(MAE)。总的来说,我们希望MAE尽可能低!
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error
# Function for comparing different approaches
def score_dataset(X_train, X_valid, y_train, y_valid):
model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(X_train, y_train)
preds = model.predict(X_valid)
return mean_absolute_error(y_valid, preds)
我们使用select_dtypes()方法删除对象列。
drop_X_train = X_train.select_dtypes(exclude=['object'])
drop_X_valid = X_valid.select_dtypes(exclude=['object'])
print("MAE from Approach 1 (Drop categorical variables):")
print(score_dataset(drop_X_train, drop_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop categorical variables):
175703.48185157913
Scikit-learn
有一个OrdinalCoder
类,可用于获取顺序编码。我们在分类变量上循环,并对每个列分别应用顺序编码器。
from sklearn.preprocessing import OrdinalEncoder
# Make copy to avoid changing original data
label_X_train = X_train.copy()
label_X_valid = X_valid.copy()
# Apply ordinal encoder to each column with categorical data
ordinal_encoder = OrdinalEncoder()
label_X_train[object_cols] = ordinal_encoder.fit_transform(X_train[object_cols])
label_X_valid[object_cols] = ordinal_encoder.transform(X_valid[object_cols])
print("MAE from Approach 2 (Ordinal Encoding):")
print(score_dataset(label_X_train, label_X_valid, y_train, y_valid))
MAE from Approach 2 (Ordinal Encoding):
165936.40548390493
在上面的代码单元中,对于每一列,我们将每个唯一的值随机分配给不同的整数。这是一种比提供自定义标签更简单的常见方法;然而,如果我们为所有序数变量提供更好的信息标签,我们可以期待性能的进一步提升。
我们使用scikit-learn
中的OneHotEncoder
类学习获取one-hot
编码。有许多参数可用于自定义其行为。
handle_unknown='ignore'
以避免验证数据包含训练数据中未表示的类时出错,以及sparse=False
可确保编码的列作为numpy
数组(而不是稀疏矩阵)返回。为了使用编码器,我们只提供我们想要进行one-hot
编码的分类列。例如,为了对训练数据进行编码,我们提供了X_train[object_cols]
。(下面代码单元中的object_cols
是带有分类数据的列名列表,因此X_train[object_cols]
包含训练集中的所有分类数据。)
from sklearn.preprocessing import OneHotEncoder
# Apply one-hot encoder to each column with categorical data
OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))
OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))
# One-hot encoding removed index; put it back
OH_cols_train.index = X_train.index
OH_cols_valid.index = X_valid.index
# Remove categorical columns (will replace with one-hot encoding)
num_X_train = X_train.drop(object_cols, axis=1)
num_X_valid = X_valid.drop(object_cols, axis=1)
# Add one-hot encoded columns to numerical features
OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)
print("MAE from Approach 3 (One-Hot Encoding):")
print(score_dataset(OH_X_train, OH_X_valid, y_train, y_valid))
MAE from Approach 3 (One-Hot Encoding):
166089.4893009678
在这种情况下,删除分类列(方法1)表现最差,因为它有最高的MAE分数。至于其他两种方法,由于返回的MAE分数在价值上非常接近,因此不能说明一种方法比另一种方法要好。
一般来说,one-hot编码(方法3)的性能通常最好,删除分类列(方法1)的性能通常最差,但具体情况会有所不同。
世界上充斥着分类数据。如果你知道如何使用这种常见的数据类型,你将成为一名更有效的数据科学家!
在练习中运用你的新技能!
通过编码分类变量,您将获得迄今为止最好的结果!
下面的问题将为你的工作提供反馈。运行以下单元格设置反馈系统。
import os
if not os.path.exists("../input/train.csv"):
os.symlink("../input/home-data-for-ml-course/train.csv", "../input/train.csv")
os.symlink("../input/home-data-for-ml-course/test.csv", "../input/test.csv")
from learntools.core import binder
binder.bind(globals())
from learntools.ml_intermediate.ex3 import *
print("Setup Complete")
Setup Complete
在本练习中,您将使用 Housing Prices Competition for Kaggle Learn Users 案例.
。
在不做任何更改的情况下运行以下代码单元,以加载X_train、X_valid、y_train和y_valid中的训练集和验证集。测试集在X_test中加载。
import pandas as pd
from sklearn.model_selection import train_test_split
# Read the data
# 读取数据
X = pd.read_csv('../input/train.csv', index_col='Id')
X_test = pd.read_csv('../input/test.csv', index_col='Id')
# Remove rows with missing target, separate target from predictors
X.dropna(axis=0, subset=['SalePrice'], inplace=True)
y = X.SalePrice
X.drop(['SalePrice'], axis=1, inplace=True)
# To keep things simple, we'll drop columns with missing values
# 删除目标值缺失的列,从预测中分享目标
cols_with_missing = [col for col in X.columns if X[col].isnull().any()]
X.drop(cols_with_missing, axis=1, inplace=True)
X_test.drop(cols_with_missing, axis=1, inplace=True)
# Break off validation set from training data
# 从训练集中分离出验证数据
X_train, X_valid, y_train, y_valid = train_test_split(X, y,
train_size=0.8, test_size=0.2,
random_state=0)
使用以下代码打印数据的前五行。查看数据各列的情况
X_train.head()
请注意,数据集包含数字变量和分类变量。在训练模型之前,需要对分类数据进行编码。
要比较不同的模型,您将使用教程中相同的score_dataset()函数。此函数报告随机森林模型的平均绝对误差(MAE)。
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error
# function for comparing different approaches
def score_dataset(X_train, X_valid, y_train, y_valid):
model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(X_train, y_train)
preds = model.predict(X_valid)
return mean_absolute_error(y_valid, preds)
您将从最简单的方法开始。使用下面的代码单元预处理X_train中的数据,并使用X_valid删除包含分类数据的列。将预处理的数据集分别命名为drop_X_train和drop_X_valid。
# Fill in the lines below: drop columns in training and validation data
drop_X_train = ____
drop_X_valid = ____
# Check your answers
step_1.check()
运行下一个代码单元以获取此方法的MAE。
print("MAE from Approach 1 (Drop categorical variables):")
print(score_dataset(drop_X_train, drop_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop categorical variables):
17837.82570776256
在开始顺序编码之前,我们将研究数据集。具体来说,我们将查看“Condition2”列。下面的代码单元打印训练集和验证集中的唯一条目。
print("Unique values in 'Condition2' column in training data:", X_train['Condition2'].unique())
print("\nUnique values in 'Condition2' column in validation data:", X_valid['Condition2'].unique())
Unique values in ‘Condition2’ column in training data: [‘Norm’ ‘PosA’ ‘Feedr’ ‘PosN’ ‘Artery’ ‘RRAe’]
Unique values in ‘Condition2’ column in validation data: [‘Norm’ ‘RRAn’ ‘RRNn’ ‘Artery’ ‘Feedr’ ‘PosN’]
如果您现在将代码编写到:
你会出错的。你能明白为什么会这样吗?(您需要使用上述输出来回答此问题。)
将顺序编码器拟合到训练数据中的列,会为训练数据中出现的每个唯一值创建相应的整数值标签。如果验证数据包含的值也没有出现在训练数据中,编码器将抛出一个错误,因为这些值不会被分配一个整数。请注意,验证数据中的“Condition2”列包含值“RRAn”和“RRNn”,但这些值不会出现在训练数据中——因此,如果我们尝试使用带有scikit-learn
的顺序编码器,代码将抛出错误。
这是现实数据中常见的问题,有很多方法可以解决这个问题。例如,您可以编写一个自定义序号编码器来处理新类别。然而,最简单的方法是删除有问题的分类列。
运行下面的代码单元格,将有问题的列保存到bad_label_cols
列表中。同样,可以安全顺序编码的列保存在good_label_cols
列表中。
# Categorical columns in the training data
object_cols = [col for col in X_train.columns if X_train[col].dtype == "object"]
# Columns that can be safely ordinal encoded
good_label_cols = [col for col in object_cols if
set(X_valid[col]).issubset(set(X_train[col]))]
# Problematic columns that will be dropped from the dataset
bad_label_cols = list(set(object_cols)-set(good_label_cols))
print('Categorical columns that will be ordinal encoded:', good_label_cols)
print('\nCategorical columns that will be dropped from the dataset:', bad_label_cols)
Categorical columns that will be ordinal encoded: [‘MSZoning’, ‘Street’, ‘LotShape’, ‘LandContour’, ‘Utilities’, ‘LotConfig’, ‘LandSlope’, ‘Neighborhood’, ‘Condition1’, ‘BldgType’, ‘HouseStyle’, ‘RoofStyle’, ‘Exterior1st’, ‘Exterior2nd’, ‘ExterQual’, ‘ExterCond’, ‘Foundation’, ‘Heating’, ‘HeatingQC’, ‘CentralAir’, ‘KitchenQual’, ‘PavedDrive’, ‘SaleType’, ‘SaleCondition’]
Categorical columns that will be dropped from the dataset: [‘Condition2’, ‘RoofMatl’, ‘Functional’]
使用下一个代码单元对X_train和X_valid中的数据进行顺序编码。将预处理的数据集分别命名为label_X_train和label_X_valid。
我们在下面提供了从数据集中删除bad_label_cols中的分类列的代码。
你应该用好的标签对分类列进行顺序编码。
from sklearn.preprocessing import OrdinalEncoder
# Drop categorical columns that will not be encoded
label_X_train = X_train.drop(bad_label_cols, axis=1)
label_X_valid = X_valid.drop(bad_label_cols, axis=1)
# Apply ordinal encoder
____ # Your code here
# Check your answer
step_2.b.check()
print("MAE from Approach 2 (Ordinal Encoding):")
print(score_dataset(label_X_train, label_X_valid, y_train, y_valid))
MAE from Approach 2 (Ordinal Encoding):
17098.01649543379
到目前为止,您已经尝试了两种不同的方法来处理分类变量。而且,您已经看到,编码分类数据比从数据集中删除列产生更好的结果。
很快,您将尝试one-hot编码。在此之前,我们还需要讨论一个话题。首先运行下一个代码单元,不做任何更改。
# Get number of unique entries in each column with categorical data
object_nunique = list(map(lambda col: X_train[col].nunique(), object_cols))
d = dict(zip(object_cols, object_nunique))
# Print number of unique entries by column, in ascending order
sorted(d.items(), key=lambda x: x[1])
[(‘Street’, 2),
(‘Utilities’, 2),
(‘CentralAir’, 2),
(‘LandSlope’, 3),
(‘PavedDrive’, 3),
(‘LotShape’, 4),
(‘LandContour’, 4),
(‘ExterQual’, 4),
(‘KitchenQual’, 4),
(‘MSZoning’, 5),
(‘LotConfig’, 5),
(‘BldgType’, 5),
(‘ExterCond’, 5),
(‘HeatingQC’, 5),
(‘Condition2’, 6),
(‘RoofStyle’, 6),
(‘Foundation’, 6),
(‘Heating’, 6),
(‘Functional’, 6),
(‘SaleCondition’, 6),
(‘RoofMatl’, 7),
(‘HouseStyle’, 8),
(‘Condition1’, 9),
(‘SaleType’, 9),
(‘Exterior1st’, 15),
(‘Exterior2nd’, 16),
(‘Neighborhood’, 25)]
上面的输出显示了每个包含分类数据的列中唯一值的数量。例如,训练数据中的“Street”列有两个唯一的值:“Grvl”和“Pave”,分别对应于碎石路和铺砌路。
我们将一个分类变量的唯一项的数量称为该分类变量的基数。例如,“Street”变量的基数为2。
使用以上输出回答以下问题。
# Fill in the line below: How many categorical variables in the training data
# have cardinality greater than 10?
high_cardinality_numcols = ____
# Fill in the line below: How many columns are needed to one-hot encode the
# 'Neighborhood' variable in the training data?
num_cols_neighborhood = ____
# Check your answers
step_3.a.check()
对于具有多行的大型数据集,one-hot编码可以极大地扩展数据集的大小。因此,我们通常只对一个基数相对较低的列进行one-hot编码。然后,高基数列可以从数据集中删除,也可以使用顺序编码。
例如,考虑一个包含10000行的数据集,其中包含一个包含100个唯一条目的分类列。
# Fill in the line below: How many entries are added to the dataset by
# replacing the column with a one-hot encoding?
OH_entries_added = ____
# Fill in the line below: How many entries are added to the dataset by
# replacing the column with an ordinal encoding?
# 填写以下行
label_entries_added = ____
# Check your answers
step_3.b.check()
接下来,您将尝试one-hot编码。但是,您将只为基数小于10的列创建one-hot编码,而不是对数据集中的所有分类变量进行编码。
在不做任何更改的情况下运行下面的代码单元格,将low_cardinality_cols设置为一个Python列表,其中包含将被one-hot编码的列。同样,high_cardinality_cols包含将从数据集中删除的分类列列表。
# Columns that will be one-hot encoded
# 进行one-hot 编码列
low_cardinality_cols = [col for col in object_cols if X_train[col].nunique() < 10]
# Columns that will be dropped from the dataset
# 删除数据集相关的列
high_cardinality_cols = list(set(object_cols)-set(low_cardinality_cols))
print('Categorical columns that will be one-hot encoded:', low_cardinality_cols)
print('\nCategorical columns that will be dropped from the dataset:', high_cardinality_cols)
Categorical columns that will be one-hot encoded: [‘MSZoning’, ‘Street’, ‘LotShape’, ‘LandContour’, ‘Utilities’, ‘LotConfig’, ‘LandSlope’, ‘Condition1’, ‘Condition2’, ‘BldgType’, ‘HouseStyle’, ‘RoofStyle’, ‘RoofMatl’, ‘ExterQual’, ‘ExterCond’, ‘Foundation’, ‘Heating’, ‘HeatingQC’, ‘CentralAir’, ‘KitchenQual’, ‘Functional’, ‘PavedDrive’, ‘SaleType’, ‘SaleCondition’]
Categorical columns that will be dropped from the dataset: [‘Exterior2nd’, ‘Exterior1st’, ‘Neighborhood’]
使用下一个代码单元对X_train和X_valid中的数据进行one-hot编码。将预处理的数据帧分别命名为OH_X_train和OH_X_valid。
数据集中分类列的完整列表可以在Python列表object_cols中找到。
你应该只对低基数的分类列进行一次one-hot编码。所有其他分类列都应从数据集中删除。
from sklearn.preprocessing import OneHotEncoder
# Use as many lines of code as you need!
# 需要多行的代码
OH_X_train = ____ # Your code here
OH_X_valid = ____ # Your code here
# Check your answer
step_4.check()
运行下一个代码单元以获取此方法的MAE。
print("MAE from Approach 3 (One-Hot Encoding):")
print(score_dataset(OH_X_train, OH_X_valid, y_train, y_valid))
MAE from Approach 3 (One-Hot Encoding):
17525.345719178084
完成第4步后,如果您想使用所学知识将结果提交到排行榜,则需要在生成预测之前对测试数据进行预处理。
这一步完全是可选的,你不需要将结果提交到排行榜就可以成功完成练习。
如果您需要帮助记住如何参加比赛或将结果保存到CSV,请查看上一个练习。生成包含结果的文件后,请按照以下说明操作:
您现在已成功提交竞赛!
如果您想继续工作以提高性能,请选择屏幕右上角的编辑按钮。然后您可以更改代码并重复该过程。还有很大的改进空间,你将在工作中登上排行榜。
### step 1
# Drop columns in training and validation data
drop_X_train = X_train.select_dtypes(exclude=['object'])
drop_X_valid = X_valid.select_dtypes(exclude=['object'])
# step 2
# Drop categorical columns that will not be encoded
label_X_train = X_train.drop(bad_label_cols, axis=1)
label_X_valid = X_valid.drop(bad_label_cols, axis=1)
# Apply ordinal encoder
ordinal_encoder = OrdinalEncoder()
label_X_train[good_label_cols] = ordinal_encoder.fit_transform(X_train[good_label_cols])
label_X_valid[good_label_cols] = ordinal_encoder.transform(X_valid[good_label_cols])
# step 3
# How many categorical variables in the training data
# have cardinality greater than 10?
high_cardinality_numcols = 3
# How many columns are needed to one-hot encode the
# 'Neighborhood' variable in the training data?
num_cols_neighborhood = 25
# How many entries are added to the dataset by
# replacing the column with a one-hot encoding?
OH_entries_added = 1e4*100 - 1e4
# How many entries are added to the dataset by
# replacing the column with an ordinal encoding?
label_entries_added = 0
# step 4
# Apply one-hot encoder to each column with categorical data
OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[low_cardinality_cols]))
OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[low_cardinality_cols]))
# One-hot encoding removed index; put it back
OH_cols_train.index = X_train.index
OH_cols_valid.index = X_valid.index
# Remove categorical columns (will replace with one-hot encoding)
num_X_train = X_train.drop(object_cols, axis=1)
num_X_valid = X_valid.drop(object_cols, axis=1)
# Add one-hot encoded columns to numerical features
OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)