机器学习中级教程
缺失值的发生,为现实数据集中的这一常见挑战做好准备。
在本教程中,您将学习三种处理缺失值的方法。然后,您将在真实数据集上比较这些方法的有效性。
正文
介绍
有许多方法可以导致数据丢失值。例如,
- 两间卧室的房子不包括第三间卧室的价值
- 被调查者可选择不分享其收入
大多数机器学习库(包括scikit-learn)在试图使用缺失值的数据构建模型时都会出错。因此,你需要选择以下策略之一。
三种方法
1) 一个简单的选项:删除缺少值的列
最简单的选项是删除缺少值的列。
除非删除列中的大多数值都丢失了,否则使用这种方法,模型将失去对大量(可能有用的!)信息。作为一个极端的例子,考虑一个有10,000行的数据集,其中一个重要的列缺少一个条目。这种方法将完全删除该列!
2) 更好的选择:插补
插补用一些数字填充缺失的值。例如,我们可以填写每列的平均值。
在大多数情况下,估算值并不完全正确,但它通常会得到比完全删除列更准确的模型。
3) 插补的扩展
插补是标准方法,通常效果良好。然而,估算值可能系统地高于或低于实际值(数据集中未收集到实际值)。或者,缺少值的行在其他方面可能是唯一的。在这种情况下,您的模型可以通过考虑最初缺少的值来做出更好的预测。
在这种方法中,我们像以前一样对缺失的值进行插补。此外,对于原始数据集中缺少条目的每一列,我们添加一个新列,显示插补条目的位置。
在某些情况下,这将有意义地改善结果。在其他情况下,这根本没有帮助。
举个例子
在本例中,我们将使用墨尔本住房数据集。我们的模型将使用房间数量和土地面积等信息来预测房价。
我们将不关注数据加载步骤。相反,相反,可以想象您已经拥有训练集和验证集(X_train、X_valid、y_train和y_valid)。
定义衡量每种方法质量的功能
我们定义了一个函数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=10, random_state=0)
model.fit(X_train, y_train)
preds = model.predict(X_valid)
return mean_absolute_error(y_valid, preds)
方法1的得分(删除缺少值的列)
因为我们同时使用训练集和验证集,所以我们会小心地在两个数据帧中删除相同的列。
# Get names of columns with missing values
cols_with_missing = [col for col in X_train.columns
if X_train[col].isnull().any()]
# Drop columns in training and validation data
reduced_X_train = X_train.drop(cols_with_missing, axis=1)
reduced_X_valid = X_valid.drop(cols_with_missing, axis=1)
print("MAE from Approach 1 (Drop columns with missing values):")
print(score_dataset(reduced_X_train, reduced_X_valid, y_train, y_valid))
MAE from Approach 1 (Drop columns with missing values):
183550.22137772635
方法2的得分(插补)
接下来,我们使用 SimpleImputer
将缺少的值替换为每列的平均值。
虽然很简单,但填充平均值通常表现得相当好(但这因数据集而异)。虽然统计学家已经尝试了更复杂的方法来确定插补值(例如回归插补),但一旦将结果插入复杂的机器学习模型,复杂的策略通常不会带来额外的好处。
from sklearn.impute import SimpleImputer
# Imputation
my_imputer = SimpleImputer()
imputed_X_train = pd.DataFrame(my_imputer.fit_transform(X_train))
imputed_X_valid = pd.DataFrame(my_imputer.transform(X_valid))
# Imputation removed column names; put them back
imputed_X_train.columns = X_train.columns
imputed_X_valid.columns = X_valid.columns
print("MAE from Approach 2 (Imputation):")
print(score_dataset(imputed_X_train, imputed_X_valid, y_train, y_valid))
MAE from Approach 2 (Imputation):
178166.46269899711
我们看到,方法2的MAE低于方法1,因此方法2在该数据集上表现更好。
方法3的得分(插补的扩展)
接下来,我们对缺失的值进行插补,同时跟踪插补的值。
# Make copy to avoid changing original data (when imputing)
X_train_plus = X_train.copy()
X_valid_plus = X_valid.copy()
# Make new columns indicating what will be imputed
for col in cols_with_missing:
X_train_plus[col + '_was_missing'] = X_train_plus[col].isnull()
X_valid_plus[col + '_was_missing'] = X_valid_plus[col].isnull()
# Imputation
my_imputer = SimpleImputer()
imputed_X_train_plus = pd.DataFrame(my_imputer.fit_transform(X_train_plus))
imputed_X_valid_plus = pd.DataFrame(my_imputer.transform(X_valid_plus))
# Imputation removed column names; put them back
imputed_X_train_plus.columns = X_train_plus.columns
imputed_X_valid_plus.columns = X_valid_plus.columns
print("MAE from Approach 3 (An Extension to Imputation):")
print(score_dataset(imputed_X_train_plus, imputed_X_valid_plus, y_train, y_valid))
MAE from Approach 3 (An Extension to Imputation):
178927.503183954
我们可以看到,方法3的表现比方法2稍差。
那么,为什么插补比删除列表现更好呢?
训练数据有10864行和12列,其中三列包含缺少的数据。对于每一列,缺少的条目不到一半。因此,删除列会删除很多有用的信息,因此插补的效果会更好。
# Shape of training data (num_rows, num_columns)
print(X_train.shape)
# Number of missing values in each column of training data
missing_val_count_by_column = (X_train.isnull().sum())
print(missing_val_count_by_column[missing_val_count_by_column > 0])
'''
(10864, 12)
Car 49
BuildingArea 5156
YearBuilt 4307
dtype: int64
'''
结论
与我们简单地删除缺失值的列(在方法1中)相比,通常情况下,填充缺失值(在方法2和方法3中)会产生更好的结果。
轮到你了
在本练习中,自己比较这些方法来处理缺失的价值观!
练习部分
现在轮到你测试关于缺失值处理的新知识了。你可能会发现这有很大的不同。
设置
下面的问题将为你的工作提供反馈。运行以下单元格设置反馈系统。
# Set up code checking
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.ex2 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
# 读取数据
X_full = pd.read_csv('../input/train.csv', index_col='Id')
X_test_full = pd.read_csv('../input/test.csv', index_col='Id')
# 删除目标值缺失的行,从预测中分享目标
# Remove rows with missing target, separate target from predictors
X_full.dropna(axis=0, subset=['SalePrice'], inplace=True)
y = X_full.SalePrice
X_full.drop(['SalePrice'], axis=1, inplace=True)
# 为简单,只保留数值型变量
X = X_full.select_dtypes(exclude=['object'])
X_test = X_test_full.select_dtypes(exclude=['object'])
# 从培训集中分离出验证数据
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()
您已经可以在前几行中看到一些缺失的值。在下一步中,您将更全面地了解数据集中缺少的值。
第1步:初步调查
运行下面的代码格,不做任何更改。
# 查看训练集结构,即(行,列) Shape of training data (num_rows, num_columns)
print(X_train.shape)
# 查看训练集各列中缺失值数量
missing_val_count_by_column = (X_train.isnull().sum())
print(missing_val_count_by_column[missing_val_count_by_column > 0])
(1168, 36)
LotFrontage 212
MasVnrArea 6
GarageYrBlt 58
dtype: int64
A 部分
以上输出回答以下问题。
# 填写以下行:培训数据中有多少行有缺失值?
num_rows = ____
#填写以下行:训练集中有多少列
num_cols_with_missing = ____
#填写以下行:所有的训练数据表格中包含多少缺失条目?
tot_missing = ____
# Check your answers
step_1.a.check()
B 部分
考虑到你以上的答案,你认为处理缺失值的最佳方法是什么?
由于数据中缺少的条目相对较少(缺失值百分比最大的列缺少的条目不到其条目的20%),我们可以预期删除列不太可能产生好的结果。这是因为我们会扔掉很多有价值的数据,因此插补可能会表现得更好。
为了比较处理缺失值的不同方法,您将使用教程中相同的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)
第2步:删除缺失值的列
在这一步中,您将预处理X_train和X_valid中的数据,以删除缺失值的列。将预处理的数据集分别设置为 reduced_X_train和reduced_X_valid。
#填写以下行:获取缺失值的列的名称
____ #你的代码在这里
# 填写以下行:在训练和验证数据中删除列
# Fill in the lines below: drop columns in training and validation data
reduced_X_train = ____
reduced_X_valid = ____
# Check your answers
step_2.check()
在不做任何更改的情况下运行以下代码,以获取此方法的MAE。
print("MAE (Drop columns with missing values):")
print(score_dataset(reduced_X_train, reduced_X_valid, y_train, y_valid))
MAE (Drop columns with missing values):
17837.82570776256
第3步:插补
A 部分
使用下一个代码单元格,用每列的平均值填充缺失值。将预处理的数据帧设置为 imputed_X_train和imputed_X_valid。确保列名与X_train和X_valid中的列名匹配。
from sklearn.impute import SimpleImputer
# 在以下行填写:插补 Fill in the lines below: imputation
____ # 你的代码
imputed_X_train = ____
imputed_X_valid = ____
# Fill in the lines below: imputation removed column names; put them back
imputed_X_train.columns = ____
imputed_X_valid.columns = ____
# Check your answers
step_3.a.check()
检查:当您更新启动程序代码时,Check() 会告诉您代码是否正确。您需要更新创建变量imputed_X_train,imputed_X_valid的代码
在不做任何更改的情况下运行下一个代码单元,以获取此方法的MAE。
print("MAE (Imputation):")
print(score_dataset(imputed_X_train, imputed_X_valid, y_train, y_valid))
MAE (Imputation):
18062.894611872147
B 部分
比较每种方法的MAE。结果有什么让你惊讶的吗?为什么你认为一种方法比另一种更好?
第四步:生成测试预测
在最后一步中,您将使用您选择的任何方法来处理缺少的值。在预处理了训练和验证功能之后,您将训练和评估一个随机森林模型。然后,在生成可提交给竞赛的预测之前,您将对测试数据进行预处理!
A 部分
使用以下代码单元预处理训练和验证数据。将预处理的数据集命名为final_X_train和final_X_valid。你可以在这里选择任何方法! 要将这些步骤标记为正确,只需确保以下情况:
- 预处理的数据集具有相同的列数
- 预处理的数据集没有缺失值,
- 最终的X_train和y_train具有相同的行数,并且 final_X_valid和y_valid的行数也相同。
总之一句话,训练集和验证集的数据没有缺失值,并具有相同的结构,即有相同的列(特征)。
# 预处理 训练集和验证集的特征
final_X_train = ____
final_X_valid = ____
# Check your answers
step_4.a.check()
运行以下代码单元来训练和评估随机森林模型。(请注意,我们不使用上面的 score_dataset() 函数,因为我们很快就会使用经过训练的模型来生成测试预测!)
# Define and fit model
# 定义和拟合模型
model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(final_X_train, y_train)
# Get validation predictions and MAE
# 获得验证数据的预测值和 MAE
preds_valid = model.predict(final_X_valid)
print("MAE (Your approach):")
print(mean_absolute_error(y_valid, preds_valid))
MAE (Your approach):
17791.59899543379
B 部分
使用下一个代码单元预处理测试数据。确保使用的方法与预处理培训和验证数据的方式一致,并将预处理的测试功能命名为final_X_test。
然后,使用预处理的测试特征和经训练后的模型在 preds_test
中生成测试预测值。
为了将此步骤能正常运行,您只需确保:
- 预处理的测试数据帧没有缺失值,并且
final_X_test
的行数与X_test
的行数相同。
需要测试的数据也相同的处理,得到与 A 部分 相同的列(特征),相同不改变原来测试集的行数。
# Fill in the line below: preprocess test data
# 填写以下行:处理测试集数据
final_X_test = ____
# Fill in the line below: get test predictions
# 填写以下行:获得测试集的预测值
preds_test = ____
# Check your answers
step_4.b.check()
运行下一个代码单元,不做任何更改,将结果保存到CSV
文件中,该文件可以直接提交给竞赛。
提交你的结果
一旦成功完成第4步后,您就可以将结果提交到排行榜了!(在上一个练习中,您还学习了如何执行此操作。如果您需要如何执行此操作的提示,请使用以下说明。)
首先,如果你还没有参加比赛,你需要参加。所以点击这个链接打开一个新窗口。然后点击“加入竞争”按钮。
接下来,按照以下说明操作:
- 首先单击窗口右上角的“保存版本”按钮。这将生成一个弹出窗口。
- 确保选择了“全部保存并运行”选项,然后单击“保存”按钮。
- 这将在笔记本的左下角生成一个窗口。完成运行后,单击“保存版本”按钮右侧的数字。这会在屏幕右侧显示一个版本列表。点击省略号(…)在最新版本的右侧,选择“在查看器中打开”。这将使您进入同一页面的查看模式。您需要向下滚动才能返回这些说明。
- 单击屏幕右侧的“输出”选项卡。然后,点击你想提交的文件,点击提交按钮,将结果提交到排行榜。
您现在已成功提交竞赛!
如果您想继续工作以提高性能,请选择屏幕右上角的编辑按钮。然后您可以更改代码并重复该过程。还有很大的改进空间,你将在工作中登上排行榜。
答案
### step 1
# How many rows are in the training data?
num_rows = 1168
# How many columns in the training data have missing values?
num_cols_with_missing = 3
# How many missing entries are contained in all of the training data?
tot_missing = 212 + 6 + 58
# step 2
# Get names of columns with missing values
cols_with_missing = [col for col in X_train.columns
if X_train[col].isnull().any()]
# Drop columns in training and validation data
reduced_X_train = X_train.drop(cols_with_missing, axis=1)
reduced_X_valid = X_valid.drop(cols_with_missing, axis=1)
# step 3
# Imputation
my_imputer = SimpleImputer()
imputed_X_train = pd.DataFrame(my_imputer.fit_transform(X_train))
imputed_X_valid = pd.DataFrame(my_imputer.transform(X_valid))
# Imputation removed column names; put them back
imputed_X_train.columns = X_train.columns
imputed_X_valid.columns = X_valid.columns
# step 4
# Imputation
final_imputer = SimpleImputer(strategy='median')
final_X_train = pd.DataFrame(final_imputer.fit_transform(X_train))
final_X_valid = pd.DataFrame(final_imputer.transform(X_valid))
# Imputation removed column names; put them back
final_X_train.columns = X_train.columns
final_X_valid.columns = X_valid.columns