处理缺失值的端到端介绍

源节点: 1121996

这篇文章是作为 数据科学博客马拉松

概述

数据为我们提供了分析和预测未来事件的能力。 每天,越来越多的公司正在采用数据科学技术,如预测性预测、聚类等。 虽然不断学习复杂的 ML 和 DL 算法非常有趣,但不应忘记掌握基本知识 数据预处理. 数据预处理的重要部分之一是处理缺失值。 这是关于如何处理不同类型的缺失数据的完整指南。

内容

  1. 为什么处理缺失值很重要?
  2. 缺失值背后的原因
  3. 缺失值的类型
  4. 检查数据集中的缺失值
  5. 可视化缺失值
  6. 删除缺失值的行
  7. 删除缺失值的列
  8. 连续变量的插补
    1. 均值插补
    2. 中位数插补
  9. 用回归预测缺失值
  10. 分类数据中的缺失值
  11. 时间序列数据中的缺失值
    1. 前向填充缺失值
    2. 向后填充缺失值
    3. 线性插值
  12. 对缺失值鲁棒的算法
  13. 结论

为什么处理缺失数据很重要?

现实世界中的数据在大多数情况下都有很多缺失的数据。 每个值丢失的原因可能不同。 可能有数据丢失或损坏,或者也可能有特定原因。 缺失的数据会降低模型的预测能力。 如果你应用缺失数据的算法,那么参数估计就会出现偏差。 如果您不处理丢失的数据,您就无法对结果充满信心。

缺失值背后的原因

有没有想过数据集中丢失数据背后的原因?

数据丢失背后的一些可能原因是:

  • 人们不会在数据收集调查中提供有关某些问题的信息。 例如,有些人可能不愿意分享有关他们的工资、饮酒和吸烟习惯的信息。 这些是人们故意遗漏的
  • 在某些情况下,数据是从各种可用的过去记录中积累的,而不是直接积累的。 在这种情况下,数据损坏是一个主要问题。 由于维护率低,部分数据损坏导致数据丢失
  • 数据收集过程中的不准确也会导致数据丢失。 例如,在手动数据录入中,很难完全避免人为错误
  • 设备不一致导致测量错误,进而无法使用。

缺失值的类型

数据丢失的原因多种多样。 我们可以将它们分为三个主要组:完全随机丢失、随机丢失、不随机丢失。

1. 完全随机缺失 (MCAR)

缺失的数据不遵循任何特定模式,它们只是随机的。 这些数据的缺失与其余变量无关或独立。 不可能用其余的可变数据来预测这些值。 例如,在数据收集过程中,由于粗心大意丢失了特定样本。 这是一个理想的情况,统计分析不会有偏差。 但是除非非常确定,否则您不应该假设 MCAR 的存在,因为这是一种罕见的情况。

2. 随机缺失 (MAR)

与 MCAR 不同的是,这里的数据在特定子集中丢失。 可以借助其他功能来预测数据是否存在/不存在。 但是,您无法自己预测丢失的数据。

例如,让我们考虑一项关于在互联网上花费的时间的调查,其中有一个部分是关于在 Netflix、亚马逊 Prime 等平台上花费的时间。 据观察,老年人(45 岁以上)比年轻人更不可能填满它。 这是 MAR 的一个例子。 在这里,“年龄”参数决定数据是否会丢失。 MAR 比 MCAR 更常见。

3. 不随机丢失 (NMAR)

这是一个严重而棘手的情况。 假设调查的目的是衡量对社交媒体的过度使用/上瘾。 如果过度使用社交媒体的人没有故意填写调查,那么我们就有了 NMAR 的案例。 这很可能会导致结果的偏差。 通常的方法,如删除行/列,插补将不起作用。 为了解决这个问题,需要对该领域有深入的了解。

现在我们已经看到了不同类型的缺失数据,让我们继续讨论处理它们的各种方法。

检查缺失值

当您有数据集时,第一步是检查哪些列缺少数据以及有多少。 让我们用数据科学中最著名的数据集来学习,当然是泰坦尼克号幸存者! 使用 pandas read_csv 函数读取数据集,如下所示。

