![Python机器学习(原书第3版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/531/38458531/b_38458531.jpg)
4.1 处理缺失数据
在实际应用中,出于各种原因导致样本缺少一个或多个数值的现象并不少见。可能在数据收集的过程中出现了错误、某些测量不适当,也可能是某个字段在调查时为空白。常见的缺失值是数据表中的空白或占位符,如NaN
,它表示该位置不是一个数字,或者是NULL
(在关系型数据库中常用的未知值指示符)。不幸的是,大多数计算工具都无法处理这些缺失值,忽略它们甚至可能会产生不可预知的结果。因此,在对数据进行进一步分析之前,我们必须事先处理好缺失值。本节将介绍几种处理缺失值的实用技术,包括从数据集删除这些条目或用其他训练样本和特征填充。
4.1.1 识别数据中的缺失值
在讨论处理缺失值技术之前,先从逗号分隔值(comma-separated values, CSV)文件创建一个简单的数据框(data frame),以便更好地理解这一问题:
![085-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/085-01.jpg?sign=1739025902-3AGqv190DJpxOyUMZynRCk7AgE78OyFk-0-e479c2cec9e59ea33b3114bdfd1c0a75)
上面的代码调用read_csv
函数把CSV格式的数据读入pandas DataFrame
,结果发现有两个失踪的表单元被NaN
所取代。前面代码示例中的StringIO
函数只用于说明。从csv_data
读入数据到pandas的DataFrame
,就像用硬盘上的普通CSV文件一样方便。
对比较大的DataFrame
,手工查找缺失值可能很烦琐。在这种情况下,可以调用isnull
方法返回包含布尔值的DataFrame
,指示一个表单元是包含了数字型的数值(False
)还是数据缺失(True
)。调用sum
方法,可以得到每列缺失值的数量,如下所示:
![086-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/086-01.jpg?sign=1739025902-goyOhMEsLD81SMyYO54Jpiodj8X74ozC-0-7aef437a5f5a06f6f3578359ed26ecdd)
这样就可以统计表中每列缺失值的情况。下一小节将介绍处理这些缺失数据的策略。
用panda数据框来方便数据处理
尽管scikit-learn最初只是为NumPy数组开发的,但是有时用pandas的DataFrame
预处理数据可能更方便。如今,大多数的scikit-learn函数都支持将DataFrame
对象作为输入,但是,由于scikit-learn的API对NumPy数组的处理更加成熟,因此我们建议尽可能使用NumPy数组。在为scikit-learn估计器提供数据之前,随时可以通过values
属性来存取DataFrame
底层NumPy数组中的数据:
![086-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/086-02.jpg?sign=1739025902-mVoQA2JGS9AIZCRUFLaRyTMrWPScv6Bl-0-c19678c14b8b678ab1cc1c6155a74b36)
4.1.2 删除有缺失值的训练样本或特征
处理缺失数据最简单的方法是从数据集中彻底删除相应的特征(列)或训练样本(行),调用dropna
方法可以很容易地删除有缺失值的数据行:
![086-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/086-03.jpg?sign=1739025902-Xx5IadkeBAIrxCBENx5plRJLv4X84a3f-0-a2019066e2069345f2de949f23d797d2)
类似地,也可以通过设置axis
参数为1来删除其中至少包含一个NaN
的列:
![086-04](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/086-04.jpg?sign=1739025902-nGnie99xHbIt9hYnYllWo7zV8zSChkFR-0-4e0f59f66ec29a3c5727d0443ff93940)
dropna
方法还支持一些其他的参数,有时候也可以派上用场:
![086-05](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/086-05.jpg?sign=1739025902-qwzWaDwZehAu9TW0v8jvbyr5Zngab4Kc-0-5dff504e680ba77aac3e10ef3a09f577)
![087-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/087-01.jpg?sign=1739025902-han2NtFT8hfOkG5pZ7v86UAgmViE5PwF-0-15158261ffc900c93d19a0ff96196d1f)
虽然删除缺失数据似乎很方便,但是也有一定的缺点。例如,可能最终会因为删除太多样本而使分析变得不太可靠,也可能因为删除太多特征列使分类器无法获得有价值的信息。下一节将研究处理缺失数据的最常用方法之一,即插值技术。
4.1.3 填补缺失值
删除训练样本或整列特征通常不可行,因为这会损失太多有价值的数据。在这种情况下,可以用不同的插值技术,根据其他的训练样本来估计缺失值。最常见的插值技术是均值插补(mean imputation),我们只需要用整个特征列的平均值来替换缺失值。一个方便的实现方式是调用scikit-learn的SimpleImputer
类,如下面的代码所示:
![087-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/087-02.jpg?sign=1739025902-4BdBIVFUA0TSjcmYPWBaTGC2nAk7XAfR-0-744f33bc03a7b4f58f168ee1a4f87d3e)
在这里,我们用相应的均值替换每个NaN
值,该均值是针对每个特征列分别计算的。strategy
参数的其他选项还包括median
或most_frequent
,其中后者用最频繁值来替代缺失的数据值。这对插补分类特征值非常有用,例如存储红、绿、蓝颜色码的特征列。本章后面将会给出此类数据的示例。
填补缺失值的另一种更方便的方法是使用pandas的fillna
方法,并提供一个插补方法作为参数。例如,使用pandas,可以通过以下的命令直接在DataFrame
对象中实现相同的均值插补(如图4-1所示):
![087-03](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/087-03.jpg?sign=1739025902-LQpJvcTt5eENbKZd2NRxo079g3ErEHiJ-0-1b3d1b2f7fbdb2b07a312ecb32377457)
![087-04](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/087-04.jpg?sign=1739025902-rpb0VTKHKY6SQCn1Nigc4lH7IYuNpPmz-0-af015521599a81b6e6dd0ab7bfd17e7e)
图 4-1
4.1.4 了解scikit-learn估计器API
在上一节中,我们用scikit-learn库的SimpleImputer
类插补数据集中的缺失值。SimpleImputer
类属于scikit-learn库中用来进行数据转换的转换器(transformer)类。fit
方法用于从训练数据中学习参数,transform
方法利用这些参数来转换数据,这两个方法是估计器的必要方法。任何要转换的数据数组都必须要有与拟合模型的数据数组具有相同数量的特征。
图4-2展示了如何用在训练数据集上拟合的转换器转换训练数据集和新测试数据集。
![088-01](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/088-01.jpg?sign=1739025902-rrOeZ7BzZwlE478rIJYpcA81FNhkuEn1-0-87dd1b465ac26019b6eb131e2d569d51)
图 4-2
我们在第3章中所涉及的分类器就属于所谓的scikit-learn估计器,该API在概念上与转换器类非常相似。估计器有一个predict
方法,但也可能有一个transform
方法,在本章稍后部分你会看到。你可能还记得,在训练估计器进行分类时,我们也曾调用fit
方法来学习模型的参数。然而,在监督学习任务中,我们为拟合模型额外提供了分类标签,它们能用于通过predict
方法对新数据样本进行预测,如图4-3所示。
![088-02](https://epubservercos.yuewen.com/CE1019/20240330708910706/epubprivate/OEBPS/Images/088-02.jpg?sign=1739025902-4dEEHwlyjORfxlj6rc2qr9G9Q6sbTfqN-0-255ec2d48017d57628bc28ace99ba43d)
图 4-3