当前位置:   article > 正文

【机器学习(6)】数据预处理:预处理、标准化和数据纠偏

数据纠偏

模型评价体系

回顾一下以前提及的模型评价的体系

类别概念
模型(model)规律和经验
学习(learning)从数据中总结规律的过程
误差(error)衡量模型准确性的指标
训练集(教材教辅)训练模型的数据集
验证集(模拟考卷)测试学习模型泛化能力的数据集
应用数据(高考)模型实际应用场景的特征集

数据预处理与特征工程

  1. 概念:数据预处理与特征工程泛指对训练数据集进行特征增加、删除、变换的方法

  2. 目标:通过对训练数据的处理变换,提高模型训练表现和泛化能力

  3. 类别:
            特征变换:预处理、标准化、纠偏
            特征增加与删减:特征降维与变量扩展

1. 预处理

1.1缺失值:样本的部分特征信息缺失

处理方式:

1) 删除:缺失样本量 非常大,删除整个变量;如果缺失量较少,且 难以填充 则删除缺失样本

2) 填充:缺失量 小于10%,根据缺失变量的数据分布采取 均值(正态分布)中位数(偏态分布) 进行填充

3) 模拟或预测缺失样本:根据样本的数据分布,生成 随机值填充(插值);使用与缺失相比相关性非常高的特征,建立模型,预测缺失值

数据:房价数据(1000条共10个维度)

1.2 数据导入
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
os.chdir(r"C:\Users\86177\Desktop")

df = pd.read_excel('realestate_sample.xlsx')
print(df.columns.to_list())
print(df.shape)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

–> 输出的结果为:

['id', 'complete_year', 'average_price', 'area', 'daypop', 'nightpop', 'night20-39',
 'sub_kde', 'bus_kde', 'kind_kde']
(1000, 10)
  • 1
  • 2
  • 3
1.3 缺失值查看
print(df.isna().sum())
#print(df.isnull().sum())
#两种方法都可以
  • 1
  • 2
  • 3

–> 输出的结果为:

id                0
complete_year     0
average_price    11
area              8
daypop            0
nightpop         12
night20-39        0
sub_kde           0
bus_kde           0
kind_kde          0
dtype: int6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
1.4 逐一填充

注意,这里的缺失值不是很多,可以直接选择删除数据(df.dropna(inplace=True)),但是在数据量不大的情况下,还要进行预测,建议选择数据填充;删除的方式比较简单,不进行梳理了。下面选择填充的方式处理数据,梳理一下填充的过程:首先是要查看一下数据的分布情况,然后再决定采用填充的方式(三种方式进行数据的填充)

1)查看房价并进行数据填充

# 查看房价
df['average_price'].hist()
plt.show()
  • 1
  • 2
  • 3

–> 输出的结果为:(数据的分布大致是符合正态分布的,可以采用均值填充的方式)
在这里插入图片描述
2) 填充‘average_price’字段空值(核心:如何填充空值)

# 选择均值填充
price_mean = df['average_price'].mean()
print(price_mean)
# 使用均值进行缺失值填充
df.loc[df['average_price'].isna(),'average_price'] = price_mean
print(df.isnull().sum())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

–> 输出的结果为:(填充空值核心代码:df.loc[df['A'].isna(),'A'] = B

29629.326592517693
id                0
complete_year     0
average_price     0
area              8
daypop            0
nightpop         12
night20-39        0
sub_kde           0
bus_kde           0
kind_kde          0
dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3)查看房间面积并进行数据填充

df['area'].hist()
plt.show()
  • 1
  • 2

–> 输出的结果为:(房间面积数据是符合偏态分布的,可以按照中位数进行填充)
在这里插入图片描述
4) 填充‘area’字段空值

# 选择中位数填充
area_median = df['area'].median()
print(area_median)
# 使用均值进行缺失值填充
df.loc[df['area'].isna(),'area'] = area_median
print(df.isnull().sum())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

–> 输出的结果为:(最后就剩下一个字段未被处理)

79.8
id                0
complete_year     0
average_price     0
area              0
daypop            0
nightpop         12
night20-39        0
sub_kde           0
bus_kde           0
kind_kde          0
dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5)查看夜间人口密度并进行数据填充

df['nightpop'].hist()
plt.show()
  • 1
  • 2

–> 输出的结果为:(中间有部分数据是很突出的,这里可以换种方式处理)
在这里插入图片描述
6) 填充‘nightpop’字段空值