train=pd.read_csv('../input/titanic/train.csv') test=pd.read_csv('../input/titanic/test.csv') print('训练数据形状:', train.shape ) print('测试数据形状:', test.shape) train.head()

缺失值数据

来源:来自作者 Kaggle 笔记本的图片

现在我们有了泰坦尼克号数据的训练和测试数据框。

如何检查哪些列缺少数据,有多少?

“isnull()”函数用于此目的。 当您与 isnull 一起调用 sum 函数时,每列中缺失数据的总和就是输出。

missing_values=train.isnull().sum() 打印(missing_values)
乘客 ID 0 幸存 0 Pclass 0 姓名 0 性别 0 年龄 177 SibSp 0 Parch 0 票 0 票价 0 客舱 687 登船 2 dtype:int64

请注意,有 3 列缺少值:Age、Cabin、Embarked

尽管我们知道每列中缺失了多少个值,但必须知道它们占总数的百分比。 所以,让我们用一行代码来计算。

mis_value_percent = 100 * train.isnull().sum() / len(train) 打印(mis_value_percent)
PassengerId 0.000000 0.000000幸存0.000000 Pclass名称0.000000 0.000000性别年龄19.865320 SibSp 0.000000 0.000000烘干票务0.000000 0.000000票价客舱77.104377走上0.224467 D型:float64

很明显,77% 的“Cabin”列丢失了,这是一个非常重要的百分比。 Age 有大约 19% 的数据缺失,而 Embarked 仅缺失 0.2%。 这就是我们对缺失数据的定量分析。 定性呢? 继续阅读!

使用 Missingno 可视化缺失值

你猜怎么着? 我们有一个 python 包,专门用于可视化和探索数据集的缺失数据。 “Missingno”python 包。 继续并快速安装它

点安装缺失没有

使用它,我们可以以热图、条形图和矩阵的形式进行可视化。 通过分析它们的分布方式,您可以得出它们属于 MCAR、MAR 或 NMAR 的类别。 我们还可以找到包含缺失的列与目标列的相关性

首先使用missingno 库的'bar()' 函数为非空值制作条形图。 您已将 pandas 数据框传递给此函数。

导入 missingno 作为 msno msno.bar(train)

失踪没有| 缺失值来源:来自作者 Kaggle 笔记本的图片

接下来,我们可以绘制矩阵可视化。 这有助于我们了解缺失的数据是如何通过数据分布的,即它们是局部的还是均匀分布的,或者是否存在任何模式和许多此类问题。

msno.matrix(火车)
图案条

在矩阵图中,您将看到每个缺失数据的空行。 请注意,“Embarked”列只有两个随机缺失的数据,它们不遵循任何模式。 它们可能在数据采集过程中丢失了。 因此,这可以归类为完全随机丢失。

age 和 Cabin 列可能是 MAR。 但我们要确保它们之间没有相关性。

怎么做?

幸运的是,missingno 包还提供了“热图”功能。 使用它我们可以发现不同列的缺失数据之间是否存在任何相关性。

msno.heatmap(火车)

显示输出!

热图 | 缺失值

热图显示 Age 和 Cabin 列的缺失数据之间没有如此强的相关性。 因此,这些列的缺失数据可以归类为 MAR 或随机缺失。

我希望您清楚如何分析缺失值。 接下来,我将继续讨论处理这些缺失数据的不同方法。

删除缺失值的行

这是一种简单的方法,我们删除所有具有属于特定列的缺失值的行。 尽管这很容易,但它有一个巨大的缺点。 您最终可能会丢失大量数据。 这将减少数据集的大小并使您的模型预测有偏差。 只有当缺失值的数量非常少时,才应该使用它。

例如,“Embarked”列只有 2 个缺失值。 因此,我们可以删除缺少此列的行。 按照下面的代码片段。

print('Dataset before :', len(train)) train.dropna(subset=['Embarked'],how='any',inplace=True) print('Dataset after :', len(train)) print( '缺失值:',train['Embarked'].isnull().sum())
之前的数据集:891 之后的数据集:889 缺失值:0

