赞
踩
回顾一下以前提及的模型评价的体系
类别 | 概念 |
---|---|
模型(model) | 规律和经验 |
学习(learning) | 从数据中总结规律的过程 |
误差(error) | 衡量模型准确性的指标 |
训练集(教材教辅) | 训练模型的数据集 |
验证集(模拟考卷) | 测试学习模型泛化能力的数据集 |
应用数据(高考) | 模型实际应用场景的特征集 |
概念:数据预处理与特征工程泛指对训练数据集进行特征增加、删除、变换的方法
目标:通过对训练数据的处理变换,提高模型训练表现和泛化能力
类别:
特征变换:预处理、标准化、纠偏
特征增加与删减:特征降维与变量扩展
处理方式:
1) 删除:缺失样本量 非常大,删除整个变量;如果缺失量较少,且 难以填充 则删除缺失样本
2) 填充:缺失量 小于10%,根据缺失变量的数据分布采取 均值(正态分布) 或 中位数(偏态分布) 进行填充
3) 模拟或预测缺失样本:根据样本的数据分布,生成 随机值填充(插值);使用与缺失相比相关性非常高的特征,建立模型,预测缺失值
数据:房价数据(1000条共10个维度)
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)
–> 输出的结果为:
['id', 'complete_year', 'average_price', 'area', 'daypop', 'nightpop', 'night20-39',
'sub_kde', 'bus_kde', 'kind_kde']
(1000, 10)
print(df.isna().sum())
#print(df.isnull().sum())
#两种方法都可以
–> 输出的结果为:
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
注意,这里的缺失值不是很多,可以直接选择删除数据(df.dropna(inplace=True)
),但是在数据量不大的情况下,还要进行预测,建议选择数据填充;删除的方式比较简单,不进行梳理了。下面选择填充的方式处理数据,梳理一下填充的过程:首先是要查看一下数据的分布情况,然后再决定采用填充的方式(三种方式进行数据的填充)
1)查看房价并进行数据填充
# 查看房价
df['average_price'].hist()
plt.show()
–> 输出的结果为:(数据的分布大致是符合正态分布的,可以采用均值填充的方式)
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())
–> 输出的结果为:(填充空值核心代码: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
3)查看房间面积并进行数据填充
df['area'].hist()
plt.show()
–> 输出的结果为:(房间面积数据是符合偏态分布的,可以按照中位数进行填充)
4) 填充‘area’字段空值
# 选择中位数填充
area_median = df['area'].median()
print(area_median)
# 使用均值进行缺失值填充
df.loc[df['area'].isna(),'area'] = area_median
print(df.isnull().sum())
–> 输出的结果为:(最后就剩下一个字段未被处理)
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
5)查看夜间人口密度并进行数据填充
df['nightpop'].hist()
plt.show()
–> 输出的结果为:(中间有部分数据是很突出的,这里可以换种方式处理)
6) 填充‘nightpop’字段空值
这里不采用中位数的方式进行填充了,收集的数据中含有日间人口面密度,夜间人口密度以及夜间20-39岁人口密度,可以通过查看这三者直接的相关性来进行夜间人口密度数据的填充(前提是相关性比较大),下面就使用线性回归,然后使用r2
作为评价指标,也就是高中学的相关性系数,其值越接近1,说明两者的相关性越大
首先查看一下三者的相关系数,因为是要进行nightpop数据的填充,选择相关性大的
# 查看相关性系数
print(df[['daypop','nightpop','night20-39']].corr())
–> 输出的结果为:(这里需要强调一下,相关性系数大于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
这里选取 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)}')
–> 输出的结果为:(划分数据之前,要将数据中的缺失值全部去除,r2
为0.878,说明可以用来预测)
R squared is: 0.878354472726492
查看填充后的数据缺失情况
df.loc[df['nightpop'].isna(),['nightpop']] = model.predict(df.loc[df['nightpop'].isna(),['daypop']])
print(df.isnull().sum())
–> 输出的结果为:(至此所有的缺失值都已经填充完毕)
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
7) 特殊字段的处理
比如在收集数据中,会有一些数据显示的是未知/未知数据等,这些数据其实也是等同于缺失数据,因此也是需要单独拿出来进清洗的
# 特殊类型的缺失值
print(df.complete_year.value_counts().head())
–> 输出的结果为:(显然输出的第一个就是我们要处理的缺失数据)
未知 102
2006 83
1994 56
2005 54
1995 53
Name: complete_year, dtype: int64
对于这种特殊数据的处理,一般选择直接删除即可
df = df[df.complete_year!='未知'].reset_index(drop=True)
print(df.head())
–> 输出的结果为:(注意,这一步为什么不放在第一步,而放在最后一步???如果放在第一步的话,直接就相当于删除了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
离群值:远离数据主要部分的样本(极大值或极小值)
处理方式:
删除:直接删除离群样本
填充样本:使用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()
–> 输出的结果为:(通过左右图对比,箱型图更为明显的显示上下限)
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()
–> 输出的结果为:(两端的数据就进行处理完毕了,注意上面的.map()
方法,就是直接加载函数的意思,trans
这里是transform
的缩写不是train
)
这里处理完标签数据之后,就可以进行数据的保存了
df.to_excel('realestate_sample_preprocessed.xlsx',index=False)
1) 标准化目的:
去除数据量纲的影响
提高模型的解释性
加快模型收敛速度
2) 标准化的方法:
中心化:减去均值再除以标准差
01标准化:减去最小值再除以最大值与最小值的差
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()
–> 输出的结果为:(原数据有0-3000变换到低区间范围)
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)
–> 输出的结果为:(上述的代码会完成两项功能,首先是训练的数据输入进来之后会进行标准化处理,然后将标准化后的数据再输入到线性回归模型中进行训练)
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) 通用变换方法:以降低数据的偏态系数为目标,使得数据分布更加接近正太分布的变换方法
yeo-johnson 变换:可以处理包含正数、负数和零的变量
box-cox变换:只能处理数值皆为正数的变量
使用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)
–> 输出的结果为:(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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。