在数据科学领域,数据往往是不完美的,充满了缺失值。这些缺失值可能会对分析结果造成偏差,因此数据科学家需要避免设计出指向无效结果的有偏估计。缺失数据是指在个体的一个或多个变量中没有可用值。由于缺失数据,分析的统计能力可能会降低,从而影响结果的有效性。本文将指导了解以下主题:缺失数据的原因、缺失数据的类型、检测缺失值的方法以及处理缺失值的策略。
缺失数据可能由多种原因引起。数据来自不同的来源,在挖掘数据时,有可能会丢失数据。然而,大多数情况下,缺失数据的原因是项目无响应,即人们不愿意(由于对问题缺乏了解)回答调查中的问题,一些人不愿意回答敏感问题,如年龄、工资、性别等。
在处理缺失值之前,了解缺失值的类别是必要的。有三种主要的缺失值类别:
如果给定数据集中的缺失值(Y)与其他变量或变量(Y)本身没有关系,那么这个变量就是完全随机缺失的。换句话说,当数据是MCAR时,缺失数据与任何值之间没有关系,缺失值没有特定的原因。
让理解以下例子:女性比男性更不愿意谈论年龄和体重。男性比女性更不愿意谈论工资和情感。这种缺失内容表明是随机缺失。MAR发生在缺失不是随机的,但缺失值与其他观测数据有系统关系,但与缺失数据本身没有关系。
这是缺失情况中最困难的一种。MNAR发生在缺失不是随机的,并且缺失值、观测值和缺失本身之间有系统关系。为了确认,如果缺失在两个或更多变量中持有相同的模式,可以用一个变量对数据进行排序并进行可视化。
数值检测缺失值:首先,检测数据集中每一列的缺失值百分比将有助于了解缺失值的分布情况。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings # 忽略任何警告
warnings.filterwarnings("ignore")
train = pd.read_csv("Train.csv")
mis_val = train.isna().sum()
mis_val_per = train.isna().sum() / len(train) * 100
mis_val_table = pd.concat([mis_val, mis_val_per], axis=1)
mis_val_table_ren_columns = mis_val_table.rename(columns={0: '缺失值', 1: '总值百分比'})
mis_val_table_ren_columns = mis_val_table_ren_columns[mis_val_table_ren_columns.iloc[:, :] != 0].sort_values('总值百分比', ascending=False).round(1)
mis_val_table_ren_columns
使用Missingno库可视化检测缺失值:
Missingno是一个简单的Python库,它提供了一系列的可视化方法来识别pandas数据框中缺失数据的行为和分布。它可以是条形图、矩阵图、热图或树状图的形式。
pip install missingno
import missingno as msno
msno.bar(train)
上面的条形图提供了数据集完整性的快速图形摘要。可以观察到Item_Weight、Outlet_Size列有缺失值。但如果能找到缺失数据的位置就更有意义了。
msno.matrix(train)
图表中出现白色的地方就是有缺失值的地方。一旦找到缺失数据的位置,就可以很容易地找出缺失数据的类型。
在分类了缺失值的模式之后,就需要对它们进行处理。
删除技术从数据集中删除缺失值。以下是缺失数据的类型:
当存在完全随机缺失的情况时,优先选择整行删除。整行删除也称为完整案例分析,因为它删除了包含一个或多个缺失值的所有数据。
train_1 = train.copy()
train_1.dropna()
如果数据集的规模较小,则不推荐使用整行删除,因为如果删除包含缺失值的行,那么数据集就会变得非常短,机器学习模型在小数据集上的表现也不会很好。
如果缺失是完全随机的,即MCAR,那么可以使用成对删除。成对删除比整行删除更能减少数据损失。它也称为可用案例分析,因为它只删除空值,而不是整行。
train_2 = train.copy()
train_2['Item_Weight'].mean() # pandas跳过缺失值并计算剩余值的平均值。
如果一列包含很多缺失值,比如说超过80%,并且该特征没有意义,那么可以删除整个列。
插补技术用替代值替换缺失值。根据数据的性质和问题,可以用多种方式进行插补。插补技术大致可以分为以下几类:
正如标题所暗示的——它用零或任何常数值替换缺失值。
from sklearn.impute import SimpleImputer
train_constant = train.copy()
mean_imputer = SimpleImputer(strategy='constant') # 使用常数值插补
train_constant.iloc[:, :] = mean_imputer.fit_transform(train_constant)
train_constant.isnull().sum()
语法与用常数插补相同,只是SimpleImputer的策略会改变。它可以是“Mean”或“Median”或“Most_Frequent”。
train_most_frequent = train.copy()
mean_imputer = SimpleImputer(strategy='most_frequent') # 策略也可以是mean或median
train_most_frequent.iloc[:, :] = mean_imputer.fit_transform(train_most_frequent)
train_most_frequent.isnull().sum()
train_knn = train.copy(deep=True)
from sklearn.impute import KNNImputer
knn_imputer = KNNImputer(n_neighbors=2, weights="uniform")
train_knn['Item_Weight'] = knn_imputer.fit_transform(train_knn[['Item_Weight']])
train_knn['Item_Weight'].isnull().sum()