缺失值是现实数据集中的常见问题,处理缺失值是数据预处理的关键步骤。缺失值的发生可能有多种原因,例如数据的结构和质量、数据输入错误、传输过程中的数据丢失或数据收集不完整。这些缺失值可能会影响机器学习模型的准确性和可靠性,因为它们可能会引入偏差和扭曲结果,有些模型甚至可能在存在缺失值的情况下根本无法工作。所以在建立模型之前,需要对缺失值进行适当的处理。
本文将展示如何使用三种不同级别的方法来处理这些缺失值:
初级: 删除、均值/中值插值、使用领域知识进行估计中级: 回归插值、K 最近邻(KNN) 插值高级: 链式方程的多元插补(MICE)、MICEforest
必须首先检查有多少个缺失值每个功能都有。作为探索性数据分析的一部分,我们可以使用以下代码来完成此操作:
导入pandas 并在数据集中读取它们,在下面的示例中,我们将使用葡萄酒质量数据集。
importpandasaspd df=pd.read_csv('Wine_Quality.csv') 然后您可以使用以下代码行检查缺失值。
df.isnull().sum()
df_filtered=df[df.isnull().any(axis=1)] df_filtered.head()
现在我们可以开始处理这些缺失值。
最简单的方法是删除行或列(属性)。这通常是在缺失值的百分比非常大或缺失值对分析或结果没有显着影响时进行的。
删除含有缺失值的行。
df_droprows=df.dropna() df_droprows.isnull().sum()
使用以下方法删除列或属性:
df_dropcols=df.drop(columns=['类型', '固定酸度', '柠檬酸', '挥发酸度', '残留糖', '氯化物', 'pH', '硫酸盐']) df_dropcols.isnull( ).sum() 通过删除行,我们最终得到更短的数据集。当特征被删除时,我们最终会得到一个完整的数据集,但缺少一些特征。
print('删除行时的形状: ', df_droprows.shape) print('删除特征时的形状: ', df_dropcols.shape)
这两种方法都是最直接的方法,都会导致宝贵数据——的丢失,所以一般不建议使用。
平均值/中位数插值
下一个基本方法是用特征的平均值或中位数替换缺失值。在这种情况下,不会丢失任何特征或行。但这种方法只能用于数值特征(如果使用平均值,我们应该确保数据集不倾斜或包含重要的异常值)。
例如,以下使用均值计算缺失值:
df=df.fillna(df.mean()) 现在让我们检查估计值之一:
df[df.index==86]
如果要使用中位数,可以使用:
df=df.fillna(df.median()) df[df.index==86]
可以看到这里中位数和平均值还是有差距的。
模式
与上面的方法一样,该方法用特征的众数或最常见的值替换缺失值。该方法可用于对特征进行分类。
首先,让我们检查一个类别是否占主导地位。我们通过value_counts方法可以达到:
df['type'].value_counts()
可见数量最多的是“白”。因此:可以通过以下方式填写
df['type']=df['type'].fillna(df['type'].mode())Scikit-Learn 的SimpleImputer 类
您还可以使用Scikit-learn 的SimpleImputer 类来执行均值、中位数和众数的插值。只需将策略设置为“mean”、“median”或“most_Frequency”
df_numeric=df.drop(columns='type') imputer_median=SimpleImputer(strategy='median') imputer_median.fit(df_numeric) df_impulated_median=pd.DataFrame(imputer_median.transform(df_numeric), columns=df_numeric.columns) df_impulated_median.head ()我们还可以将策略设置为“constant”并指定“fill_value”来填充常量值。
均值/中位数/众数: 的优点
它实施起来简单快捷,可以保留样本量,并降低机器学习模型等下游分析中的偏差风险。它比更复杂的方法在计算上更便宜。缺点:
未能考虑数据的变异性或分布可能会导致估计值不代表真实值。缺失值可能被低估或高估,尤其是在具有极端值或离群值的数据集中。减少方差并人为地夸大估算数据集中的相关系数。它假设缺失值是随机完全缺失(MCAR),但情况可能并非总是如此。使用领域知识进行评估
处理缺失数据的另一种可能的方法是使用基于领域知识或业务规则的估计来替换缺失值。可以通过咨询相关领域的专家来估计合理可信的缺失值,他们可以提供专业的见解。
但这种方法在现实中并不一定有效,因为我们需要专业人员来确保它产生准确可靠的估计,而该领域这样的专家并不多。所以我们把它放在这里的primary方法中。
还有稍微更先进的技术来填补那些缺失的值,我们将使用预测模型来解决问题。但在此之前,需要更好地理解缺失值的本质。
缺失值类型
在我们继续使用更先进的技术之前,我们需要考虑数据集中可能遇到的缺失类型。数据集中存在不同类型的缺失,了解缺失的类型可以帮助确定适当的方法。以下是一些常见的类型:
Random: 完全缺失在这种类型中,缺失值是完全随机的,这意味着值缺失的概率不依赖于任何观察到或未观察到的变量。例如,如果受访者不小心跳过了调查中的某个问题,则这就是MCAR。
随机缺失: 在这种类型中,值缺失的概率取决于观察到的变量而不是值本身。例如,如果受访者不太可能回答敏感问题,但不回答问题的倾向取决于可观察变量(例如年龄、性别和教育程度),那么这就是MAR。
非随机缺失: 在这种类型中,值缺失的概率取决于未观察到的变量,包括缺失值本身。例如,如果抑郁程度较高的个体报告抑郁水平的可能性较小,并且在数据中无法观察到不报告的趋势,那么这就是MNAR。
回归插值
我们将使用回归模型,通过分析数据集中的其他特征并使用它们的相关性来填充,对这些缺失值做出有根据的猜测。
在处理遵循特定模式(MAR 或MCAR)的缺失数据时,回归插补特别有用。因为当特征之间存在很强的相关性时,这种方法效果很好。
在这里,我们将创建一个不包含分类特征的数据版本。然后对每列中的缺失值拟合线性回归模型。这里需要用到Scikit-learn的线性回归模块。
importpandasaspd fromsklearn.linear_modelimportLinearRegression # 读取数据df=pd.read_csv('Wine_Quality.csv') # 制作仅包含数字特征的子数据框df=df.drop(columns='type') # 分隔缺失值的列Missing_cols=df。 columns[df.isna().any()].tolist() non_missing_cols=list(set(df.columns) -set(missing_cols)) print(missing_cols) # 循环遍历每个有缺失值的列forcolinmissing_cols: # 创建一个当前列中不存在缺失值的数据帧的副本df_temp=df.dropna(subset=[col] +non_missing_cols) # 将数据帧拆分为特征(X)和目标变量(y) X=df_temp[non_missing_cols] y=df_temp[col] # 创建并拟合线性回归模型lr=LinearRegression() lr.fit(X, y) # 使用拟合模型估算当前列中的缺失值df.loc[df[col].isna( ), col]=lr .predict(df.loc[df[col].isna(), non_missing_cols])
回归插值的优点:
可以处理大量缺失值。可以保留数据集的统计属性,例如均值、方差和相关系数。可以通过减少偏差和增加样本量来提高机器学习模型等下游分析的准确性。回归插值的缺点:
它假设缺失变量和观察到的变量之间存在线性关系。如果缺失值不是随机缺失(MAR)或完全随机缺失(MCAR),则可能会引入偏差。可能不适用于分类变量或序数变量。计算成本高昂且耗时,尤其是对于大型数据集。 (KNN)插值
另一种方法是使用聚类模型(例如K 最近邻(KNN))来估计这些缺失值。这与回归插补类似,只是使用不同的算法来预测缺失值。
importpandasaspd fromsklearn.imputeimportKNNImputer # 读取数据df=pd.read_csv('Wine_Quality.csv') # 制作仅包含数字特征的子数据框df=df.drop(columns='type') # 创建一个KNN imputer 对象imputer=KNNImputer(n_neighbors)=5) # 使用KNN插补缺失值df=pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
这里我们要介绍一个包fancyimpute,它包含了各种插值方法:
pip install fancyimpute 使用的方法如下:
# 导入必要的库importnumpyasnp importpandasaspd fromfancyimputeimportKNN # 加载数据集df=pd.read_csv('Wine_Quality.csv') # 删除非数字特征df=df.drop(columns='type') # 获取缺失值的列列表missing_cols=df.columns[df.isna().any()].tolist() # 创建KNN imputer 的实例imputer=KNN() # 将imputer 拟合并变换到数据集imputed_array=imputer.fit_transform(df[ Missing_cols]) # 替换原始数据集中的缺失值df[missing_cols]=impulated_array # 查看估算数据集dfKNN 插值的优点:
可以捕获变量之间复杂的非线性关系。不对数据的分布或变量之间的相关性做出任何假设。比均值或中位数插补等简单插补方法更准确,特别是对于中小型数据集。缺点:
计算成本可能很高,尤其是对于大型数据集或高维数据。距离度量的选择和选择的最近邻居的数量可能很敏感,这会影响准确性。对于高度倾斜或稀疏的数据表现不佳。
MICE 是一种常用的估算缺失数据的方法。它的工作原理是将每个缺失值替换为基于模型的一组合理值,该模型考虑了数据集中变量之间的关系。
该算法首先根据完整的其他变量集为数据集中的每个变量创建一个预测模型。然后使用相应的预测模型估算每个变量的缺失值。这个过程会重复多次,每轮插值都使用上一轮的插值,就好像它们是真的一样,直到实现收敛。
然后组合多个估算数据集以创建包含所有缺失数据的估算值的最终数据集。 MICE是一种强大而灵活的方法,可以处理具有许多缺失值和变量之间复杂关系的数据集。它已成为许多领域填充缺失数据的流行选择,包括社会科学、健康研究和环境科学。
fancyimpute包中包含了该方法的实现,我们可以直接使用。
importnumpyasnp importpandasaspd fromfancyimputeimportIterativeImputer # 读取数据df=pd.read_csv('Wine_Quality.csv') # 将type 列转换为类别(以便mouseforest 可以作为分类属性而不是字符串处理) df=df.drop(columns='type') # 获取缺失值的列列表missing_cols=df.columns[df.isna().any()].tolist() # 创建MICE算法的实例imputer=IterativeImputer() # 将imputer拟合到数据集imputed_array=imputer.fit_transform(df[missing_cols]) # 替换原始数据集中的缺失值df[missing_cols]=impulated_array # 查看imput数据集df 这个实现无法填充分类变量,那么我们应该如何处理分类变量?
会展森林
MICEforest 是MICE 的一个变体,它使用lightGBM 算法来估算数据集中的缺失值,这是一个很奇特的想法,对吧?
我们可以使用mouseforest 包来实现
pipinstallmiceforest #or condainstall-cconda-forgemiceforest 使用起来也非常简单:
importpandasaspd importmiceforestasmf # 读取数据df=pd.read_csv('Wine_Quality.csv') # 将type 列转换为类别(以便mouseforest 可以作为分类属性而不是字符串处理) df['type']=df['type'] .astype('category') # 创建MICE 算法的实例imputer=mf.ImputationKernel(data=df, save_all_iterations=True, random_state=42) # 将imputer 拟合到数据集。设置迭代次数为3 imputer.mice (3, verbose=True) # 生成估算数据集impulated_df=imputer.complete_data() # 查看估算数据集impulated_df 可以看到分类变量'type' 的缺失值已填写
这里介绍三个层次的缺失值处理方法。这三种方法的选择将取决于数据集、缺失数据量和分析目标。还需要仔细考虑输入缺失数据对最终结果的潜在影响。处理缺失数据是数据分析中的关键步骤,使用适当的插补方法可以帮助我们释放隐藏在数据中的见解,同时寻求主题专家的输入并评估输入数据的质量有助于确保后续分析的有效性。