这里不采用中位数的方式进行填充了,收集的数据中含有日间人口面密度,夜间人口密度以及夜间20-39岁人口密度,可以通过查看这三者直接的相关性来进行夜间人口密度数据的填充(前提是相关性比较大),下面就使用线性回归,然后使用r2作为评价指标,也就是高中学的相关性系数,其值越接近1,说明两者的相关性越大

首先查看一下三者的相关系数,因为是要进行nightpop数据的填充,选择相关性大的

# 查看相关性系数
print(df[['daypop','nightpop','night20-39']].corr())
  • 1
  • 2

–> 输出的结果为:(这里需要强调一下,相关性系数大于0.9基本上就可以认定变量共线性很高了,因此用谁预测都可以,虽然nightpop与night20-39变量之间的相关性较高,但实际情况中如果nightpop缺失,night20-39作为夜间的一部分,缺失的可能性也比较高,因此用daypop预测来填充缺失数据。)

              daypop  nightpop  night20-39
daypop      1.000000  0.949165    0.938495
nightpop    0.949165  1.000000    0.983803
night20-39  0.938495  0.983803    1.000000
  • 1
  • 2
  • 3
  • 4

这里选取 daypop 变量数据进行 nightpop 变量缺失数据的预测填充

# 训练线性回归模型对夜间人口进行填补
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
train = df.copy().loc[~df['nightpop'].isna(),['daypop','nightpop']].reset_index(drop=True)
x = train[['daypop']]
y = train[['nightpop']]
model = LinearRegression()
model.fit(x,y)
print(f'R squared is: {r2_score(y, x)}')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

–> 输出的结果为:(划分数据之前,要将数据中的缺失值全部去除,r2为0.878,说明可以用来预测)

R squared is: 0.878354472726492
  • 1

查看填充后的数据缺失情况

df.loc[df['nightpop'].isna(),['nightpop']] = model.predict(df.loc[df['nightpop'].isna(),['daypop']])
print(df.isnull().sum())
  • 1
  • 2

–> 输出的结果为:(至此所有的缺失值都已经填充完毕)

id               0
complete_year    0
average_price    0
area             0
daypop           0
nightpop         0
night20-39       0
sub_kde          0
bus_kde          0
kind_kde         0
dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

7) 特殊字段的处理

比如在收集数据中,会有一些数据显示的是未知/未知数据等,这些数据其实也是等同于缺失数据,因此也是需要单独拿出来进清洗的

# 特殊类型的缺失值
print(df.complete_year.value_counts().head())
  • 1
  • 2

–> 输出的结果为:(显然输出的第一个就是我们要处理的缺失数据)

未知      102
2006     83
1994     56
2005     54
1995     53
Name: complete_year, dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对于这种特殊数据的处理,一般选择直接删除即可

df = df[df.complete_year!='未知'].reset_index(drop=True)
print(df.head())
  • 1
  • 2

–> 输出的结果为:(注意,这一步为什么不放在第一步,而放在最后一步???如果放在第一步的话,直接就相当于删除了100多条数据,占了本身数据量的十分之一,那么对于其它字段的数据填充就会造成影响,而填充完毕后再删除特殊字段就不会有这种问题)

		id	complete_year average_price  area	  daypop	 nightpop	night20-39	  sub_kde		 bus_kde		kind_kde
0	107000909879	2008	33464.0		25.70	119.127998	150.060287	53.842050	5.241426e-11	0.279422	7.210007e-11
1	107000908575	1996	38766.0		26.57	119.127998	150.060287	53.842050	5.241426e-11	0.279422	7.210007e-11
2	107000846227	2005	33852.0		28.95	436.765809	376.523010	183.301881	1.523092e-24	0.231135	2.922984e-01
3	107000676489	1995	39868.0		30.10	247.545324	385.412857	142.819971	4.370519e-11	0.321443	2.401811e-01
4	107000676873	1995	42858.0		30.10	247.545324	385.412857	142.819971	4.370519e-11	0.321443	2.401811e-01
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
1.5 离群值处理

离群值:远离数据主要部分的样本(极大值或极小值)
处理方式:

删除:直接删除离群样本

填充样本:使用box-plot定义变量的数值上下界,以上界填充极大值,以下界填充最小值

1) 查看房价的离群情况

