数据清洗与异常值处理代码解析

本文将详细解析一段用于数据清洗和异常值处理的Python代码,该代码使用了pandasnumpyscipymatplotlib等库,实现了数据去重、相似度计算、缺失值处理、插值以及异常值识别等功能。

1. 导入必要的库

import pandas as pd
import numpy as np
from scipy.interpolate import interp1d, lagrange
import matplotlib.pyplot as plt
import os

代码解析

  • pandas:用于数据处理和分析,提供了高效的数据结构如DataFrameSeries
  • numpy:用于数值计算,提供了多维数组对象和各种数学函数。
  • scipy.interpolate:提供了插值函数,如interp1dlagrange
  • matplotlib.pyplot:用于数据可视化,绘制图表。
  • os:用于与操作系统进行交互,处理文件和目录路径。

2. 构建文件路径

准备工作

在这里插入图片描述
2.
在这里插入图片描述

# 构建文件路径
# 构建文件路径
data_dir = 'data'
tmp_dir = 'tmp'

3. 读取数据

代码 4 - 7

download_path = os.path.join(data_dir, 'user_download.csv')
print(download_path)
# 使用 GB2312 编码读取文件
download = pd.read_csv(download_path, index_col=0, encoding='gb2312')
print(download)

代码解析

  • pd.read_csv():用于读取CSV文件。
  • index_col=0:指定第一列为索引列。
  • encoding='gb2312':指定文件编码为gb2312。
结果

在这里插入图片描述

4. 数据去重

方法一:自定义去重函数

# 定义去重函数
def del_rep(list1):
    list2 = []
    for i in list1:
        if i not in list2:
            list2.append(i)
    return list2

# 去重
# 将下载意愿从数据框中提取出来
download_list = list(download['是否愿意下载'])
print('去重前下载意愿选项总数为:', len(download_list))
download_rep = del_rep(download_list)  # 使用自定义的去重函数去重
print('方法一去重后下载意愿选项总数为:', len(download_rep))
print('用户选项为:', download_rep)

代码解析

  • del_rep():自定义的去重函数,通过遍历列表,将不在新列表中的元素添加到新列表中。
  • download['是否愿意下载']:从DataFrame中提取是否愿意下载列。
  • list():将Series对象转换为列表。

结果

在这里插入图片描述

方法二:使用set去重

# 代码 4 - 8
# 方法二
print('去重前下载意愿选项总数为:', len(download_list))
download_set = set(download_list)  # 利用set的特性去重
print('方法二去重后下载意愿选项总数为:', len(download_set))
print('用户选项为:', download_set)

代码解析

  • set():将列表转换为集合,集合的元素具有唯一性,从而实现去重。
结果

在这里插入图片描述

检测与处理重复值

在这里插入图片描述
在这里插入图片描述

方法三:使用drop_duplicates方法

在这里插入图片描述

# 代码 4 - 9
# 对下载意愿去重
download = pd.read_csv(download_path, encoding='gb2312')
download_select = download['是否愿意下载'].drop_duplicates()
print('drop_duplicates方法去重之后下载意愿选项总数为:', len(download_select))

代码解析

  • drop_duplicates()pandasSeries对象的方法,用于去除重复值。
    在这里插入图片描述
结果

在这里插入图片描述

对用户总信息表去重

# 代码 4 - 10
all_info = pd.read_csv('./data/user_all_info.csv',encoding="gbk")
print('去重之前用户的形状为:', all_info.shape)
shape_det = all_info.drop_duplicates(subset=['用户编号', '编号']).shape
print('依照用户编号,编号去重之后用户总信息表大小为:', shape_det)

代码解析

  • drop_duplicates(subset=['用户编号', '编号']):根据用户编号编号列去除重复行。
结果

在这里插入图片描述

5. 相似度计算

计算年龄和每月支出的相似度

# 代码 4 - 11
# 求取年龄和每月支出的相似度
corr_det = all_info[['年龄', '每月支出']].corr(method='kendall')
print('年龄和每月支出的相似度矩阵为:\n', corr_det)

代码解析

  • corr(method='kendall'):计算相关系数矩阵,method='kendall'表示使用Kendall秩相关系数。

结果

在这里插入图片描述

计算居住类型、年龄和每月支出的相似度

# 代码 4 - 12
import pandas as pd

