在医疗领域,对原始数据的收集往往是以内容为导向的,而对其格式的要求并不严谨。比如,某个病人去一次门诊所产生的数据,有些可能是手写在病历上的,这些数据可能会有缺失,如时间项没有填写;可能会有错误,如姓名项写成了同音字;还有可能遭到损坏,如治疗建议由于字迹潦草已经无法辨认。这样的数据显然不能直接拿来使用,因为错误的数据不可能得到正确的结果。
同时,数据可能有许多不同的来源,尤其那些非结构化的数据,数据之间的格式可能相差甚远。当我们把这些不同来源的数据合并在一起时,就会出现大量的数据缺失和格式混乱。因此数据分析师常说,他们花了80%的时间来做数据预处理的工作,包括加载、清洗、转换、合并和重新排列。其中删除错误值、处理缺失值的过程就叫作数据清洗。
当前可用来实现数据预处理的软件工具有很多,在这里我们介绍一个强大、快速、易用且灵活的开源数据处理和分析工具pandas。pandas是Python的一个第三方库,也就是说,只要掌握了Python编程语言的基本用法,就可以很容易地使用pandas。
pandas提供了一系列的数据结构和函数,使用它们可以有效地处理结构化、表格化数据,完成数据清洗、整合和计算工作。具体的使用方法可以查阅pandas官方网站提供的技术文档,本节的后续内容将重点讲解数据清洗的思路。表2.1列出了一些pandas统计计算函数示例。
表2.1 部分pandas统计计算函数
下面我们讲解几种可以提高数据准确性和完整性的清洗方法。
缺失值,顾名思义,是指数据库中空缺的值,图2.1是一个示例。处理缺失值的办法主要有删除和插补两种。
图2.1 国家统计局医疗相关数据中的缺失值
删除操作包括删除一行和删除一列两种方法。就图2.1所示的情况而言,删除一行相当于删掉了一个样本,适用于某一行中的数据缺失关键变量的情况,这样可以去掉质量比较差的样本。比如,2020年的数据就缺失过多,可以考虑进行删除操作。值得注意的是,删除行会减少样本的总数量,因此有可能对后续的数据分析过程造成影响。
删除一列则是去掉那些缺失值过多的变量,这种操作只应该在中前期处理原始数据时进行,一般在有缺失值的变量与我们要研究的命题相关性不强时使用。比如图2.1中的街道卫生院数这个变量,自2016年起就不再统计,如果这个数据不是我们关注的重点,就可以进行删除操作。
插补操作的具体方法有很多种,总体思路就是在尽量不影响结果的前提下往缺失处填上合适的值。在缺失数据较少时可以尝试寻找资料手工填补,但这么做的成本非常高。相对而言,使用该变量的平均值或者中位数进行填补则效率高得多。
比较复杂的插补策略有KNN方法,即用其他变量定义一个距离概念,然后用与缺失值样本最相近的 K 个样本的变量值确定该样本的变量值。还有线性回归方法,即寻找一个与缺失变量线性相关的其他变量,建立该变量与缺失变量的线性回归模型,然后计算出缺失变量值。
异常值是指数据集中那些偏离了正常取值区间的值,也被称为离群点。用直方图等数据可视化方法有时可以轻松地找到异常值。不过,要高效、自动地找到异常值,往往要借助以下办法。
❑ 统计描述,既包括做直方图,也包括用分位数判断。所谓分位数,就是在一组从小到大排列的数据中,占据某些特定位置的数据。比如二分位数,就是将数据分为数量相等的两部分的那个数据,也叫中位数。类似地,百分位数就是将数据分为数量相等的100份的那99个值,记作 P 1 , P 2 , …, P 99 。通常会把小于 P 5 和大于 P 95 的数据作为异常值。
❑ 3 σ 原则,对于那些近似服从正态分布的数据,可以把与平均值相差超过3倍标准差的值都视作异常值,因为根据正态分布的性质,这样的值出现的概率小于0.3%。
❑ 对数据集进行建模,比如对数据集进行回归分析,把与模型预估值相差比较大的数据值视作异常值。
❑ 对数据集进行聚类,将聚类结果中离各个大类距离都比较远的数据值视作异常值。
不过,在找到异常值之后该如何进行处理,则不一而足,应该根据实际情况决定。有时候异常值很少,是由于数据损坏导致的,可以作为缺失值处理。有时候所用的分析算法对异常值不敏感,那么不处理也可以。还有的时候异常值正是我们所需要的关键数据,比如在监控住院病人的生理指标分析其病情变化过程时,可能需要将这部分数据专门提出来进行处理。
如图2.2所示为2019年世界各国累计感染新冠肺炎的每10万人案例数,使用直方图进行描述。可以看出数据整体呈现双峰分布,最左侧是每10万人累计感染案例不足1000的国家,有99个,说明疫情控制得当、总发病率不足1%的国家有99个。数据的另一个分布高峰在7000~8000之间,这也许说明在疫情控制措施不够严密的情况下,新冠肺炎的发病率是7%~8%。而最右侧发病率超过17%的这个数据就属于异常值。
图2.2 2019年世界各国每10万人感染新冠肺炎病例情况
噪声不是指某个或者某些特殊的数据值,而是指那些在观测过程中影响变量的观测结果的一些随机误差。比如某个体重秤没有校零,那么凡是由这个体重秤称出的体重数据就都带有这个噪声。我们可以把观测值和真实值之间的关系描述为:
观测值=真实值+噪声
有时候噪声太大也可能会导致产生异常值,不过一般情况下,噪声只会在数据上造成微小的扰动。这些扰动可能会使最终的分析结果产生偏差,或者让机器学习模型的性能变差,所以对数据的降噪处理就十分重要。这一步也被称作数据平滑。
分箱法是处理噪声的一种简单好用的方法,其背后的思想很简单,如果扰动是随机的,那么噪声既可能是正值,也可能是负值。若把相邻数据一起处理就可以让正负噪声相互抵消。这个过程分为两步,第一步是装箱,把相邻数据放在一起。第二步是取替代值,可以是箱内数据的平均数、中位数或是箱子的上下边缘值等。
图2.3中的数据为各国每10万人中累计感染新冠肺炎病例数的一部分,左边是原始数据,通过取整处理得到了右边降噪后的数据。分箱法不仅可以用来降噪,还可以作为离散化方法,用来把连续数据变为分类数据。
图2.3 分箱法降噪
另一种处理噪声的方法是回归法。与处理缺失值或异常值中提到的一样,回归法需要一个数据稳定可靠的其他变量,且该变量与我们要处理的变量有相关性。回归法同样也分两步:第一步是对两个变量做回归分析,得到解析表达式;第二步是把我们选择的那个变量的数据值代入表达式中,用计算出的值取代要处理的变量的原始数据值,也就是把所有数据点都落到回归线上去。
如图2.4所示,图中横坐标为2019年各国新冠肺炎感染病例总数,而纵坐标为各国新冠肺炎死亡病例总数。这两个变量之间存在明显的线性相关性,拟合直线的斜率即为新冠肺炎的死亡率(1.8%)。将图中所有数据点沿着竖直方向移动到拟合直线上,就完成了对死亡病例总数的回归降噪。
图2.4 回归法降噪