fig,axes = plt.subplots(1,2,figsize = (15,6))
df['average_price'].hist(ax = axes[0])
df[['average_price']].boxplot(ax = axes[1])
plt.show()
  • 1
  • 2
  • 3
  • 4

–> 输出的结果为:(通过左右图对比,箱型图更为明显的显示上下限)
在这里插入图片描述
2) 根据箱线图的上下限进行异常值的填充,封装处理方法

def boxplot_fill(col):
    # 计算iqr:数据四分之三分位值与四分之一分位值的差
    iqr = col.quantile(0.75)-col.quantile(0.25)
    # 根据iqr计算异常值判断阈值
    u_th = col.quantile(0.75) + 1.5*iqr # 上界
    l_th = col.quantile(0.25) - 1.5*iqr # 下界
    # 定义转换函数:如果数字大于上界则用上界值填充,小于下界则用下界值填充。
    def box_trans(x):
        if x > u_th:
            return u_th
        elif x < l_th:
            return l_th
        else:
            return x
    return col.map(box_trans)
# 填充效果查看
boxplot_fill(df['average_price']).hist()
# 进行赋值
df['average_price'] = boxplot_fill(df['average_price'])
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

–> 输出的结果为:(两端的数据就进行处理完毕了,注意上面的.map()方法,就是直接加载函数的意思,trans这里是transform的缩写不是train
在这里插入图片描述

1.6 保存数据

这里处理完标签数据之后,就可以进行数据的保存了

df.to_excel('realestate_sample_preprocessed.xlsx',index=False)
  • 1

2. 数据标准化

1) 标准化目的:
         去除数据量纲的影响
         提高模型的解释性
         加快模型收敛速度

2) 标准化的方法:
         中心化:减去均值再除以标准差
         01标准化:减去最小值再除以最大值与最小值的差

2.1 sklearn 中标准化的方法
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
# 以中心化为例讲解sklearn中标准化的使用方法
scaler = StandardScaler()
fig,axes = plt.subplots(1,2,figsize = (15,6))
df['daypop'].hist(ax=axes[0])

trans_data = df.copy()[['daypop']]
scaler.fit(trans_data)
trans_data['daypop'] = scaler.transform(trans_data)
trans_data['daypop'].hist(ax=axes[1])
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

–> 输出的结果为:(原数据有0-3000变换到低区间范围)
在这里插入图片描述

2.2 过程封装Pipeline中,整合数据标准化与模型
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# 构建模型工作流
pipe_lm = Pipeline([
        ('sc',StandardScaler()),
        ('lm_regr',LinearRegression())
        ])
print(pipe_lm)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

–> 输出的结果为:(上述的代码会完成两项功能,首先是训练的数据输入进来之后会进行标准化处理,然后将标准化后的数据再输入到线性回归模型中进行训练)

Pipeline(memory=None,
         steps=[('sc',
                 StandardScaler(copy=True, with_mean=True, with_std=True)),
                ('lm_regr',
                 LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
                                  normalize=False))],
         verbose=False)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3. 数据纠偏

1) 数据分布:
         正态分布:数据呈现对称的钟形分布
         右偏态:样本大量集中在均值左边(均值偏到了右边)
         左偏态:样本大量集中在均值右边(均值偏到了左边)

2) 处理方法:
         右偏态:常用对数函数处理
         左偏态:常用指数函数处理

3) 通用变换方法:以降低数据的偏态系数为目标,使得数据分布更加接近正太分布的变换方法
         yeo-johnson 变换:可以处理包含正数、负数和零的变量
         box-cox变换:只能处理数值皆为正数的变量

sklearn 中纠偏的方法

使用pipeline进行纠偏过程的整合

from sklearn.preprocessing import PowerTransformer
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
# 构建模型工作流
pipe_lm = Pipeline([
        ('sc',StandardScaler()),
        ('pow_trans',PowerTransformer(method='yeo-johnson')),
        ('lm_regr',LinearRegression())
        ])
print(pipe_lm)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

–> 输出的结果为:(method = ‘yeo-johnson’ or 'box-cox’,默认的是使用前者)

Pipeline(memory=None,
         steps=[('sc',
                 StandardScaler(copy=True, with_mean=True, with_std=True)),
                ('pow_trans',
                 PowerTransformer(copy=True, method='yeo-johnson',
                                  standardize=True)),
                ('lm_regr',
                 LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
                                  normalize=False))],
         verbose=False)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/148171
推荐阅读
相关标签
  

闽ICP备14008679号