all_info = pd.read_csv('./data/user_all_info.csv',encoding="gbk")

# 将“居住类型”列转换为数值类型(标签编码,假设只有城市和农村)
all_info['居住类型'] = all_info['居住类型'].map({'城市': 1, '农村': 0})

# 计算 Pearson 相关系数
corr_det1 = all_info[['居住类型', '年龄', '每月支出']].corr(method='pearson')
print('居住类型、年龄和每月支出的 Pearson 法相似度矩阵为:\n', corr_det1)

代码解析

  • corr(method='pearson'):计算相关系数矩阵,method='pearson'表示使用皮尔逊相关系数。
结果

在这里插入图片描述

6. 特征相等矩阵计算与去重

定义求取特征是否完全相同的矩阵的函数

在这里插入图片描述

# 代码 4 - 13
# 定义求取特征是否完全相同的矩阵的函数
def feature_equals(df):
    # 使用DataFrame的列名初始化一个全为False的矩阵,确保结构正确
    df_equals = pd.DataFrame(False, index=df.columns, columns=df.columns)
    for i in df.columns:
        for j in df.columns:
            # 比较列i和列j是否完全相等(数据和索引均一致)
            df_equals.loc[i, j] = df[i].equals(df[j])
    return df_equals

# 应用上述函数
app_desire = feature_equals(all_info)
print('app_desire的特征相等矩阵的前7行7列为:\n', app_desire.iloc[:7, :7])

代码解析

  • feature_equals():自定义函数,用于计算特征相等矩阵。
  • equals():用于比较两个Series对象是否完全相同。

结果

在这里插入图片描述

去除重复列

# 代码 4 - 14
# 遍历所有数据
len_feature = app_desire.shape[0]
dup_col = []
for m in range(len_feature):
    for n in range(m + 1, len_feature):
        if app_desire.iloc[m, n] & (app_desire.columns[n] not in dup_col):
            dup_col.append(app_desire.columns[n])

# 进行去重操作
print('需要删除的列为:', dup_col)
all_info.drop(dup_col, axis=1, inplace=True)
print('删除多余列后all_info的特征数目为:', all_info.shape[1])

代码解析

  • 通过遍历特征相等矩阵,找出重复的列名。
  • drop(dup_col, axis=1, inplace=True):删除指定的列,axis=1表示列方向,inplace=True表示在原DataFrame上进行修改。
结果

7. 缺失值处理

在这里插入图片描述

查看缺失值和非缺失值数量

# 代码 4 - 15
print('all_info每个特征缺失的数目为:\n', all_info.isnull().sum())
print('all_info每个特征非缺失的数目为:\n', all_info.notnull().sum())

代码解析

  • isnull().sum():计算每个特征的缺失值数量。
  • notnull().sum():计算每个特征的非缺失值数量。

结果

在这里插入图片描述

删除包含缺失值的行

在这里插入图片描述

# 代码 4 - 16
print('去除缺失的行前all_info的形状为:', all_info.shape)
all_info1 = all_info.dropna(axis=0, how='any')
print('去除缺失的行后all_info的形状为:', all_info1.shape)
all_info1_path = os.path.join(tmp_dir, 'all_info_notnull.csv')
all_info1.to_csv(all_info1_path, index=False)

在这里插入图片描述

代码解析

  • dropna(axis=0, how='any'):删除包含缺失值的行,axis=0表示行方向,how='any'表示只要有一个缺失值就删除该行。
  • to_csv():将DataFrame保存为CSV文件,index=False表示不保存索引列。
结果

在这里插入图片描述

用均值填充缺失值

在这里插入图片描述
在这里插入图片描述

# 代码 4 - 17
# 求每月支出平均值
mean_num = all_info['每月支出'].mean()
# 缺失值替换为均值
all_info['每月支出'] = all_info['每月支出'].fillna(mean_num)
print('每月支出特征缺失的数目为:\n', all_info['每月支出'].isnull().sum())

代码解析

  • mean():计算均值。
  • fillna(mean_num):用均值填充缺失值。
结果

在这里插入图片描述

8. 插值方法

在这里插入图片描述

线性插值

