在使用机器学习算法对分类和回归预测问题进行建模时,缺失值可能会导致问题。
一种常见的方法是用计算出的统计量(例如列的平均值)替换缺失值。这允许数据集像往常一样进行建模,但没有向模型表明该行原始包含缺失值。
解决此问题的一种方法是包含额外的二进制标志输入特征,这些特征指示行或列是否包含已填充的缺失值。此附加信息可能有助于模型预测目标值,也可能无助于模型预测目标值。
在本教程中,您将学习如何为建模添加缺失值的二进制标志。
完成本教程后,您将了解:
- 如何在具有缺失值的分类数据集上加载和评估具有统计插补的模型。
- 如何添加一个标志来指示一行是否有一个以上缺失值,并使用此新特征评估模型。
- 如何为每个具有缺失值的输入变量添加一个标志,并使用这些新特征评估模型。
通过我的新书《机器学习数据准备》启动您的项目,其中包括分步教程和所有示例的Python源代码文件。
让我们开始吧。
- 2020年7月更新:修复了创建标志变量中的错误。

为机器学习的缺失值添加二进制标志
图片由keith o connell提供,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 填充马疝数据集
- 使用缺失值的二进制标志进行建模
- 使用所有缺失值指示器进行建模
填充马疝数据集
马疝气数据集描述了患有疝气的马匹的医学特征以及它们是存活还是死亡。
有300行和26个输入变量,一个输出变量。这是一个二元分类预测任务,涉及预测马匹存活为1,死亡为2。
在这个数据集中,我们可以选择预测许多字段。在这种情况下,我们将预测问题是否为手术(列索引23),使其成为一个二元分类问题。
数据集中许多列都有大量缺失值,每个缺失值都用问号字符(“?”)标记。
下面提供了数据集中带有标记缺失值的行示例。
1 2 3 4 5 |
2,1,530101,38.50,66,28,3,3,?,2,5,4,4,?,?,?,3,5,45.00,8.40,?,?,2,2,11300,00000,00000,2 1,1,534817,39.2,88,20,?,?,4,1,3,4,2,?,?,?,4,2,50,85,2,2,3,2,02208,00000,00000,2 2,1,530334,38.30,40,24,1,1,3,1,3,3,1,?,?,?,1,1,33.00,6.70,?,?,1,2,00000,00000,00000,1 1,9,5290409,39.10,164,84,4,1,6,2,2,4,4,1,2,5.00,3,?,48.00,7.20,3,5.30,2,1,02208,00000,00000,1 ... |
你可以在此处了解更多关于此数据集的信息:
无需下载数据集,我们将在工作示例中自动下载。
在加载的数据集中使用 Python 将缺失值标记为 NaN(非数字)是一种最佳实践。
我们可以使用read_csv() Pandas函数加载数据集,并指定“na_values”将“?”值加载为缺失值,并用NaN值标记。
下面的示例下载数据集,将“?”值标记为NaN(缺失),并总结数据集的形状。
1 2 3 4 5 6 7 8 9 10 |
# 总结马疝气数据集 from pandas import read_csv # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv' dataframe = read_csv(url, header=None, na_values='?') data = dataframe.values # 分割输入和输出元素 ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] print(X.shape, y.shape) |
运行示例会下载数据集并报告行数和列数,符合我们的预期。
1 |
(300, 27) (300,) |
接下来,我们可以评估此数据集上的模型。
我们可以使用SimpleImputer类执行统计插补,并用每列的平均值替换缺失值。然后,我们可以在数据集上拟合一个随机森林模型。
有关如何使用SimpleImputer类的更多信息,请参阅教程
为了实现这一点,我们将定义一个管道,该管道首先执行插补,然后拟合模型,并使用重复分层K折交叉验证(三次重复和10折)评估此建模管道。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 评估马疝数据集的均值插补和随机森林 from numpy import mean from numpy import std from pandas import read_csv from sklearn.ensemble import RandomForestClassifier from sklearn.impute import SimpleImputer from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.pipeline import Pipeline # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv' dataframe = read_csv(url, header=None, na_values='?') # 分割输入和输出元素 data = dataframe.values ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] # 定义建模管道 model = RandomForestClassifier() imputer = SimpleImputer() pipeline = Pipeline(steps=[('i', imputer), ('m', model)]) # 定义模型评估 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1) print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores))) |
运行示例使用马疝数据集上的均值统计插补评估随机森林。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,管道的估计分类准确率约为86.2%。
1 |
平均准确率:0.862 (0.056) |
接下来,让我们看看是否可以通过提供更多关于缺失值的信息来提高模型的性能。
想开始学习数据准备吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
使用缺失值的二进制标志进行建模
在上一节中,我们用计算出的统计量替换了缺失值。
模型不知道缺失值已被替换。
行是否包含缺失值的知识在模型进行预测时可能很有用。
向模型公开此知识的一种方法是提供一个额外的列,该列是一个二进制标志,指示该行是否具有缺失值。
- 0:行不包含缺失值。
- 1:行包含缺失值(已/将被插补)。
这可以直接在加载的数据集上实现。首先,我们可以对每行求和以创建一个新列,如果该行包含至少一个NaN,则和将为NaN。
然后,我们可以将新列中的所有值标记为1(如果它们包含NaN),否则标记为0。
最后,我们可以将此列添加到加载的数据集中。
综合起来,下面列出了添加二进制标志以指示每行中一个或多个缺失值的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 添加一个二进制标志,指示行是否包含缺失值 from numpy import isnan from numpy import hstack from pandas import read_csv # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv' dataframe = read_csv(url, header=None, na_values='?') # 分割输入和输出元素 data = dataframe.values ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] print(X.shape) # 对每行求和,其中包含 NaN 的行将求和为 NaN a = X.sum(axis=1) # 将所有非 NaN 标记为 0 a[~isnan(a)] = 0 # 将所有 NaN 标记为 1 a[isnan(a)] = 1 a = a.reshape((len(a), 1)) # 作为另一列添加到数据集 X = hstack((X, a)) print(X.shape) |
运行示例首先下载数据集并按预期报告行数和列数。
然后创建新的二进制变量,指示一行是否包含缺失值,并将其添加到输入变量的末尾。然后报告输入数据的形状,确认添加了该特征,从27列增加到28列。
1 2 |
(300, 27) (300, 28) |
然后,我们可以像上一节中那样使用额外的二进制标志评估模型,并查看它是否会影响模型性能。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# 评估具有缺失值和已插补缺失值的二进制标志的模型性能 from numpy import isnan from numpy import hstack from numpy import mean from numpy import std from pandas import read_csv from sklearn.ensemble import RandomForestClassifier from sklearn.impute import SimpleImputer from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.pipeline import Pipeline # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv' dataframe = read_csv(url, header=None, na_values='?') # 分割输入和输出元素 data = dataframe.values ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] # 对每行求和,其中包含 NaN 的行将求和为 NaN a = X.sum(axis=1) # 将所有非 NaN 标记为 0 a[~isnan(a)] = 0 # 将所有 NaN 标记为 1 a[isnan(a)] = 1 a = a.reshape((len(a), 1)) # 作为另一列添加到数据集 X = hstack((X, a)) # 定义建模管道 model = RandomForestClassifier() imputer = SimpleImputer() pipeline = Pipeline(steps=[('i', imputer), ('m', model)]) # 定义模型评估 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1) print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores))) |
运行示例会报告马疝数据集上带有附加特征和插补的平均值和标准偏差分类准确度。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们看到性能从86.2%略微提升到86.3%。差异很小,可能不具有统计学意义。
1 |
平均准确率:0.863 (0.055) |
此数据集中的大多数行都有缺失值,此方法可能在缺失值较少的数据集上更有益。
接下来,让我们看看是否可以向模型提供更多关于缺失值的信息。
使用所有缺失值指示器进行建模
在上一节中,我们添加了一个额外的列来指示一行是否包含缺失值。
更进一步是指出每个输入值是否缺失并已插补。这有效地为每个包含缺失值的输入变量添加了一个额外的列,并且可能为模型带来好处。
这可以通过在定义SimpleImputer实例时将“add_indicator”参数设置为True来实现。
1 2 3 |
... # 插补并标记缺失值 X = SimpleImputer(add_indicator=True).fit_transform(X) |
我们可以通过一个实际示例来演示这一点。
下面的示例像以前一样加载马疝数据集,然后对整个数据集进行缺失值插补,并为每个具有缺失值的输入变量添加指示器变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 插补并为具有缺失值的列添加指示器 from pandas import read_csv from sklearn.impute import SimpleImputer # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv' dataframe = read_csv(url, header=None, na_values='?') data = dataframe.values # 分割输入和输出元素 ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] print(X.shape) # 插补并标记缺失值 X = SimpleImputer(strategy='mean', add_indicator=True).fit_transform(X) print(X.shape) |
运行示例首先下载并按预期汇总数据集的形状,然后应用插补并添加二进制(1和0值)列,指示给定输入变量的每行是否包含缺失值。
我们可以看到输入变量的数量已从27增加到48,这表明添加了21个二进制输入变量,反过来,27个输入变量中有21个必须包含至少一个缺失值。
1 2 |
(300, 27) (300, 48) |
接下来,我们可以使用此附加信息评估模型。
下面的完整示例演示了这一点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 评估使用添加的指示器特征对马疝数据集的插补 from numpy import mean from numpy import std from pandas import read_csv from sklearn.ensemble import RandomForestClassifier from sklearn.impute import SimpleImputer from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.pipeline import Pipeline # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv' dataframe = read_csv(url, header=None, na_values='?') # 分割输入和输出元素 data = dataframe.values ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] # 定义建模管道 model = RandomForestClassifier() imputer = SimpleImputer(add_indicator=True) pipeline = Pipeline(steps=[('i', imputer), ('m', model)]) # 定义模型评估 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1) print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores))) |
运行示例会报告马疝数据集上带有附加指示器特征和插补的平均值和标准偏差分类准确度。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们看到性能从上一节的86.3%显著提升到86.7%。
这可能提供了强有力的证据,表明对于此数据集和所选模型,为每个已输入列添加一个标志是一种更好的策略。
1 |
平均准确率:0.867 (0.055) |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
相关教程
总结
在本教程中,您学习了如何为建模添加缺失值的二进制标志。
具体来说,你学到了:
- 如何在具有缺失值的分类数据集上加载和评估具有统计插补的模型。
- 如何添加一个标志来指示一行是否有一个以上缺失值,并使用此新特征评估模型。
- 如何为每个具有缺失值的输入变量添加一个标志,并使用这些新特征评估模型。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
我认为你应该交换行
# 将所有非 NaN 标记为 0
a[~isnan(a)] = 0
# 将所有 NaN 标记为 1
a[isnan(a)] = 1
谢谢!
我同意,谢谢!已修复。
感谢分享!
我知道这是一个技术性很强的帖子,但仍然
值得一提的是,最好检查一下缺失/不缺失本身是否是目标类(例如)的强指标(例如,在单变量分析中),因为很多时候这意味着数据采集过程中存在偏差。
例如,在二元分类中,如果80%的阳性类别样本有缺失值,而
30%的阴性类别样本有缺失值,那么很可能这是一个问题。
好建议!
如果是这种情况,您应该怎么做?
很棒的教程。我看到许多人谈论对数值变量进行此操作。我们也对分类变量进行插补。为什么此过程不用于分类变量而仅用于数值变量?
你好休斯顿…您可能会对以下内容感兴趣
https://www.analyticsvidhya.com/blog/2021/04/how-to-handle-missing-values-of-categorical-variables/