想象一下,如果您对“年龄”列执行相同操作。 您将丢失大约 77% 的数据!

删除列

当一列有大量缺失值时,用我们拥有的最少可用的真实数据来估算这些值是没有意义的。 因此,当任何列的缺失值超过 80% 时,您只需从分析中删除该列即可。 在我们的例子中,“Cabin”缺少 77% 的数据,因此您可以选择删除此列。

确保删除的列对您的分析不重要。 如果是这样,您应该尝试获取更多数据,然后估算缺失值。

连续变量的插补

火车['年龄'][:10]
0 22.0 1 38.0 2 26.0 3 35.0 4 35.0 5 NaN 6 54.0 7 2.0 8 27.0 9 14.0 姓名:年龄,数据类型:float64
train['Age']=train['Age'].replace(np.NaN,train['Age'].mean()) train['Age'][:10]
0 22.000000 1 38.000000 2 26.000000 3 35.000000 4 35.000000 5 29.699118 6 54.000000 7 2.000000 8 27.000000 9 14.000000 64 XNUMX XNUMX XNUMX XNUMX XNUMX

您可以看到“NaN”已替换为 29.699(计算平均值)。

均值插补有一定的缺点。 如果数据分布很不均匀,有很多异常值,那么均值就不能反映数据的实际分布。 均值受极值或异常值的影响很大。 因此,如果数据没有很多异常值并且遵循接近正态分布,请使用均值插补

中位数插补

连续特征的缺失值可以用剩余非空值的中值填充。 与均值不同,中位数的优点是不受异常值的影响。 让我们在这里实现它。

train['Age']=train['Age'].replace(np.NaN,train['Age'].median()) train['Age'][:10]

均值插补

您可以观察到中值 (28.0) 已被填充以代替 NaN 值。

同样,您也可以执行模式插补。 通常,使用该模式进行插补对于分类缺失值很流行。 我将在后面的部分深入介绍

用回归预测缺失值

如果我们可以在其他变量的帮助下预测它们,而不是在所有地方填充单个平均值或中值,该怎么办?

是的! 我们可以使用具有非空值的特征来预测缺失值。 可以建立回归或分类模型来预测缺失值。 让我们为泰坦尼克号数据集的“年龄”列实现这一点。

我们可以处理用于构建模型的数据。 “年龄”特征将是目标变量。

x_train: 具有“年龄”值的数据集行将被过滤。 “年龄”是目标 x_测试: 它是具有非空值的“年龄”列

火车 将有缺失年龄值的数据,这将被预测(y_pred)

将熊猫导入为 pd data=pd.read_csv('../input/titanic/train.csv') data = data[["Survived", "Pclass", "Sex", "SibSp", "Parch", "Fare ", "Age"]] 数据["Sex"] = [1 if x=="male" else 0 for x in data["Sex"]] test_data = data[data["Age"].isnull()] data.dropna(inplace=True) x_train = data.drop("Age", axis=1) x_test = test_data.drop("Age", axis=1) y_train = data["Age"]

让我们为这些数据拟合一个线性回归模型。 我将在这里使用 sklearn 库。

从 sklearn.linear_model 导入 LinearRegression 模型 = LinearRegression() model.fit(X_train, y_train) y_pred = model.predict(X_test)

现在,我们在 y_pred 中预测了 Age 列的空值。

我将打印出几个测试输入和预测输出的示例,以便您更好地理解。

打印(x_test[:10])
幸存的 Pclass Sex SibSp Parch Fare 5 0 3 1 0 0 8.4583 17 1 2 1 0 0 13.0000 19 1 3 0 0 0 7.2250 26 0 3 1 0 0 7.2250 28 1 3 0 0 0 7.8792 29 0 3 1 0 0 7.8958 31 1 1 0 1 0 146.5208 32 1 3 0 0 0 7.7500 36 1 3 1 0

这就是将输入传递给回归模型的方式。 让我们看看预测的年龄值。

