前言
本文详细介绍缺失值处理、异常值检测、数据一致性处理。这是数据清洗的第一步,关键步骤。
1、缺失值类型
缺失值可分为三种类型:
完全随机缺失(MCAR):缺失完全随机,与其他变量无关。缺失的发生是完全随机的,没有可预测的规律或趋势。
随机缺失(MAR):缺失与其他变量有关,但与缺失数据本身的值无关。例如,在一个调查问卷中,如果男性比女性更倾向于不回答某个问题,那么这个缺失就与性别这个变量有关。
非随机缺失(MNAR):缺失与缺失变量本身的值有关。
2、判断缺失值类型
可以使用可视化分析或统计检验方法。
2.1 统计检验方法
回顾下统计检验方法的定义、基本概念、常见类型
定义:统计检验是统计学中用于判断数据是否支持某个假设的方法。它通过计算统计量和对应的p值来评估样本数据与假设之间的差异。
-
基本概念:
-
假设:
原假设(H0)
表示没有显著差异或效应的假设。例如,在t检验中,原假设通常是两个总体均值相等。备择假设(H1 或 Ha)
:表示存在显著差异或效应的假设。例如,在t检验中,备择假设可能是两个总体均值不相等。
-
假设:
显著差异:两个样本在某个统计量(如均值,中位数等)上存在显著的差别。这种差别不太可能是由于随机误差或抽样误差导致的。
统计量:衡量样本数据与假设之间的差异。常见的统计量包括
t统计量
、F统计量
、卡方统计量
等。p值:p值越小,表示样本数据与原假设之间的差异越显著。
显著性水平(α):事先设定的阈值,如0.05、0.01等。若p<α,则拒绝H0。
2.2 常见统计检验类型
t检验:用于比较两个样本均值的差异。用于
连续变量
。方差分析(ANOVA):用于比较三个或更多样本均值的差异。用于
连续变量
。卡方检验:用于检验
分类变量
之间的独立性或拟合优度。回归分析:用于检验变量之间的线性关系。用于
连续变量
。非参数检验:如Wilcoxon秩和检验、Kruskal-Wallis检验等,用于不满足参数检验假设的数据。
独立样本T检验的代码示例:
import pandas as pd
import numpy as np
from scipy.stats import ttest_ind
# 创建一个示例数据集
data = {
'A': [1, 2, 3, 4, 5, np.nan, 7, 8, 9, 10],
'B': [10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
'C': [np.nan, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
}
df = pd.DataFrame(data)
# 判断A列的缺失值类型
# 检查A列缺失值与其他变量的关系
missing_a = df[df['A'].isnull()]
non_missing_a = df[df['A'].notnull()]
# 对B列进行t检验
t_stat, p_value = ttest_ind(missing_a['B'], non_missing_a['B'], nan_policy='omit')
print(f"A列缺失值与B列的关系t检验结果:t统计量={t_stat:.4f}, p值={p_value:.4f}")
# 对C列进行t检验
t_stat, p_value = ttest_ind(missing_a['C'], non_missing_a['C'], nan_policy='omit')
print(f"A列缺失值与C列的关系t检验结果:t统计量={t_stat:.4f}, p值={p_value:.4f}")
# 如果A列缺失值与其他变量的p值均大于显著性水平(如0.05),则可能是MCAR
# 如果A列缺失值与其他变量的p值小于显著性水平,则可能是MAR
# 如果A列缺失值与其他变量的p值小于显著性水平,且缺失值与A列本身值有关,则可能是MNAR
SciPy
是一个开源的Python
库,建立在NumPy库之上,提供了大量的数学算法和工具,代码里SciPy包
的模块用到scipy.stats
统计函数,里面很多stats.mstats API。
T检验函数:ttest_ind(a, b, axis=0, equal_var=True, alternative='two-sided')。
2.3 Python中查询缺失值
以Pandas
为例,DataFrame 中NaN
。可以分为三种:
-
缺失值,三种写法:
np.nan
(Not a Number) 、None
和pd.NaT
(时间格式的空值,注意大小写不能错)。 -
空值:空字符串
""
- 还有导入的Excel,表示缺失值的字符
-
、?
等。
接下来进行用Python查询缺失值:
最常用的⽅法:
-
isnull()
,返回True表示此处为缺失值。 - 与
any()
⽅法搭配使用来查询存在缺失值的行。 - 与
sum()
⽅法搭配使用来查询存在缺失值的列。
df.isnull() # 缺失值为True
df.isnull().any(axis=1) # 缺失值的行为True
df.isnull().sum() # 缺失值的列为1
df[df.isnull().values==True] #输出为有缺失值的行
df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False) # 删除空值
isna()和isnull()的用法相同
查询字符-
、?
,用正则表达式:
import re
df[df["C列"].apply(lambda x: len(re.findall('NA|[*|?|!|#|-]', x)) != 0)]
df.apply()
函数可以用于对DataFrame的行或列应用一个函数。lambda
函数是一种简洁的匿名函数定义方式,常与apply()一起使用,以便在数据处理中快速定义和应用简单的函数。如df.apply(lambda row: row['A'] + row['B'] + row['C'], axis=1)
对每行求和,df.apply(lambda x: x * 2)
对每列的值乘2,不用循环函数了。
3、缺失值填补
3.1 均值/中位数/众数填充与插值方法
a) 均值填充:公式:x̄ = (Σx_i) / n
原理:用变量的平均值填充缺失值。适用于正态分布的数据
,但可能会降低数据的变异性。b) 中位数填充:公式:中位数 = 将数据排序后的中间值
原理:用变量的中位数填充缺失值。对异常值不敏感,适用于偏态分布数据
。c) 众数填充:公式:众数 = 出现频率最高的值
原理:用变量中出现最频繁的值填充缺失值。适用于分类变量
。
3.2 Python中sklearn
或pandas
实现均值/中位数/众数填充
最简单的是df.fillna(value)
,使用指定的值(如均值、中位数、众数等)填充缺失值。
SimpleImputer
是 scikit-learn
库中的一个类,用于对缺失数据进行填充。它可以根据指定的策略来填充缺失值,如 mean
(均值)、median
(中位数)、most_frequent
(众数)或 constant
(常数)。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.impute import SimpleImputer
# 创建示例数据集
np.random.seed(42)
df = pd.DataFrame({
'A': np.random.randn(100),
'B': np.random.randn(100),
'C': np.random.randn(100)
})
df.loc[np.random.choice(df.index, 20), 'A'] = np.nan
df.loc[np.random.choice(df.index, 20), 'B'] = np.nan
df.loc[np.random.choice(df.index, 20), 'C'] = np.nan
# 不同填充方法
methods = ['mean', 'median', 'most_frequent']
imputed_dfs = {}
for method in methods:
imputer = SimpleImputer(strategy=method)
imputed_dfs[method] = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
# 可视化比较
plt.figure(figsize=(15, 5))
for i, (name, imputed_df) in enumerate([('Original', df)] + [(m, imputed_dfs[m]) for m in methods]):
plt.subplot(1, 4, i + 1)
plt.scatter(imputed_df['A'], imputed_df['B'], alpha=0.5)
plt.title(f'{name.capitalize()} Imputation')
plt.xlabel('Feature A')
plt.ylabel('Feature B')
plt.tight_layout()
plt.show()
结果如下,
# 统计比较
for name, imputed_df in [('Original', df)] + [(m, imputed_dfs[m]) for m in methods]:
print(f"\n{name.capitalize()} Imputation Statistics:")
print(imputed_df.describe())
结果比较:
3.3 基于模型的缺失值填补(如KNN、MICE)
-
a) KNN(K-最近邻)填充:
原理:找到与缺失值样本最相似的K个样本,用这些样本的平均值填充。
-
算法步骤:
(1)计算所有样本间的距离(如欧氏距离)
(2)选择K个最近的邻居
(3)用这K个邻居的平均值填充缺失值
-
b) MICE(多重插补链式方程):
原理:使用其他变量的信息来预测缺失值,通过多次迭代来改善估计。
-
算法步骤:
(1)用简单方法(如均值)填充所有缺失值
(2)选择一个有缺失值的变量,将其他变量作为预测变量
(3)使用回归模型预测缺失值
(4)重复步骤2-3,直到所有变量都被处理
(5)重复多次整个过程,生成多个完整数据集
3.4 Python中sklearn
实现KNN、MICE填充
import pandas as pd
import numpy as np
from sklearn.impute import KNNImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
import matplotlib.pyplot as plt
# 创建示例数据集
np.random.seed(42)
df = pd.DataFrame({
'A': np.random.randn(100),
'B': np.random.randn(100),
'C': np.random.randn(100)
})
df.loc[np.random.choice(df.index, 20), 'A'] = np.nan
df.loc[np.random.choice(df.index, 20), 'B'] = np.nan
df.loc[np.random.choice(df.index, 20), 'C'] = np.nan
# KNN填充
knn_imputer = KNNImputer(n_neighbors=5)
df_knn = pd.DataFrame(knn_imputer.fit_transform(df), columns=df.columns)
# MICE填充
mice_imputer = IterativeImputer(random_state=0)
df_mice = pd.DataFrame(mice_imputer.fit_transform(df), columns=df.columns)
# 可视化比较
plt.figure(figsize=(15, 5))
for i, (name, imputed_df) in enumerate([('Original', df), ('KNN', df_knn), ('MICE', df_mice)]):
plt.subplot(1, 3, i + 1)
plt.scatter(imputed_df['A'], imputed_df['B'], alpha=0.5)
plt.title(f'{name} Imputation')
plt.xlabel('Feature A')
plt.ylabel('Feature B')
plt.tight_layout()
plt.show()
# 统计比较
for name, imputed_df in [('Original', df), ('KNN', df_knn), ('MICE', df_mice)]:
print(f"\n{name} Imputation Statistics:")
print(imputed_df.describe())
结果不做过多介绍,请各位自行测试。
4、总结
- 缺失值类型、如何判断缺失值类型
- Pandas中缺失值的表现方式,代码查询的方法
- 缺失值填充,用均值、中位数、KNN等等,展示代码
- 这里列一些Python中数据处理常用的包、用法,自行学习吧,非常有用的。
-
Pandas
包、Numpy
包、Scipy
包、Sklearn
包、Matplotlib
包 -
df.apply()
与lambda
函数结合使用 -
df.apply()
和groupby()
的结合使用 -
enumerate
的用法,它是一个迭代器
-