# 代码 4 - 18
# 线性插值
# 创建自变量x
x = np.array([1, 2, 3, 4, 5, 8, 9, 10])
# 创建因变量y1
y1 = np.array([2, 8, 18, 32, 50, 128, 162, 200])
# 创建因变量y2
y2 = np.array([3, 5, 7, 9, 11, 17, 19, 21])
# 线性插值拟合x、y1
linear_ins_value1 = interp1d(x, y1, kind='linear')
# 线性插值拟合x、y2
linear_ins_value2 = interp1d(x, y2, kind='linear')
print('当x为6、7时,使用线性插值y1为:', linear_ins_value1([6, 7]))
print('当x为6、7时,使用线性插值y2为:', linear_ins_value2([6, 7]))

代码解析

  • interp1d(x, y, kind='linear'):创建线性插值函数,kind='linear'表示线性插值。
结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

拉格朗日插值

# 拉格朗日插值
large_ins_value1 = lagrange(x, y1)  # 拉格朗日插值拟合x、y1
large_ins_value2 = lagrange(x, y2)  # 拉格朗日插值拟合x、y2
print('当x为6,7时,使用拉格朗日插值y1为:', large_ins_value1([6, 7]))
print('当x为6,7时,使用拉格朗日插值y2为:', large_ins_value2([6, 7]))

代码解析

  • lagrange(x, y):创建拉格朗日插值多项式。
结果

在这里插入图片描述

样条插值

# 样条插值
# 样条插值拟合x、y1
y1_new = np.linspace(x.min(), x.max(), 10)
f = interp1d(x, y1, kind='cubic')  # 编辑插值函数格式
spline_ins_value1 = f(y1_new)  # 通过相应的插值函数求得新的函数点
# 样条插值拟合x、y2
y2_new = np.linspace(x.min(), x.max(), 10)
f = interp1d(x, y2, kind='cubic')  # 编辑插值函数格式
spline_ins_value2 = f(y2_new)  # 通过相应的插值函数求得新的函数点
print('使用样条插值y1为:', spline_ins_value1)
print('使用样条插值y2为:', spline_ins_value2)

代码解析

  • interp1d(x, y, kind='cubic'):创建三次样条插值函数。
  • np.linspace():生成指定区间内的等间距数组。
结果

在这里插入图片描述

9. 异常值识别

3σ原则识别异常值

在这里插入图片描述

# 代码 4 - 19
all_info = pd.read_csv(all_info1_path)

# 定义3σ原则来识别异常值函数
def out_range(ser1):
    std = ser1.std()
    if std == 0:
        return pd.Series([])
    bool_ind = (ser1.mean() - 3 * std > ser1) | (ser1.mean() + 3 * std < ser1)
    index = np.arange(ser1.shape[0])[bool_ind]
    outrange = ser1.iloc[index]
    return outrange

outlier = out_range(all_info['年龄'])
print('使用3σ原则判定异常值个数为:', outlier.shape[0])
print('异常值的最大值为:', outlier.max())
print('异常值的最小值为:', outlier.min())

代码解析

  • out_range():自定义函数,使用3σ原则识别异常值。
  • std():计算标准差。
  • mean():计算均值。
结果

在这里插入图片描述

箱线图识别异常值

在这里插入图片描述

# 代码 4 - 20
plt.figure(figsize=(10, 8), dpi=1080)
p = plt.boxplot(list(all_info['年龄'].values))  # 画出箱线图
outlier1 = p['fliers'][0].get_ydata()  # fliers为异常值的标签
img_path = os.path.join(tmp_dir, '用户年龄异常数据识别.jpg')
plt.savefig(img_path)
plt.show()
print('年龄数据异常值个数为:', len(outlier1))
print('年龄数据异常值的最大值为:', max(outlier1))
print('年龄数据异常值的最小值为:', min(outlier1))

代码解析

  • plt.boxplot():绘制箱线图。
  • p['fliers'][0].get_ydata():获取箱线图中的异常值。
  • plt.savefig():保存图片。
  • plt.show():显示图片。
  • 在这里插入图片描述

结果

在这里插入图片描述

总结

通过本文的学习,读者可以掌握以下知识和技能:

  • 使用pandas进行数据读取、去重、缺失值处理和相关系数计算。
  • 自定义函数实现数据去重和异常值识别。
  • 使用scipy进行插值计算,包括线性插值、拉格朗日插值和样条插值。
  • 使用matplotlib绘制箱线图识别异常值。

更多推荐