在进行数据竞赛中,数据预处理阶段经常需要对数据进行缺失值处理。关于缺失值的处理并没有想象中的那么简单。以下为一些经验分享,基本涵盖了大部分处理方式。
在前两种情况下可以根据其出现情况删除缺失值的数据,同时,随机缺失可以通过已知变量对缺失值进行估计。在第三种情况下,删除包含缺失值的数据可能会导致模型出现偏差,同时,对数据进行填充也需要格外谨慎。正确判断缺失值的类型,能给我们的工作带来很大的便利,但目前还没有一套规范的缺失值类型判定标准。大多是依据经验或业务进行判断。
解决缺失数据问题的方法主要有:成列删除、成对删除、虚拟变量调整、插补、多重插补和最大似然等。
missing_value_formats = ["n.a.","?","NA","n/a", "na", "--"]
df = pd.read_csv("employees.csv", na_values = missing_value_formats)
df.isna().sum()
成列删除(listwise deletion)
常见的操作方法:删除掉所有存在缺失值的个案。
优点:
可用于任何类型的统计分析。 不需要特别的运算方法。 如果数据是MCAR,则减少的样本将会是原样本的一个随机次样本。 如果任何因变量缺失数据的概率不取决于自变量的值,则使用成列删除的回归估计值将会是无偏误的。 缺点:
标准误通常较大。 如果数据不是MCAR而只是MAR,那么成列删除可能会产生有偏误的估计值。(例如,教育缺失数据的概率取决于职业地位,那么对于二者的回归会产生一个有偏误的回归系数估计值。) 成对删除(pairwise deletion)
一般的备选方案,在进行多变量的联立时,只删除掉需要执行的变量的缺失数据。例如在ABC三个变量间,需要计算A和C的协方差,那么只有同时具备A/C的数据会被使用。文献指出,当变量间的相关性普遍较低时,成对删除会产生更有效的估计值。然而当变量间的相关性较高时,建议还是使用成列删除。理论上成对删除不建议作为成列删除的备选方案。这是一种保守的处理方法,最大限度地保留了数据集中的可用信息。
优点:如果数据为MCAR,成对删除就产生一致的参数估计值(在大样本中接近无偏误),且有比成列删除更少的抽样变异(较小的真实标准误),而当变量间相关性普遍较低时,成对删除会产生更有效的估计值。
缺点:
DataFrame.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
参数说明:
# 删除所有含空的行
df.dropna(inplace=True)
# 删除某列含控制的行
df.dropna(subset=['列名'],inplace=True)
新建两个变量,其中一个变量D为“是否缺失”,缺失值设为0,存在值设为1。
另一个变量X’,将缺失值设为c(可以是任何常数),存在值设为本身。随后,对X’,D和其他变量(因变量和其他预设模型中的自变量)进行回归。这种调整的好处是它利用了所有可用的缺失数据的信息(是否缺失)。为了便利,一个好的c的设置方式是现有非缺失数据X的均数。这样做的好处是,D的系数可以被解释成“在控制了其他变量的情况下,X具缺失数据的个体其Y的预测值减去具X平均数的个体于Y的预测值”
相对丢弃而言,补全是更加常用的缺失值处理方式。通过一定的方法将缺失的数据补上,从而形成完整的数据记录,对于后续的数据处理、分析和建模至关重要。常用的补全方法如下。
当你对自己手头的数据集足够了解时,可以选择自己填写缺失值。
将空值作为一种特殊的属性值来处理,它不同于其他的任何属性值。如所有的空值都用“unknown”填充。一般作为临时填充或中间过程。有时可能导致严重的数据偏离,一般不推荐。
将初始数据集中的属性分为数值属性和非数值属性来分别进行处理。
如果空值是数值型的,就根据该属性在其他所有对象的取值的平均值来填充该缺失的属性值; 如果空值是非数值型的,就根据统计学中的众数原理,用该属性在其他所有对象的取值次数最多的值(即出现频率最高的值)来补齐该缺失的属性值。
与其相似的另一种方法叫条件平均值填充法(Conditional Mean Completer)。在该方法中,用于求平均的值并不是从数据集的所有对象中取,而是从与该对象具有相同决策属性值的对象中取得。这两种数据的补齐方法,其基本的出发点都是一样的,以最大概率可能的取值来补充缺失的属性值,只是在具体方法上有一点不同。与其他方法相比,它是用现存数据的多数信息来推测缺失值。
对于一个包含空值的对象,热卡填充法在完整数据中找到一个与它最相似的对象,然后用这个相似对象的值来进行填充。不同的问题可能会选用不同的标准来对相似进行判定。该方法概念上很简单,且利用了数据间的关系来进行空值估计。这个方法的缺点在于难以定义相似标准,主观因素较多。
最为典型的代表是K均值(K-means clustering),先根据欧式距离或相关分析来确定距离具有缺失数据样本最近的K个样本,将这K个值加权平均来估计该样本的缺失数据。
同均值插补的方法都属于单值插补,不同的是,它用层次聚类模型预测缺失变量的类型,再以该类型的均值插补。
假设X=(X1,X2…Xp)为信息完全的变量,Y为存在缺失值的变量,那么首先对X或其子集行聚类,然后按缺失个案所属类来插补不同类的均值。如果在以后统计分析中还需以引入的解释变量和Y做分析,那么这种插补方法将在模型中引入自相关,给分析造成障碍。
先根据欧式距离或相关分析来确定距离具有缺失数据样本最近的K个样本,将这K个值加权平均来估计该样本的缺失数据。这个方法要求我们选择k的值(最近邻居的数量),以及距离度量。KNN既可以预测离散属性(k近邻中最常见的值)也可以预测连续属性(k近邻的均值)。
根据数据类型的不同,距离度量也不尽相同:
KNN算法最吸引人的特点之一在于,它易于理解也易于实现。其非参数的特性在某些数据非常“不寻常”的情况下非常有优势。
KNN算法的一个明显缺点是,在分析大型数据集时会变得非常耗时,因为它会在整个数据集中搜索相似数据点。此外,在高维数据集中,最近与最远邻居之间的差别非常小,因此KNN的准确性会降低。
这种方法是用空缺属性值的所有可能的属性取值来填充,能够得到较好的补齐效果。但是,当数据量很大或者遗漏的属性值较多时,其计算的代价很大,可能的测试方案很多。一般不推荐。
另有一种方法,填补遗漏属性值的原则是一样的,不同的只是从决策相同的对象中尝试所有的属性值的可能情况,而不是根据信息表中所有对象进行尝试,这样能够在一定程度上减小原方法的代价。
这种方法是用空缺属性值的所有可能的属性取值来试,并从最终属性的约简结果中选择最好的一个作为填补的属性值。这是以约简为目的的数据补齐方法,能够得到好的约简结果;但是,当数据量很大或者遗漏的属性值较多时,其计算的代价很大。
另一种称为条件组合完整化方法(Conditional Combinatorial Complete),填补遗漏属性值的原则是一样的,不同的只是从决策相同的对象中尝试所有的属性值的可能情况,而不是根据信息表中所有对象进行尝试。条件组合完整化方法能够在一定程度上减小组合完整化方法的代价。在信息表包含不完整数据较多的情况下,可能的测试方案将巨增。
基于完整的数据集,建立回归方程,或利用机器学习中的回归算法。对于包含空值的对象,将已知属性值代入方程来估计未知属性值,以此估计值来进行填充。当变量不是线性相关时会导致有偏差的估计。较常用。但是要注意防止过拟合。
EM算法是一种在不完全数据情况下计算极大似然估计或者后验分布的迭代算法。在每一迭代循环过程中交替执行两个步骤:
在缺失类型为随机缺失的条件下,假设模型对于完整的样本是正确的,那么通过观测数据的边际分布可以对未知参数进行极大似然估计(Little and Rubin)。这种方法也被称为忽略缺失值的极大似然估计,对于极大似然的参数估计实际中常采用的计算方法是期望值最大化(Expectation Maximization,EM)。
该方法比删除个案和单值插补更有吸引力,它一个重要前提:适用于大样本。有效样本的数量足够以保证ML估计值是渐近无偏的并服从正态分布。但是这种方法可能会陷入局部极值,收敛速度也不是很快,并且计算很复杂。
多值插补的思想来源于贝叶斯估计,认为待插补的值是随机的,它的值来自于已观测到的值。具体实践上通常是估计出待插补的值,然后再加上不同的噪声,形成多组可选插补值。根据某种选择依据,选取最合适的插补值。
多重插补方法分为三个步骤:
多重插补和贝叶斯估计的思想是一致的,但是多重插补弥补了贝叶斯估计的几个不足。
同时,多重插补保持了单一插补的两个基本优点,即应用完全数据分析方法和融合数据收集者知识的能力。相对于单一插补,多重插补有三个极其重要的优点:
多重插补也有以下缺点:
通过寻找属性间的关系来对遗失值填充。它寻找之间具有最大相关性的两个属性,其中没有遗失值的一个称为代理属性,另一个称为原始属性,用代理属性决定原始属性中的遗失值。这种基于规则归纳的方法只能处理基数较小的名词型属性。
就几种基于统计的方法而言,删除元组法和平均值法差于热卡填充法、期望值最大化方法和多重填充法;回归是比较好的一种方法,但仍比不上hot deck和EM;EM缺少MI包含的不确定成分。值得注意的是,这些方法直接处理的是模型参数的估计而不是空缺值预测本身。它们合适于处理无监督学习的问题,而对有监督学习来说,情况就不尽相同了。譬如,你可以删除包含空值的对象用完整的数据集来进行训练,但预测时你却不能忽略包含空值的对象。另外,C4.5和使用所有可能的值填充方法也有较好的补齐效果,人工填写和特殊值填充则是一般不推荐使用的。
DataFrame.fillna(value=None,method=None,axis=None,inplace=False,limit=None,downcast=None,**kwargs)
参数说明:
# 用常量0填充
df1 = df.fillna(0)
# 用字典填充
df2 = df.fillna({'Normal':60,'exam':40})
# 指定method = 'ffill'/'pad':用前一个非缺失值去填充该缺失值
df2 = df.fillna(method='ffill')
# 将exam列的缺失值用均值替换
exa_mea = df['exam'].fillna(df['exam'].mean())
# 将Normal列的缺失值用中位数替换
Nor_med = df['Normal'].fillna(df['Normal'].median())
# 使众数(mode)填充
df['Normal'].fillna(df['Normal'].mode())
# 使用随机方式填充
df["column"].fillna(lambda x: random.choice(df[df[column] != np.nan]["column"]), inplace =True)
Series 和 DataFrame 对象都有interpolate()方法,默认情况下,该函数在缺失值处执行线性插值。这个方法是利用数学方法来估计缺失点的值,对于较大的数据非常有用。
DataFrame.interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', limit_area=None, downcast=None, **kwargs)
参数说明:
常用的有以下几种方法:
选择一种插值方法时,考虑的因素包括运算时间、占用计算机内存和插值的光滑程度。一般来说:
任何回归都是从特征矩阵中学习,然后求解连续性标签y的值,之所以能实现这个过程,是因为回归算法认为,特征矩阵和标签之前存在着某种关系,实际上特征和标签是可以相互转化的,比如说用地区,环境,附近学校数量预测房价的问题,我们既可以用地区,环境,附近学校数量的数据来预测房价,也可以反过来,用环境,附近学校数量和房价来预测地区,而回归填补缺失值,正式利用了这种情况。
def fill_dependents_missing(data, to_fill):
df = data.copy()
columns = [*df.columns]
columns.remove(to_fill)
X = df.loc[:, columns]
y = df.loc[:, to_fill]
X_train = X.loc[df[to_fill].notnull()]
y_train = y.loc[df[to_fill].notnull()]
X_pred = X.loc[df[to_fill].isnull()]
rfr = RandomForestRegressor(random_state=22, n_estimators=200, max_depth=3, n_jobs=-1)
rfr.fit(X_train, y_train)
y_pred = rfr.predict(X_pred).round()
df.loc[df[to_fill].isnull(), to_fill] = y_pred
return df
在数据预处理阶段,对于具有缺失值的数据记录不做任何处理,也是一种思路。这种思路主要看后期的数据分析和建模应用,很多模型对于缺失值有容忍度或灵活的处理方法,因此在预处理阶段可以不做处理。
常见的能够自动处理缺失值的模型包括:KNN、决策树和随机森林、神经网络和朴素贝叶斯、DBSCAN(基于密度的带有噪声的空间聚类)等。这些模型对于缺失值的处理思路是:
在数据建模前的数据归约阶段,有一种归约的思路是降维,降维中有一种直接选择特征的方法。假如我们通过一定方法确定带有缺失值(无论缺少字段的值缺失数量有多少)的字段对于模型的影响非常小,那么我们根本就不需要对缺失值进行处理。
因此,后期建模时的字段或特征的重要性判断也是决定是否处理字段缺失值的重要参考因素之一。
对于缺失值的处理思路是先通过一定方法找到缺失值,接着分析缺失值在整体样本中的分布占比,以及缺失值是否具有显著的无规律分布特征,然后考虑后续要使用的模型中是否能满足缺失值的自动处理,最后决定采用哪种缺失值处理方法。