打印(y_pred[:10])
[29.07080066 30.10833306 22.44685065 29.08927347 22.43705181 29.07922599 32.43692984 22.43898701 22.15615704 29.07922599

欢呼! 我们得到了值。

分类数据中的缺失值

到现在为止,我们看到了如何处理缺失的数值数据。 如果在分类特征的情况下数据丢失怎么办? 例如,泰坦尼克号数据集的“Cabin”特征是分类的。 在这里,我们无法计算均值和中位数。 因此,我们可以使用模式或最常出现的类/类别来填充缺失值。

train['Cabin']=train['Cabin'].fillna(train['Cabin'].value_counts().index[0])

当缺失值的百分比较小时,首选此方法。 它不会造成大量数据丢失,并且具有统计相关性。

但是,如果您有很多缺失值,那么用最频繁的类进行插补是没有意义的。 相反,让我们为缺失值创建一个单独的类别,例如“未知”或“不可用”。 班级将增加一班。

当缺失值来自分类列(字符串或数字)时,缺失值可以替换为最常见的类别。 如果缺失值的数量非常大,则可以将其替换为新类别。

train['Cabin']=train['Cabin'].fillna('Unknown') train['Cabin'][:10]

客舱行 | 填写 na

它适用于小数据集。 它还通过添加唯一类别来消除数据丢失。

如何处理时间序列数据中的缺失值?

以有序的方式收集信息和时间戳的数据集被表示为时间序列数据。 如果您在时间序列数据中有缺失值,您显然可以尝试上述任何一种方法。 但是也有一些特定的方法可以在这里使用。

为了得到一个想法,我将创建一个简单的虚拟数据集。

time= pd.date_range("1/01/2021", period=10, freq="W") df = pd.DataFrame(index=time); df[“售出单位”] = [5.0,4.0,np.nan,np.nan,1.0,np.nan,3.0,6.0,np.nan,2.0]; 打印(df)

售出单位

让我们继续讨论方法

前向填充缺失值

下一行的值将用于填充缺失值。'ffill'代表'向前填充'。 这很容易实现。 您只需要在 fillna() 函数中将“method”参数作为“ffill”传递。

forward_filled=df.fillna(method='ffill') 打印(forward_fill)

向前填充

向后填充缺失值

在这里,我们使用前一行的值来填充缺失值。 'bfill' 代表向后填充。 在这里,您需要传递 'bfill' 作为方法参数。

向后填充 = df.fillna(method='bfill') 打印(向后填充)

后耳塞满

我希望您能够通过上面的图片发现两种情况的不同之处。

线性插值

时间序列数据有很多变化。 上述使用回填和向前填充的插补方法并不是最好的解决方案。 线性插值来拯救你!

在这里,这些值填充有递增或递减的值。 它是一种插补技术,试图绘制数据点之间的线性关系。 它使用可用的非空值来计算缺失点。

插值 = df.interpolate(limit_direction="both") 打印(插值)

缺失值的线性插值

将这些值与前后填充进行比较,然后自己检查哪个好!

这些是处理时间序列数据中缺失值的一些基本方法

对缺失值鲁棒的算法

在某些情况下,上述方法均无效。 但是,您需要进行分析。 然后,您应该选择支持缺失值的算法。 KNN(K 个最近邻)就是这样一种算法。 它将通过取 K 个最近值中的大多数来考虑缺失值。 随机森林对于具有缺失值的分类数据也很稳健。 许多基于决策树的算法,如 XGBoost、Catboost 都支持缺失值的数据。

结论

总而言之,第一步是探索数据并找出哪些变量缺少数据,百分比是多少,以及它属于哪个类别。 在此之后,您将对可以尝试的方法有一个战略性的想法。 除了线性回归模型之外,一个有用的提示是尝试使用 K 最近邻算法进行插补。 如果上述方法不起作用,您可以查找一些最近的方法,例如使用 Datawig 或 Hot-Deck Imputation 方法。

我希望你喜欢阅读。

您可以通过以下方式与我联系: [电子邮件保护]

LinkedIn: 数据预处理

本文中显示的媒体不归 Analytics Vidhya 所有,由作者自行决定使用。

资料来源:https://www.analyticsvidhya.com/blog/2021/10/end-to-end-introduction-to-handling-missing-values/

时间戳记:

更多来自 分析维迪亚