一、单因子探索分析与可视化
1.理论铺垫
集中趋势:均值、中位数、分位数、众数
离中趋势:标准差、方差
数据分布:偏态与峰态、正态分布与三大分布
抽样理论:抽样误差、抽样精度
四分位数计算方法:
Q1:的位置=(n+1)0.25
Q2:的位置=(n+1)0.5
Q3:的位置=(n+1)0.75
1)数列项为偶数时
3、5、9、11、17、19
Q2的值为中间两个数字和除以2
Q1位置:(n+1)/ 4 = 1.75
Q3位置:3(n+1)/ 4=5.25
对应值得计算为落到位置的区间值,低位+(高位-低位)小数部分
Q1值:3+(5-3)* 0.75 = 4.5
Q3值:17+(19-17)* 0.25 = 17.5
2)数列项为奇数时:
3、5、9、11、17、19、35
先计算位置,在通过位置计算对应的数值
Q1:(n+1)*0.25=2
Q2:(n+1)*0.5=4
Q3:(n+1)*0.75=6
当下标正好为整数时,对应的数值为Q1=5、Q2=11、Q3=19
3、5、9、11、17
Q1:(n+1)*0.25=1.5
Q2:(n+1)*0.5=3
Q3:(n+1)*0.75=4.5
当计算的下标不是整数时,对应的数值为Q1=(3+5)*0.5=4、Q2=9、Q3=(11+17)*0.5=14
四分位数计算方法
import pandas as pd
df=pd.read_csv("HR.csv")
df.head(10)
type(df) # 数据结构为DataFrame
type(df["satisfaction_level"]) # 同时,组成df中的每一列数据的数据结构为Series
df.mean() # 均值
df["satisfaction_level"].mean()
df.median() # 中位数
df.quantile(q=0.25) # 分位数
df.["satisfaction_level"].quantile(q=0.25) # 下四分位数
df.mode() # 众数
# 离中趋势:标准差,方差
df.std() # 标准差
df.["satisfaction_level"].std()
df.var()
df.sum()
# 偏态系数、峰态系数
df.skew() # 若小于零,左偏(负偏),说明大多数人比较满意
df.kurt() # 若小于零,说明比正太稍微平缓一些
峰度以bk表示,正态分布的峰度为3。以一般而言,正态分布为参照,峰度可以描述分布形态的陡缓程度,
若bk<3,则称分布具有不足的峰度,
若bk>3,则称分布具有过度的峰度。若知道分布有可能在峰度上偏离正态分布时,可用峰度来检验分布的正态性。
如果超值峰度为正,称为尖峰态(leptokurtic)。如果超值峰度为负,称为低峰态(platykurtic)。(以正太分布为0为标准)
# 分布函数
import scipy.stats as ss # scipy中的统计包stats
ss.norm # 生成一个正态分布,不过这个正态分布是一个对象,有很多调用的方法
ss.norm.stats(moments="mvsk") # 均值,方差,偏态系数,峰态系数
ss.norm.pdf(0.0) # 指定横坐标,返回纵坐标的值
ss.norm.ppf(0.9) # 表示一个累积值,从负无穷到正无穷是积分是1;表示积分为0.9时是从负无穷到多少
ss.cdf(2) # 从负无穷到2它的累积概率是多少
ss.norm.rvs(size=10) # 得到10个符合正态分布的数
# 卡方分布、t分布、f分布
ss.chi2
ss.t
ss.f
# 抽样
df.sample(n=10) # 抽10个
df.sample(frac=0.001) # 百分比 0.001
df.["satisfaction_level"].sample(n=10)
学习方法:使用的时候打开官网,10分钟接触pandas,打开API reference
2.数据分类
离散值,连续值?过于粗糙
统计学上分四类:
定类(类别):根据事物离散、无差别属性进行分类
定序(顺序):可以界定数据的大小,但不能测定差值(比如:收入的低、中、高)
定距(间隔):可以界定数据大小的同时,可以测定差值,但无绝对零点(比如不能说摄食20度比摄氏10度热一倍)
定比(比率):可以界定数据大小,可以测定差值,有绝对零点(升高多少厘米等)
3.单属性分析
异常值分析:离散异常值、连续异常值、知识异常值
对比分析:绝对数与相对数,时间,空间,理论维度比较
结构分析:各组成部分的分布与规律
分布分析:数据分布频率的显式分析
1)异常值分析
x<Q1-k(Q3-Q1)或者x>Q3+1.5(Q3-Q1)*
异常值的危害:很有可能把大部分本可以代表数据属性的值失去了它的意义,连续异常值的处理可以直接舍弃,也可以直接取边界值来代替它的异常值。
离散异常值:有时候空值就是一种异常值;或者在收入离散化以后分成“低、中、高”三类,出现其他的值都是异常值,这些异常值可以直接舍弃,也可以把所有的异常值当做一个单独的值来处理。
知识异常值:比如身高出现10米
# 各字段分析
import numpy as np
import pandas as pd
df=pd.read_csv("HR.csv")
# satisfaction_level,大概是0到1之间,satisfaction_level,简称sl,是一个series,sl_s
1)异常值:对数据型类型的属性来说,null就是它的异常值
sl_s=df["satisfaction_level"]
sl_s.isnull() # 列出有没有异常值,True有多少个
sl_s[sl_s.isnull()] # 查看异常值有几条
df[df["satisfaction_level"].isnull()] # 查看异常值的具体情况
2)sl_s=sl_s.dropna() # 丢弃该异常值并且重新赋值
sl_s=sl_s.fillna() # 发现异常值填充成多少
sl_s.quantile(q=0.25) # 下四分位数
sl_s.quantile(q=0.75)
# 怎么获取离散化分布
np.histogram(sl_s.values,bins=np.arange(0.0,1.1,0.1))
# last_evaluation,0到1之间
le_s=df["last_evaluation"]
le_s[le_s>1]
le_s[le_s<=1] # 去除超大值
q_low=le_s.quantile(q=0.25)
q_high=le_s.quantile(q=0.75)
q_interval=q_high-q_low
k=1.5
le_s=le_s[le_s<q_high+k*q_interval][le_s>q_low-k*q_interval]
np.histogram(le_s.values,bins=np.arange(0.0,1.1,0.1)) # 直方图
# number_project ,范围2到7
np_s[np_s.isnull()]
np_s.value_counts() # 数每个数出现了多少次
np_s.value_counts(normalize=True) # 查看每个数占的比例分别是多少
np_s.value_counts(normalize=True).sort_index() # 排序
# average_monthly_hours
amh_s=df["average_monthly_hours"]
amh_s=amh_s[amh_s<amh_s.quantile(0.75)+1.5*(amh_s.quantile(0.75)-amh_s.quantile(0.25))][amh_s>amh_s.quantile(0.25)-1.5*(amh_s.quantile(0.75)-amh_s.quantile(0.25))]
np.histogram(amh_s.values,bins=10) # 看分布
np.histogram(amh_s.values,bins=np.arange(amh_s.min(),amh_s.max()+10,10)) # 左闭右开
amh_s.value_counts(bins=np.arange(amh_s.min(),amh_s.max()+10,10)) # 获得左开右闭的区间
# time_spend_company
tsc_s=df["time_spend_company"]
tsc_s.value_counts().sort_index() # 查看每个数出现多少次并排序
# work_accident
wa_s=df["work_accident"]
wa_s.value_counts()
# left
l_s=df["left"]
l_s.value_counts()
# promotion_last_5years
pl5_s=df["promotion_last_5years"]
pl5_s.value_counts()
# salary
s_s.value_counts()
s_s.where(s_s!="nme").dropna() # 除异常值且去除空值
# department ,也是离散值
d_s=df["department"]
d_s.value_counts(normalize=True)
d_s=d_s.where(d_s!="sale")
2)对比分析
1)绝对数比较
2)相对数比较(结构,比例,比较,动态,强度)
时间(同比,环比),空间,经验与计划
3)结构分析
部分:总体
静态,动态
4)分布分析
从三个角度来看分布分析:
1.直接获得概率分布:我们得到了一些数,把它们排列一下;或者如果它是离散的,我们就把离散值数出来,它的数量排列一下,,就可以得到它的分布,不管怎么说,直接得到的分布可能形状不一,但这就是它的分布,这个分布有可能是有意义的,也有可能在对比过后才有意义,也有可能就是没有意义,和分析者最终的分析目的有关,也有可能和其他属性进行复核分析才有关系,这种情况下或者进行比较,或者进行复核分析,得到的分布才会有意义。
**2.判断一个分布是不是正态分布:**用假设检验判断是不是正态分布,或者用偏态、峰态系数来判断不是正态分布
3.极大似然
5)简单对比分析
df=df.dropna(axis=0,how="any") # 对表操作时选定axis=0,删除该行
df=df[df["last_evaluation"]<=1][df["salary"]!="nme"][df["department"]!="sale"]
#对比分析,以部门为单位进行对比,对部门进行分组,用groupby命令
df.groupby("department").mean() # mean,聚合方法
df.loc[:,["last_evaluation","department"]].groupby("department").mean()
# index全选,属性列选last_evaluation,department
df.loc[:,["average_monthly_hours","department"]].groupby("department")["average_monthly_hours"].apply(lambda x:x.max()-x.min()) # 极差
4.可视化分析
python可视化工具:matplotlib、seaborn(matplotlib的封装)、plotly(画出的表格可以直接用在网页当中)
# 可视化
df=pd.read_csv("HR.csv")
# 柱状图
import seaborn as sns
import matplotlib.pyplot as plt
plt.title("SALARY")
plt.xlabel("salary")
plt.ylabel("Number")
plt.xticks(np.arange(len(df["salary"].value_counts()))+0.5,df["salary"].value_counts().index)
plt.axis([0,4,0,10000])
plt.bar(np.arange(len(df["salary"].value_counts()))+0.5,df["salary"].value_counts(),width=0.5) # 长度、高度
for x,y in zip(np.arange(len(df["salary"].value_counts()))+0.5,df["salary"].value_counts()):
plt.text(x,y,y,ha="center",va="bottom")
plt.show()
sns.set_style(style="whitegrid")
sns.set_context(context="notebook",font_scale=2)
sns.set_palette("summer")
sns.countplot(x="salary",hue="Department",data=df)
plt.show()
二、探索性数据分析(多因子与复合分析)
探索性数据分析最重要的作用是把数据的全貌进行展现,让数据分析者能根据数据进行决策。每个属性的数据除了自己本身已具有的性质以外,属性与属性之间,或者属性与某个成熟的规律之间也可能有联系。接下来分析属性与属性之间常见的联系和分析方法。
1.理论铺垫
假设检验与方差检验
相关系数:皮尔逊、斯皮尔曼
回归:线性回归
PCA与奇异值分解
1)假设检验
根据一定的假设条件,从样本推断总体,或者推断样本与样本之间关系的一种方法,换言之,就是做出一个假设,然后根据数据或者已知的分布性质来推断假设成立的概率有多大。步骤为:
1):建立原假设H0(包含等号),H0的反命题为H1,也叫备择假设
2)选择检验统计量:是根据数据的像均值、方差等性质构造的一个转换函数,构造这个函数的目的,是让这个数据符合一个已知的分布比较容易解决的格式。比如,我们把一些数据减去它的均值,再除以标准差,这样判断转换后的统计量,也就是检验统计量是否符合标准正态分布即可以判断数据的分布是否是正态分布的概率了。
3)根据显著性水平α(一般为0.05),确定拒绝域
显著性水平,就是我们可以接受假设的失真程度的最大限度,显著性水平和相似度的加和为1,比如,我们确定了某数据的属性有95%的可能性是某个分布,那么它的显著性水平就是5%,显著性水平一般是人为定的一个值,这个值定的越低,相当于对数据和分布的契合程度的要求就越高。这个值我们一般取0.05,也就是说要求数据有95%的可能与某分布一致,一旦确定了显著性水平,那么这个已知的分布上就可以画出一段与这个分布相似性比较高的区域,我们叫做接受域。接受域以外的区域就是拒绝域。如果上一步说到的检验统计量落入了拒绝域,那么H0就可以认为是假的,也就是可以被拒绝掉的
4)计算p值或者样本统计值,做出判断
根据计算的统计量和我们要比较的分布,进行判断的过程。
思路有两种:
1.根据我们之前讲到的区间估计的方法,计算一个检验统计量的分布区间,看这个区间是不是包含了我们要比较的分布的特征;
2.计算一个p值,直接和显著性水平进行比较,这个p值可以理解为比我们计算出来的检验统计量结果更差的概率,如果p值小于α,那么这个假设就可以认为是假的
假设检验的方法有很多,这些方法的差别一般取决于检验统计量的选取上。t分布检验常用来检验两组样本分布是不是一致,常用来临床医疗上药物有没有效果就可以用t分布检验的方法来进行实验;f检验常用来方差分析。
2)卡方检验:假设化妆行为和性别无关
3)方差检验
前面的例子中我们只研究了一个样本,或者两个样本的研究方法。如果有多个样本,检验样本两两之间是不是有差异。一个思路,就是进行两两分析,但这样,对比次数可能就比较大,如果数据样本数据量比较多,就比较耗时耗力。还有就是用方差检验,因为用到了f分布,所以也叫f检验。
方差检验(单因素)
计算:
S
S
T
=
∑
i
=
1
m
∑
j
=
1
n
i
(
x
i
j
−
x
ˉ
)
2
SST=\sum_{i=1}^{m}\sum_{j=1}^{n_i}(x_{ij}-\bar{x})^2
SST=i=1∑mj=1∑ni(xij−xˉ)2
S
S
M
=
∑
i
=
1
m
∑
j
=
1
n
i
(
x
ˉ
i
−
x
ˉ
)
2
SSM=\sum_{i=1}^{m}\sum_{j=1}^{n_i}(\bar{x}_{i}-\bar{x})^2
SSM=i=1∑mj=1∑ni(xˉi−xˉ)2
S
S
E
=
∑
i
=
1
m
∑
j
=
1
n
i
(
x
i
j
−
x
ˉ
i
)
2
SSE=\sum_{i=1}^{m}\sum_{j=1}^{n_i}(x_{ij}-\bar{x}_{i})^2
SSE=i=1∑mj=1∑ni(xij−xˉi)2
拒绝,说明它们的均值有差异
4)相关系数
相关系数是衡量两组数据, 或者说两组样本的分布趋势、变化趋势一致性程度的因子。相关系数有正相关、负相关、不相关之分。相关系数越大,越接近于1,二者变化趋势越正向同步;相关系数越小,越接近于-1,二者的变化趋势越反向同步;相关趋势趋于0,则可认为二者是没有相关关系的。
1)Pearson相关系数:
分子:两组数据的协方差,分母:两组数据的标椎差的积
2)Spearman相关系数
n指的是每组数据的数量,d指的是两组数据排名后的名次差,该相关系数只和它的名次差有关,和具体的数值关系不大
5)线性回归
线性回归最常见的解法就是最小二乘法,本质就是最小化误差的平方的方法
线性回归效果的判定主要有以下两种度量:
6)主成分分析(PCA)
在学习矩阵的时候,经常把一个数据表看成一个空间,看做一个矩阵,这个矩阵的行对应于每个数据对象的各个属性,矩阵的列就代表每个属性的不同内容。我们也把每个属性当做由这张表构成空间的一个维度:每行所代表的实体代表的就是一个向量,这个表的内容,代表的就是一个巨大的空间。
举例:这张表中有四个属性,每个属性都是一个维度,每一行的数据都包含四个维度,构成一个向量,虽然每个向量有四个维度,但维度也是有主次之分的,比如维度A就是一个次要的维度 。
PCA的变换步骤:
7)编码实现
import numpy as np
import scipy.stats as ss
norm_dist=ss.norm.rvs(size=20)
norm_dist # 生成符合标椎正态分布的20个数
ss.normaltest(norm_dist) # 检测是否是正态分布,p值大于显著性水平0.05,故符合正态分布
ss.chi2_contingency([[15,95],[85,5]]) # 卡方检验
# 独立分布t检验主要检验两组数据的均值有没有较大的差异性
ss.ttest_ind(ss.norm.rvs(size=10),ss.norm.rvs(size=20))
# 得出p值为0.08,大于0.05,故接受该假设,它们的均值没有差异
# 增大数据量
ss.ttest_ind(ss.norm.rvs(size=100),ss.norm.rvs(size=200))
# 方差检验
ss.f_oneway([49,50,39,40,43],[28,32,30,26,34],[38,40,45,42,48])
# 通过qq图对比
from statsmodels.graphics.api import qqplot
plt.show(qqplot(ss.norm.rvs(size=100)))
2.复合分析
接下来来进行属性间的数据分析
交叉分析、因子分析
分组与钻取、聚类分析(暂略)
相关分析、回归分析(暂略)
1)交叉分析
我们得到了一张数据表,需要进行分析的时候,最直观的,有两个分析的切入点:一个是从列的角度进行分析,分析每个属性的特点并进行归纳和总结;另一个,从行的角度进行分析,也就是从案例的角度进行分析,尤其,当数据有了标注的时候,以标注为关注点,案例分析越多,也就越接近于数据整体的质量,这两种分析方法也是最为浅层次的分析,如果仅仅进行这两方面的分析,有时并不能得到最为真实,客观的结论。这是因为直接使用这种纵向分析和横向分析,忽略了数据间属性间的关联性,也就是说很有可能有信息的失真,所以,我们需要分析属性与属性之间的关系,得到更多的,能反应数据内涵的信息。交叉分析就是一种重要的分析属性与属性间关系的方法。该类的分析方法比较多:比如我们可以任意取两列,也就是两个属性,根据我们所学到的假设检验的方法,来判断它们之间是否有联系;也可以直接一一个或者几个属性为行,另一个和几个属性为列,做成一张交叉表,也叫透视表,通过观察这张新生成的表的性质,可以更直观的分析两个属性或者几个属性之间的关系。
接下来关注属性“left”,想查看各个部门的离职率之间,是不是有明显的差异?这里我们可以使用的方法,就是独立t检验的方法,基本思路就是得到各个部门的离职分布,然后两两间求它们的t检验统计量,并求出p值,目的是得到各个部门的离职分布,所以需要先group
在这里插入代码片
2)分组分析
分组分析,将数据进行分组后,再进行分析比较;或者根据数据的特征,将数据进行切分,分成不同的组,使得组内成员尽可能靠拢,组间的成员尽可能远离,在这种含义下,如果我们指定了每一条数据的分组,来计算当未知分组的数据出现的时候,更精确的判断它是哪个分组,这个过程叫做分类;如果我们不知道分组,是想让数据尽可能 的物以类聚,我们把这个过程叫做聚类,分类和聚类,都是机器学习的主要内容。
1.将数据进行分组后进行分析,分组分析一般要结合其他分析方法进行配合使用,从这个角度来讲,分组分析更像是一种辅助手段,而不是一种目的性的分析,之前“对比分析”、“交叉分析”的时候都讲到过数据分组,可以更直观的发现数据中属性的差异。分组分析最常用的具体手段就是钻取。
**钻取就是改变维的层次,变换分析的粒度。**根据钻取方向的不同,可以分为向上钻取和向下钻取。
向下钻取:就是展开数据,查看数据细节的过程;
向上钻取:就是汇总分组数据的过程。
离散属性的分组是比较容易的,连续属性的分组在分组前需要进行离散化。在进行连续属性的离散化之前,先看一下数据分布,是不是有明显的可以区分的标志:比如,将数据从小到大排列后,有没有一个明显的分隔,或者明显的拐点。
连续属性的分组要尽可能的满足相同的分组比较聚拢,不同的分组比较分离的特点,所以可以用聚类的方法进行连续属性的分组。
D:目标的标注
连续数值的基尼系数:
3)相关分析
相关分析师衡量两组数据或者样本分布趋势或者变化趋势大小的分析方法。相关分析最常用的就是相关系数。用相关系数直接衡量相关性的大小最为直接和方便。(衡量连续值的相关性)
r
(
X
,
Y
)
=
C
o
v
(
X
,
Y
)
σ
x
σ
y
=
E
[
(
X
−
μ
x
)
(
Y
−
μ
y
)
]
σ
x
σ
y
r(X,Y)=\frac{Cov(X,Y)}{\sigma_x\sigma_y}=\frac{E[(X-\mu_x)(Y-\mu_y)]}{\sigma_x\sigma_y}
r(X,Y)=σxσyCov(X,Y)=σxσyE[(X−μx)(Y−μy)]
ρ
s
=
1
−
6
∑
d
i
2
n
(
n
2
−
1
)
\rho_s=1-\frac{6\sum d_i^2}{n(n^2-1)}
ρs=1−n(n2−1)6∑di2
离散属性的相关性如何衡量?
先分析特例:有个二类离散属性的相关性问题。二类离散属性就是说它只有两个分类,一个是0,另一个是1,比如像是与否的判断,有或者没有等之类的属性值,这样,二类属性就可以编码成0和1,可以直接用皮尔逊相关系数来衡量相关性的大小的。当然也可以直接把二类属性进行相关性的计算,但是这样可能会有些失真,多类离散属性的相关系数会有些麻烦:如果多类离散属性都是定序数据的话,比如:low,medium,high。就可以直接编码成0,1,2这些值进行皮尔逊相关系数的计算,需要注意的是这样也是有失真的,一般的,我们可以用“熵”来进行离散属性的相关性系数计算。
熵:用来衡量不确定性的一个值,越接近于0,意味着它的不确定性越小,
1)熵
H
(
X
)
=
−
∑
p
i
l
o
g
(
p
i
)
H(X)=-\sum p_i\,log(p_i)
H(X)=−∑pilog(pi)
2)条件熵 H ( Y ∣ X ) = ∑ p ( x i ) H ( Y ∣ X = x i ) H(Y\mid X)=\sum p(x_i)H(Y\mid X=x_i) H(Y∣X)=∑p(xi)H(Y∣X=xi)
3)互信息(熵增益)
I
(
X
,
Y
)
=
H
(
Y
)
−
H
(
Y
∣
X
)
=
H
(
X
)
−
H
(
X
∣
Y
)
I(X,Y)=H(Y)-H(Y\mid X)=H(X)-H(X\mid Y)
I(X,Y)=H(Y)−H(Y∣X)=H(X)−H(X∣Y)
三、预处理理论
1.特征工程简介
2.异常值(空值)处理
在探索性数据分析中,我们的目的主要是观察,之前是重在对异常值的分布,异常值的数量进行观察和记录,去掉异常值的作用,也是为了更好的观察正常值的氛围,而在特征预处理这一环节,进行异常值分析却是重在对之后的模型打基础,为了让模型把属性充分的利用起来,所以这个环节的异常值分析,主要手段是将异常值丢弃或者转换,目的是之后的建模,虽然二者的目的有所不同,但用到的方法大都是一致的。
异常值处理的常用方法:异常值首先是要被识别到,空值是一种异常值,重复值有时候都是一种异常值,四位分位数上下1.5倍到3倍边界范围以外也有可能是异常值,当然,也包括业务的实际情况下,不允许出现的值。这些异常值可以用pandas当中的函数处理,也可以自己增加规则进行识别。异常值识别出来之后就要处理,1)如果异常值不多,丢弃也是一种常用的方法,pandas当中丢弃的常用方式,
2)如果异常值比较多,可以考虑用一个新的值来代替异常值,或者把这个属性是否是异常值这个判断结果代替这个属性,或者直接代替异常值,还可以考虑用集中值进行指代,(这里的集中值可以是除异常值之外的均值,中位数,众数等);
也可以用边界值来指代(比如说在连续数据中用四分位间距确定的上下边界来确定超过上下边界的数),连续型的数值还可以用插值的方法来填充异常值
例:
# 异常值处理的一般方法:
import numpy as np
import pandas as pd
df=pd.DataFrame({"A":["a0","a1","a1","a2","a3","a4"],
"B":["b0","b1","b2","b2","b3",None],
"C":[1,2,None,3,4,5],
"D":[0.1,10.2,11.4,8.9,9.1,12],
"E":[10,19,32,25,8,None],
"F":["f0","f1","g2","f3","f4","f5"]})
df # 字符类的的空值填充的是None,数值类型的空值填充的是NaN
df.isnull() #识别空值
df.dropna() # 去掉了有空值的行
df.dropna(subset=["B"]) # 去掉B属性的空值
df.duplicated(["A"]) # 识别重复值 (返回来一个series,重复的标为了True,其他都是False)
df.duplicated(["A","B"]) # 这种情况下必须两个属性合起来是重复的它才是重复的
df.drop_duplicates(["A"]) # 去掉重复值
df.drop_duplicates(["A"],keep=False) #去掉所有的重复行
df.fillna("b*") #标记异常值
df.fillna(df["E"].mean()) # 把异常值填充成均值(求均值的时候自动的把空值或者异常值忽略了)
df["E"].interpolate() # 插值,该方法只能用在Series
pd.Series([1,None,4,5,20]).interpolate() # #在末尾的话取前面一个数,第一个的话取第二个数,中间的话取相邻的平均值
df["E"].interpolate(method="spline",order=3) # 三次样条插值
# 用四分位数确定上下界的方法来进行过滤
upper_q=df["D"].quantile(0.75)
lower_q=df["D"].quantile(0.25)
q_int=upper_q-lower_q #四分位间距
k=1.5
df[df["D"]>lower_q-k*q_int][df["D"]<upper_q+k*q_int]
df.drop(2)
df[[True if item.startswith("f") else False for item in list(df["F"].values)]] # 返回True,True的行会保留。False会去掉
inplace 默认为False
当前的的操作不管操作了什么,我们定义的DataFrame结构是不会变的,只是不管是什么样的函数,它返回了一个我们处理过的结果,而如果这个inplace填成了True,那么原始的DF也会跟着变化了
3.标注
要进行建模分析,需要有一个label,在kaggle上,这份数据的目的是为了让大家通过人员的其他属性,来建立一个模型,预测员工是否会离职,以及建立模型比较精确的指明什么样的人会离职所以,label定为left这个属性,当这个属性为1时,就是离职,属性为0时,就是不离职,确定了标注的含义,和我们数据表中的标注,就可以进行下一步学习。
4.特征预处理
主要内容:
1)特征选择
2)特征变换:对指化、离散化、数据平滑、归一化(标准化)、数值化、正规化
3)特征降维
4)特征衍生
1).特征选择
剔除与标注不相关或者冗余的特征,减少特征的个数,带来效果就是减少了模型训练的时间,尤其是当数据比较多的时候,有时会有效减少过拟合,运气好的话可以提升模型的准确度。之前讲过PCA,奇异值变换等通过变换的方式降维的方法。这些对特征降维的处理方式,我们可以叫做特征提取,既然叫特征提取,那就少不了变换,而这里讲到的特征选择则是依靠统计学方法或者数据模型,机器学习模型本身的特征进行与标注影响大小的排序后,剔除排序靠后的特征,实现降维。特征选择可以放在对特征处理之前进行,也可以在特征变换处理后进行,总之,还是需要结合属性本身的特征,和任务的需求进行选择。特征选择需要进行重复迭代,多次验证,有时可能我们自己认为特征选择已经做得足够好了,但是实际模型中训练并不太好,所以,每次特征选择都要使用模型去验证,最终的目的,是为了获得能训练出更好的模型的数据,数据科学中有一个比较常见的概念叫数据规约,特征选择就是数据规约的一种处理方式,另一种数据规约的处理方式就是抽样
特征选择有三个切入思路:
1.过滤思想(评价某个特征与标注的相关性的特征,如果与标注的相关性非常小,就去掉,)
标注无疑会落入连续值或者离散值的范畴,而把特征进行比较粗的分类,也可以分为离散值或者连续值,于是,我们就可以在这张表中找到标注对应的类型,与特征对应的类型的关联性评价的方法进行评价,这里的阈值设置比较灵活,可以在特征比较多的时候阈值设置的高一些,特征比较少的时候阈值设置的低一些,或者直接根据任务需求或者经验进行设置。
2)包裹思想
假设所有的特征是个集合X,最佳的特征组合是它的一个子集,我们的任务就是要找到这个子集,我们需要先确定一个评价指标,比如正确率,于是,我们可以遍历特征子集,找到正确率评价下最佳的子集;也可以一步步进行迭代,比如,我们先拆分成几个大点的子集,如果这个时候确定了最优的特征子集,就针对这个特征子集进行接下来的拆分,直到我们的评价指标下降过快,或者低于阈值的时候, 整个过程结束,这种思想下有一种常用的方法,叫做RFE,
3)嵌入思想
这里说的嵌入的主体是特征,被嵌入的实体是一个简单的模型,也就是说根据这个简单的模型来分析特征的重要性,最常用的方式使用正则化的方式来做特征选择,
比如这里用n个特征,通过一个回归模型,对标注进行回归。回归可以使线性回归,也可以是逻辑斯特回归等方法,最后得到了一些W系数,然后对这些W系数进行正则化(正规化),转换成一个0到1之间的数, 这些系数就反应了这些特征的分量和重要程度。如果有的系数比较小,就可以把这个特征去掉,不过嵌入思想是有风险的,比如模型选择的不当,会导致重要的属性被丢弃,所以,这里嵌入思想选择的模型最好是和最终预测的模型有比较强的关联。比如都用线性模型或者都用同样分布形式的 也就是函数分布形式一致的,非线性函数。
# 特征选择
import numpy as np
import pandas as pd
import scipy.stats as ss # 统计包
#ABC,正态分布的10个数,D属性,,标注
df=pd.DataFrame({"A":ss.norm.rvs(size=10),
"B":ss.norm.rvs(size=10),
"C":ss.norm.rvs(size=10),
"D":np.random.randint(low=0,high=2,size=10)})
from sklearn.tree import DecisionTreeRegressor #决策树回归器
from sklearn.svm import SVR # SVR回归器
X=df.loc[:,["A","B","C"]] # 特征
Y=df.loc[:,"D"] # 标注
from sklearn.feature_selection import SelectKBest,RFE,SelectFromModel
# 特征选择:过滤,包裹,嵌入
skb=SelectKBest(k=2) # 过滤思想
skb.fit(X,Y)
skb.transform(X) # 去掉了第三个
rfe=RFE(estimator=SVR(kernel="linear"),n_features_to_select=2,step=1)
# 最终选择2个特征,每迭代一次,去掉一个特征
rfe.fit_transform(X,Y)
sfm=SelectFromModel(estimator=DecisionTreeRegressor(),threshold=0.1) # 表示重要性因子的那个数
sfm.fit_transform(X,Y)
sfm=SelectFromModel(estimator=DecisionTreeRegressor(),threshold=0.01)
sfm.fit_transform(X,Y)
2).特征变换
(对指化、离散化、数据平滑、归一化(标准化)、数值化、正规化)
1.特征变换–对指化(对数据进行对数化和指数化的过程)
指数化:在大于0 的部分,当横轴表示的自变量有很小的一段变化,但在纵轴上,有比横轴尺度更大的变化。也就是说,原来的大于0的范围内,数据与数据的差距很小,而经过指数变换,数据与数据间的差距变大了。
例如:
对数化(将数据缩放到一个较小的尺度内,还保留的数据的大小关系)
使用的函数为
numpy.log
numpy.exp
2.特征变换–离散化
将连续数据变换成离散数据的操作,一般是将连续变量分成几段(bins),之所以有些数据需要离散化,是有以下几个方面的原因:
1).为了克服数据本身的缺陷:
连续数据的信息会很多,但这其中也有可能存在一些我们意想不到的噪声。比如,我们想通过收入来分析私企、国企、外企的待遇情况,如果可以获得员工的工资流水,基本可以确定和比较收入情况了。但是如果有员工会通过接私活的方式赚些外快,或者有的员工工资的一部分被公司扣去用作其他用途,那工资流水就会有噪声,不能直接反应员工收入这项指标,如果有合适的方法将数据离散化,直接对比离散值的分布属性,就有可能得出更为令人信服的结论。
2)某些算法需要属性必须是离散的
比如朴素贝叶斯算法需要数据时离散值
3)数据的非线性映射需求
比如拿某些数据的分布来看,它的分布可能会有明显的拐点,那么连续数值在不同的区间内可能代表不同的含义。比如如下曲线,在A段是上升期,B段是平稳期。这可能比它连续数值本身更能代表数据的特性。
常用的离散化方法:
等频、等距、自因变量优化
前两种属于分箱计数,等频又叫等深分箱,等距又叫等宽分箱,
自因变量优化方法:根据自变量,因变量的有序分布找到拐点等特殊变化点,进行离散化。
分箱计数
(数据在分箱前一定要进行排序)
既然是箱子,那么就有它的深度和宽度,对于数据分箱来说,它的深度就是数的个数;宽度就是数的区间。
等深分箱:接下来可以用一个值来替代箱里的数据,可以用均值替代每个箱的数值,也可以用它的边界值代表每个箱的数值,边界值可以选择最靠近边界的那个数作为它的替代值,
等宽分箱:每个数据的区间尽量一致,67-6=61,把61平均分成3份,于是就可以得到3个区间,第一个区间可以到26又1/3,也就是可以把前7个数字分割出去,而第二个区间,切分到46又2/3,所以把第二个40切分出去,最后就是67.
# 分箱
import numpy as np
import pandas as pd
lst = [6,8,10,15,16,24,25,40,67]
pd.qcut(lst,q=3) # 等深分箱
pd.qcut(lst,q=3,labels=["low","medium","high"])
pd.cut(lst,bins=3) # 等宽分箱
pd.cut(lst,bins=3,labels=["low","medium","high"])
3.特征变化–归一化和标准化
归一化:一种数据变换方法,即最小化、最大化的一种特殊形式。就是将数据的范围缩放到一定的指定大小的范围内,归一化,就是将数据转换到0到1 的范围,即最小值是0,最大值是1。
归一化,使数据缩放到0到1之间,这是使数据处理起来更为方便,一方面可以直接观察单个数据相对于数据整体情况的比例;另一方面,如果遇到不同量纲的数据特征,可以方便的建立起这些数据特征之间进行合适的距离度量方法。比如一个特征的范围从1 到10 ,另一个特征的范围是从1到100,如果直接比较这两个个体的特征之间的差距,那么显然第二个特征从1到100的覆盖范围更大,代表的长度就会更长,直接去比较的话显然第二个特征的大小对数据整体间数据大小的对比所占的比重就会更大。如果把它们的尺度都缩放到0到1之间,因为两者的范围都是从0到1 的,这样对比数据间的特征距离就科学很多。
标准化:含义较广,指将数据转换成一个标准形式,如何定义这个标准形式有很多,比如归一化就是一种标准化。
这里说到的标椎化是比较狭义的,指的是将数据缩放到均值为0,标准差为1的尺度上,
标准化Z-score:
x
′
=
x
−
x
ˉ
σ
x'=\frac{x-\bar x}{\sigma}
x′=σx−xˉ
例如:下面都有2组数,且范围都是0到1之间,变换后通过观察发现原来最大值最小值的差是1,而转换后第二组的极值变成了3.02,大于了第一组的极值2。对数据转换成统一的均值、标准差的形式,从而体现在该特征下与其他数据的相对大小的特征关系。
# 归一化和标准化
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler,StandardScaler #引入归一化和标准化函数
MinMaxScaler().fit_transform(np.array([1,4,10,15,21]).reshape(-1,1))
#在fit_transform中,需要满足独立的行,所以需要reshape,-1的意思是我不指定他有多少行,但是一定有这么多列(1列)
StandardScaler().fit_transform(np.array([1,1,1,1,0,0,0,0]).reshape(-1,1))
StandardScaler().fit_transform(np.array([1,0,0,0,0,0,0,0]).reshape(-1,1))
4.特征变换–数值化
数值化就是把非数值数据转换成数值数据的过程,非数值数据在处理过程中有很多不方便,比如有四种数据类型(定类,定序,定距,定比)
保证数据属性的相对大小不变,同时也赋予了距离比较的含义。实际上,数值化的作用是为了建模使用,转换后的值都不是直接相加减的。或者它们只要保留相对大小的信息有时候就足够了。或者它们会以一个系数相乘,而系数是未知的,是去调整的,天然有去除量纲的作用,所以,对数据建模来说,它的相差有多大,其实有时候并不重要。
定序数据:标签化
**定类数据:**由于没有相对大小关系,所以直接进行标签化处理会有额外的扰动信息,这些信息有可能会影响之后建立模型的准确信,定类数据中,每种不同的数值它们的相互差别应该是一致的,比如我们说物体的颜色有红、黄、蓝,不能说红色比蓝色大等比较,所以就有了独热编码的概念(one-hotencoding),独热编码是将数据特征进行扩维,原来的n种属性由n维向量数据来表示,这个向量只有1位是1,其他均为0。
经过如上转换后,一维特征变成了四维特征,但拿这个四维特征来说,每两个不同属性间的距离都是一样的,都是根号2,在保留了数据特征的相关关系的同时,又得到了定类数据的数值化特征。可以想象的到,如果一个定类特征的属性比较多的话,比如有十几个值,特征扩维就会比较多,这样,整个特征属性空间将会是一个非常稀疏的矩阵,有的时候如果定序数据相对大小的概念和我们的标注并没有明显的相关关系,那么定序数据的属性也可以用这种独热编码的思路。
from sklearn.preprocessing import LabelEncoder,OneHotEncoder
LabelEncoder().fit_transform(np.array(["Down","Up","Up","Down"]).reshape(-1,1))
LabelEncoder().fit_transform(np.array(["Down","Medium","High","Medium","Low"]).reshape(-1,1))
lb_encoder=LabelEncoder() 在进行之前需先做labelEncoder
lb_tran_f=lb_encoder.fit_transform(np.array(["Red","Yellow","Blue","Green"]))
oht_encoder=OneHotEncoder().fit(lb_tran_f.reshape(-1,1))
oht_encoder.transform(lb_encoder.transform(np.array(["Yellow","Blue","Green","Green","Red"])).reshape(-1,1))
oht_encoder.transform(lb_encoder.transform(np.array(["Yellow","Blue","Green","Green","Red"])).reshape(-1,1)).toarray # 看结果
5.特征变化–正规化
本质是将一个向量的长度正规到单位1,如果距尺度的衡量用L1距离,那就是L1正规化;
用L2距离,也叫欧式距离,那就是L2正规化。
L
1
正
规
化
:
x
′
=
x
i
∑
j
=
1
n
∣
x
j
∣
L1正规化:x'=\frac{x_i}{\sum_{j=1}^n\mid x_j\mid}
L1正规化:x′=∑j=1n∣xj∣xi
L
2
正
规
化
:
x
′
=
x
i
∑
j
=
1
n
∣
x
j
∣
2
L2正规化:x'=\frac{x_i}{\sqrt{{\sum_{j=1}^n\mid x_j\mid^2}}}
L2正规化:x′=∑j=1n∣xj∣2xi
在数据分析处理中,正规化有3种用法:
1)直接用在特征上;
2)用在每个对象的各个特征的表示(特征矩阵的行);
3)模型的参数上(回归模型使用较多)
第三种正规化还常用在一些模型的参数上,尤其是回归模型(线性回归,逻辑回归等)。比如,可以用L2正则化,让所有的系数的平方和为1,可以表示出哪些特征对于标注的影响占比比较大,哪些特征对于标注的影响占比比较小。
import pandas as pd
import numpy as np
from sklearn.preprocessing import Normalizer
Normalizer(norm="l1").fit_transform(np.array([1,1,3,-1,2]).reshape(-1,1))
Normalizer(norm="l1").fit_transform(np.array([[1,1,3,-1,2]]))
Normalizer(norm="l2").fit_transform(np.array([[1,1,3,-1,2]]))
3).特征降维
之前说到PCA变换,奇异值分解都是根据数据属性之间的像话关系,进行相关性因子的提取,进而进行降维。
PCA变换的一般方法:
不管是一般的线性PCA变换还是奇异值分解,都没有考虑到标注。而是让特征与特征间的相关性强弱来决定降维后的分布形态,是一种无监督的降维方法,接下来的LDA是一种使用到标注的降维方法:
LDA降维
1)LDA:Linear Discriminant Analysis 线性判别式分析;(not:隐含狄利克雷分布Latent Dirichlet Allocation)
2)核心思想:投影变化后同一标注内距离尽可能小;不同标注间的距离尽可能大。
如下矩阵有m个特征,Y是标注
将矩阵根据行进行切分,可以分成两个子矩阵:
针对这两个子矩阵进行线性变换,即在这个子矩阵前乘上一个参数矩阵w。注意:标注Y并不参与到计算中。
我们希望标注间的距离尽可能大,标注内的距离尽可能小,所以我们可以最大化一个函数,这个函数就是把标注间的衡量放在分子,标注内的衡量放在分母,求它最大的时候取到的参数w,就是我们希望得到的参数,这就是LDA的基本思路
# LDA
import numpy as np
import pandas as pd
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
X=np.array([[-1,-1],[-2,-2],[-3,-2],[1,1],[2,1],[3,2]])
Y=np.array([1,1,1,2,2,2])
LinearDiscriminantAnalysis(n_components=1).fit_transform(X,Y) # n_components,降到几维
clf=LinearDiscriminantAnalysis(n_components=1).fit(X,Y) # 分类器(fisher_classifier)
clf.predict([[0.8,1]])
4).特征衍生
指的对现有的特征进行某些组合,生成新的,具有含义的特征。其也是特征工程中比较重要的一环,因为通常我们所采集到的数据特征维度不会很大,而且直接采集到的特征并不一定能完全体现数据的全部信息,需要通过已有的数据组合来发现新的含义,特征衍生常用的方法有:
加
减
乘
除
、
求
导
与
高
阶
求
导
、
人
工
归
纳
\color{red}{加减乘除、求导与高阶求导、人工归纳}
加减乘除、求导与高阶求导、人工归纳
从用户角度衍生:用户购买习惯?购买次数?;
从商品角度衍生:商品特征?是不是快消品?是否有季节影响?
从关系角度衍生:某商品是不是用户常买的?用户一般会如何搭配?
通过这些衍生,可以建立起用户和这些商品的一些关系,这个思路也是推荐系统中进行特征扩维的一种主要方法。
5).HR表的特征预处理
# HR表的特征预处理
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler,StandardScaler
from sklearn.preprocessing import LabelEncoder,OneHotEncoder
from sklearn.preprocessing import Normalizer
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.decomposition import PCA
#sl:satisfaction_level----False:MinMaxScaler; True:StandardScaler
#le:last_evaluation---False:MinMaxScaler; True:StandardScaler
#npr:number_project -------False:MinMaxScaler; True:StandardScaler
#amh:average_montly_hours ---False:MinMaxScaler; True:StandardScaler
#tsc:time_spend_company---False:MinMaxScaler; True:StandardScaler
#wa:Work_acident---False:MinMaxScaler; True:StandardScaler
#pl5:promotion_lase_5years--False:MinMaxScaler; True:StandardScaler
#dp:Department--False:LabelEncoding;True:OneHotEncoder
#slr:salary----False:LabelEncoding;True:OneHotEncoder
def hr_preprocessing(sl=False,le=False,npr=False,amh=False,tsc=False,wa=False,\
pl5=False,dp=False,slr=False,lower_d=False,ld_n=1):
df=pd.read_csv("HR.csv") #该函数第一步读入该表,然后返回该表
#1.清洗数据,数据量较少,而且在抽样过程中应该尽量保持它的全量数据,故可取全量数据,可不进行抽样
df=df.dropna(subset=["satisfaction_level","last_evaluation"]) # 去除这两个属性中的空值
df=df[df["satisfaction_level"]<=1][df["salary"]!="nme"]
# 2.得到标注
label=df["left"]
df=df.drop("left",axis=1) # 如果不指定1,会以行进行删除,而行没有left,故会报错
#3.特征选择(因为属性不多,所以暂时都保留)
#4.特征处理(标准化和归一化)
scaler_lst=[sl,le,npr,amh,tsc,wa,pl5]
column_lst=["satisfaction_level","last_evaluation","number_project",\
"average_montly_hours","time_spend_company","Work_accident",\
"promotion_last_5years"]
for i in range(len(scaler_lst)):# 遍历scaler_lst,sl是一个布尔型的数
if not scaler_lst[i]: # 所以当这个下标不为真时,也就是它为假时,我们就把这个df当中的属性值进行转换
df[column_lst[i]]=\
MinMaxScaler().fit_transform(df[column_lst[i]].values.reshape(-1,1)).reshape(1,-1)[0]
else:
df[column_lst[i]]=\
StandardScaler().fit_transform(df[column_lst[i]].values.reshape(-1,1)).reshape(1,-1)[0]
scaler_lst=[slr,dp]
column_lst=["salary","Department"] # 这两个属性都是离散值
for i in range(len(scaler_lst)):
if not scaler_lst[i]:
if column_lst[i]=="salary":
df[column_lst[i]]=[map_salary(s) for s in df["salary"].values]
else:
df[column_lst[i]]=LabelEncoder().fit_transform(df[column_lst[i]])
df[column_lst[i]]=MinMaxScaler().fit_transform(df[column_lst[i]].values.reshape(-1,1)).reshape(1,-1)[0]
else:
df=pd.get_dummies(df,columns=[column_lst[i]]) # one_hot
if lower_d:
return PCA(n_components=ld_n).fit_transform(df.values),label
return df,label
d=dict([("low",0),("medium",1),("high",2)])
def map_salary(s):
return d.get(s,0)
def main():
print(hr_preprocessing(sl=True,le=True,dp=True,lower_d=False,ld_n=3))
if __name__=="__main__":
main()
6).小结–特征工程
小结-数据清洗:数据样本抽样,异常值(空值)处理
小结-特征处理:特征选择;特征变换(对指化、离散化、数据平滑、归一化(标准化)、数值化、正规化);特征降维
数据平滑其实就是离散化
四、挖掘建模
1、机器学习与数据建模简介
算法只是逼近一个数据的表达信息的上界,也就是说,如果数据和特征没有表现出什么信息,那么模型和算法也不能无中生有。如果数据和特征在数量上和维度上表现力丰富并且明显的信息,那么模型就可以得到最大化的简化。所以说,“只要数据好,模型随便搞”
学习:通过接收到的数据,归纳提取相同与不同;
机器学习:让计算机以数据为基础,进行归纳与总结;
模型:数据解释现象的系统
机器学习根据是否有标注,分为:监督学习(分类,回归)、非监督学习(聚类,关联)、半监督学习。
标注相当于告诉模型,在什么样的数据特征下,应该输出什么样的结果。机器学习的任务,就是提炼出输入与标注间的关系,并进行预测。
根据标注是离散值还是连续值,监督学习又分为分类过程和回归过程。
如果标注是离散值,那么它就是分类学习;如果标注是连续值,就是回归学习。
2.训练集、测试集、验证集
我们会把一个数据集分为三个部分:一般取6:2:2
训练集:用来训练与拟合模型
验证集:通过训练集训练出多个模型后,为了能找到效果最佳的模型,使用各个参数对验证集数据进行预测,并记录模型的准确率等评价指标,选出效果最佳的模型所对应的参数作为模型的最终参数
测试集:通过训练集和验证集的出最优模型后,使用测试集进行模型的预测,用来衡量这个模型的性能和分类能力,即可以把测试集当做从来不存在的数据集,当已经确定模型的参数后,使用测试集进行模型的泛化能力评价。有的时候,我们会忽略验证集,而通过不断地尝试来达到验证的目的,这样,一个数据集就会只分为 训练集:测试集:8:2
泛化:对未知数据的预测能力
K-fold交叉验证:将数据集分成K份,每份轮流作一遍测试集,其他作训练集。在sklearn中,没有一步把训练集、测试集、验证集进行切分的函数。
但有一个直接切分训练集和测试集的函数:
from sklearn.model_selection import train_test_split
def hr_modeling(features, label):
from sklearn.model_selection import train_test_split
f_v = features.values # 之前返回的feature是一个DataFrame,所以先取它的值。
l_v = label.values
# 测试集占多少比例,我们希望得到6:2:2的比例,所以我们分两步进行切分
# 第一步:先得到验证集,相对于总体占20%,返回训练集(X_tt),验证集(X_validation)
X_tt, X_validation, Y_tt, Y_validation = train_test_split(f_v, l_v, test_size=0.2)
# 第二步:区分训练集和测试集
X_train, X_test, Y_train, Y_test = train_test_split(X_tt, Y_tt, test_size=0.25)
# print(len(X_train), len(X_validation), len(X_test))
输出:
3.分类算法
KNN,朴素贝叶斯,决策树,支持向量机,集成方法;
逻辑斯特回归,人工神经网络
后两种大多可以做分类也可以做回归,大多以回归为主!
如果属性都成了数值,那么每个属性都可以看做一个维度,每个对象都是一个空间中的坐标,那么数据表中的对象与对象之间就会有它的距离。
1)K近邻法
KNN(K_Nearest Neighbors)
欧式距离:
d
12
=
∑
k
=
1
n
(
x
1
k
−
x
2
k
)
2
d_{12}=\sqrt{\sum_{k=1}^n(x_{1k}-x_{2k}})^2
d12=k=1∑n(x1k−x2k)2
曼哈顿距离:
d
12
=
∑
k
=
1
n
∣
x
1
k
−
x
2
k
∣
d_{12}=\sum_{k=1}^n\mid x_{1k}-x_{2k}\mid
d12=k=1∑n∣x1k−x2k∣
闵可夫斯基距离:
d
12
=
∑
k
=
1
n
∣
x
1
k
−
x
2
k
∣
p
p
d_{12}=\sqrt[p]{\sum_{k=1}^n\mid x_{1k}-x_{2k}\mid^p}
d12=pk=1∑n∣x1k−x2k∣p
KD-Tree:如果一个空间中有很多的点,那么怎么去找我们随机指定的一个点附近的最近的k个点呢?当然有一种方法就是遍历每个点,然后进行从小到大的排序,但是这种方法的效率很低。所以有了KD-Tree,通过树形结构,可以达到快速的寻找最近点的目的。
KD-Tree工作原理:比如我们有空间上的16个点,KD-Tree是如何划分?首先通过一个维度上把所有的点分为两个部分,两部分数量尽可能保持一致;然后拿其中一个部分再进行另外一个维度上的切分,又切分为两个部分,以此类推,直到不能切分为止,其他几个部分同样如此。这样在这个空间里就会分成许多大小不一的格子,每个格子都通过我们这里的线建立成了一个树形的索引,如果把这些线当成是中间节点,而把这些点,最终的点当做叶子节点的话,会得到如下第二张图:这些深色的点都是这张图中的线,而白色的点都是图中白色的点,就可以形成这种树形结构,有了这种树形结果就可以很好的找到一个点附近的最近的k个点。
k近邻法输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k近邻法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其k个最近邻的训练实例的类别,通过多数表决等方式进行预测。
# -*- coding:utf-8 -*-
# @time:2021/3/16 19:42
# Author:wuxiping
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.preprocessing import Normalizer
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.decomposition import PCA
# sl:satisfaction_level----False:MinMaxScaler; True:StandardScaler
# le:last_evaluation---False:MinMaxScaler; True:StandardScaler
# npr:number_project -------False:MinMaxScaler; True:StandardScaler
# amh:average_montly_hours ---False:MinMaxScaler; True:StandardScaler
# tsc:time_spend_company---False:MinMaxScaler; True:StandardScaler
# wa:Work_accident---False:MinMaxScaler; True:StandardScaler
# pl5:promotion_lase_5years--False:MinMaxScaler; True:StandardScaler
# dp:Department--False:LabelEncoding;True:OneHotEncoder
# slr:salary----False:LabelEncoding;True:OneHotEncoder
def hr_preprocessing(sl=False, le=False, npr=False, amh=False, tsc=False, wa=False, pl5=False, dp=False, slr=False,
lower_d=False, ld_n=1):
df = pd.read_csv("HR.csv") # 该函数第一步读入该表,然后返回该表
# 1.清洗数据,数据量较少,而且在抽样过程中应该尽量保持它的全量数据,故可取全量数据,可不进行抽样
df = df.dropna(subset=["satisfaction_level", "last_evaluation"]) # 去除这两个属性中的空值
df = df[df["satisfaction_level"] <= 1][df["salary"] != "nme"]
# 2.得到标注
label = df["left"]
df = df.drop("left", axis=1) # 如果不指定1,会以行进行删除,而行没有left,故会报错
# 3.特征选择(因为属性不多,所以暂时都保留)
# 4.特征处理(标准化和归一化)
scaler_lst = [sl, le, npr, amh, tsc, wa, pl5]
column_lst = ["satisfaction_level", "last_evaluation", "number_project", "average_montly_hours",
"time_spend_company", "Work_accident", "promotion_last_5years"]
for i in range(len(scaler_lst)): # 遍历scaler_lst,sl是一个布尔型的数
if not scaler_lst[i]: # 所以当这个下标不为真时,也就是它为假时,我们就把这个df当中的属性值进行转换
df[column_lst[i]] = MinMaxScaler().fit_transform(df[column_lst[i]].values.reshape(-1, 1)).reshape(1, -1)[0]
else:
df[column_lst[i]] = StandardScaler().fit_transform(df[column_lst[i]].values.reshape(-1, 1)).reshape(1, -1)[
0]
scaler_lst = [slr, dp]
column_lst = ["salary", "Department"] # 这两个属性都是离散值
for i in range(len(scaler_lst)):
if not scaler_lst[i]:
if column_lst[i] == "salary":
df[column_lst[i]] = [map_salary(s) for s in df["salary"].values]
else:
df[column_lst[i]] = LabelEncoder().fit_transform(df[column_lst[i]])
df[column_lst[i]] = MinMaxScaler().fit_transform(df[column_lst[i]].values.reshape(-1, 1)).reshape(1, -1)[0]
else:
df = pd.get_dummies(df, columns=[column_lst[i]]) # one_hot
if lower_d:
return PCA(n_components=ld_n).fit_transform(df.values), label
return df, label
d = dict([("low", 0), ("medium", 1), ("high", 2)])
def map_salary(s):
return d.get(s, 0)
def hr_modeling(features, label):
from sklearn.model_selection import train_test_split
f_v = features.values # 之前返回的feature是一个DataFrame,所以先取它的值。
l_v = label.values
# 测试集占多少比例,我们希望得到6:2:2的比例,所以我们分两步进行切分
# 第一步:先得到验证集,相对于总体占20%,返回训练集(X_tt),验证集(X_validation)
X_tt, X_validation, Y_tt, Y_validation = train_test_split(f_v, l_v, test_size=0.2)
# 第二步:区分训练集和测试集
X_train, X_test, Y_train, Y_test = train_test_split(X_tt, Y_tt, test_size=0.25)
# print(len(X_train), len(X_validation), len(X_test))
from sklearn.neighbors import NearestNeighbors, KNeighborsClassifier
# NearestNeighbors可以直接获得一个点附近的最近的几个点
knn_clf = KNeighborsClassifier(n_neighbors=3) # 多次尝试发现n_neighbors=3的准确率大于n_neighbors=5的
knn_clf.fit(X_train, Y_train) # 拟合得到一个模型
Y_pred = knn_clf.predict(X_validation) # 得到模型后进行predict,
from sklearn.metrics import accuracy_score, recall_score, f1_score
print("Validation:")
print("ACC:", accuracy_score(Y_validation, Y_pred)) # 去预测它的验证集得到一个预测值,拿预测值和实际值比较,得到它的准确率
print("REC:", recall_score(Y_validation, Y_pred))
print("F-score:", f1_score(Y_validation, Y_pred))
# 用测试集进行对比:
print("Test:")
Y_pred = knn_clf.predict(X_test)
print("ACC:", accuracy_score(Y_test, Y_pred)) # 去预测它的测试集得到一个预测值,拿预测值和实际值比较,得到它的准确率
print("REC:", recall_score(Y_test, Y_pred))
print("F-score:", f1_score(Y_test, Y_pred))
# 用训练集做对比
print("Train:")
Y_pred = knn_clf.predict(X_train)
print("ACC:", accuracy_score(Y_train, Y_pred))
print("REC:", recall_score(Y_train, Y_pred))
print("F-score:", f1_score(Y_train, Y_pred))
# 准确率都比训练集低,说明存在一定的微小的过拟合现象,可忽略不计
# 存储好训练出来的模型
from sklearn.externals import joblib
joblib.dump(knn_clf, "knn_clf") # 存储该模型
knn_clf = joblib.load("knn_clf") # 使用该模型
def main(): # 在main()函数中,直接调用hr_preprocessing
features, label = hr_preprocessing()
hr_modeling(features, label)
if __name__ == "__main__":
main()
2)朴素贝叶斯
特征之间是相互独立,所以称朴素。
1.条件概率
P ( B ∣ A ) = P ( A B ) P ( A ) P(B\mid A)=\frac {P(AB)}{P(A)} P(B∣A)=P(A)P(AB)
2.联合概率
P ( A B ) = P ( A ∣ B ) P ( B ) = P ( B ∣ A ) P ( A ) P(AB)=P(A\mid B)P(B)=P(B\mid A)P(A) P(AB)=P(A∣B)P(B)=P(B∣A)P(A)
3.全概率公式
定义:设
S
S
S为试验
E
E
E的样本空间,
B
1
,
B
2
,
.
.
.
,
B
n
B_1,B_2,...,B_n
B1,B2,...,Bn为
E
E
E的一组事件。若
1)
B
i
B
j
=
∅
,
i
≠
j
,
i
,
j
=
1
,
2
,
.
.
.
,
n
B_iB_j=\emptyset,i\not=j,i,j=1,2,...,n
BiBj=∅,i=j,i,j=1,2,...,n;
2)
B
1
∪
B
2
∪
.
.
.
∪
B
n
=
S
B_1\cup B_2 \cup ...\cup B_n=S
B1∪B2∪...∪Bn=S,则称
B
1
,
B
2
,
.
.
.
,
B
n
B_1,B_2,...,B_n
B1,B2,...,Bn为样本空间
S
S
S的一个划分。
若
B
1
,
B
2
,
.
.
.
,
B
n
B_1,B_2,...,B_n
B1,B2,...,Bn为样本空间的一个划分,那么对每次试验,事件
B
1
,
B
2
,
.
.
.
,
B
n
B_1,B_2,...,B_n
B1,B2,...,Bn中必有一个且仅有一个发生。
P ( A ) = P ( A B 1 ) + P ( A B 2 ) + . . . P(A)=P(AB_1)+P(AB_2)+... P(A)=P(AB1)+P(AB2)+...
⟹ \implies ⟹全概率公式 P ( A ) = ∑ P ( A ∣ B i ) P ( B i ) P(A)=\sum P(A\mid B_i) P(B_i) P(A)=∑P(A∣Bi)P(Bi)
4.贝叶斯公式:
P ( B i ∣ A ) = P ( B i A ) P ( A ) = P ( A ∣ B i ) P ( B i ) ∑ P ( A ∣ B j ) P ( B j ) P(B_i\mid A)=\frac{P(B_iA)}{P(A)}=\frac{P(A\mid B_i)P(B_i)}{\sum P(A\mid B_j)P(B_j)} P(Bi∣A)=P(A)P(BiA)=∑P(A∣Bj)P(Bj)P(A∣Bi)P(Bi)
5.生成模型和判别模型
生成模型:通过求输入与输出的联合概率分布,再求解类别归类概率
P
(
B
∣
A
)
=
P
(
A
∣
B
)
P
(
B
)
P
(
A
)
P(B\mid A)=\frac{P(A\mid B)P(B)}{P(A)}
P(B∣A)=P(A)P(A∣B)P(B)
判别模型:不通过联合概率分布,直接可以获得输出对应最大分类的概率
生成模型:一般情况下对数据的要求更高一些,比如朴素贝叶斯中要求数据是离散的,速度相对快一些 判别模型:速度相对慢些,但是对数据容忍程度的更大一些,使用的范围也更广一些
3)决策树
分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点(node)和有向边(directed edge)组成。节点有两种类型:内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性,叶结点表示一个类。
决定特征顺序的方法:
1.信息增益ID3
熵 : H ( X ) = − ∑ p i l o g ( p i ) 熵:H(X)=-\sum p_ilog(p_i) 熵:H(X)=−∑pilog(pi)
信 息 增 益 : I ( X , Y ) = H ( Y ) − H ( Y ∣ X ) = H ( X ) − H ( X ∣ Y ) 信息增益:I(X,Y)=H(Y)-H(Y\mid X)=H(X)-H(X\mid Y) 信息增益:I(X,Y)=H(Y)−H(Y∣X)=H(X)−H(X∣Y)
例: E n t r o p y ( S ) = − 9 14 ∗ l o g 2 ( 9 14 ) − 5 14 ∗ l o g 2 ( 5 14 ) Entropy(S)=-\frac9{14}*log_2(\frac9{14})-\frac5{14}*log_2(\frac5{14}) Entropy(S)=−149∗log2(149)−145∗log2(145)
G
a
i
n
(
W
i
n
d
)
=
E
n
t
r
o
p
y
(
S
)
−
8
14
∗
E
n
t
r
o
p
y
(
W
e
a
k
)
−
6
14
∗
E
n
t
r
o
p
y
(
S
t
r
o
n
g
)
=
0.940
−
8
14
∗
0.811
−
6
14
∗
1.0
=
0.048
Gain(Wind)=Entropy(S)-\frac8{14}*Entropy(Weak)-\frac6{14}*Entropy(Strong)=0.940-\frac8{14}*0.811-\frac6{14}*1.0=0.048
Gain(Wind)=Entropy(S)−148∗Entropy(Weak)−146∗Entropy(Strong)=0.940−148∗0.811−146∗1.0=0.048
2.信息增益率C4.5
G
a
i
n
R
a
t
i
o
(
X
−
>
Y
)
=
I
(
X
,
Y
)
H
(
Y
)
GainRatio(X->Y)=\frac{I(X,Y)}{H(Y)}
GainRatio(X−>Y)=H(Y)I(X,Y)
3.Gini系数-CART
G
i
n
i
(
D
)
=
1
−
∑
(
C
k
D
)
2
Gini(D)=1-\sum(\frac{C_k}{D})^2
Gini(D)=1−∑(DCk)2
之前的决策树都是离散值,如果遇到了连续值:注意几个问题:
4)支持向量机
在充分将样本分开的情况下有一种区分方法可以最大限度的将两个标注的样本进行区分,也就是说,两个标注样本中分别找出离这条线最近的点,它们离这条线的距离是一样的,且它们的距离和是最大的。这样的切分,就是区分度最大的切分方法,此时,离这条线最近的两个标注中的样本,就是SVM中的支持向量,
多维空间中的维度可以用向量
x
x
x来表示,它的分量代表着各个维度,
w
w
w向量是它的参数,多为空间中的面,也叫超平面。
w
T
=
[
w
0
,
w
1
,
w
2
,
.
.
.
,
w
n
]
,
x
T
=
[
x
0
,
x
1
,
x
2
,
.
.
.
,
x
n
]
w^T=[w_0,w_1,w_2,...,w_n],x^T=[x_0,x_1,x_2,...,x_n]
wT=[w0,w1,w2,...,wn],xT=[x0,x1,x2,...,xn]
高维面:
w
T
x
+
b
=
0
w^Tx+b=0
wTx+b=0
分界面:
w
T
x
p
+
b
≥
ε
,
w
T
x
n
+
b
≤
−
ε
w^Tx_p+b \geq \varepsilon,w^Tx_n+b \leq -\varepsilon
wTxp+b≥ε,wTxn+b≤−ε
w
T
x
p
+
b
≥
1
,
w
T
x
n
+
b
≤
−
1
w^Tx_p+b \geq 1,w^Tx_n+b \leq -1
wTxp+b≥1,wTxn+b≤−1
⟹
y
i
(
w
T
x
i
+
b
)
≥
1
\Longrightarrow y_i(w^Tx_i+b)\geq1
⟹yi(wTxi+b)≥1
点到面的距离公式:
d
=
∣
A
x
0
+
B
y
0
+
C
z
0
+
D
A
2
+
B
2
+
C
2
∣
d=\mid \frac{Ax_0+By_0+Cz_0+D}{\sqrt{A^2+B^2+C^2}}\mid
d=∣A2+B2+C2Ax0+By0+Cz0+D∣,即求
m
a
x
2
∣
∣
w
2
∣
∣
,
⟺
m
i
n
∣
∣
w
2
∣
∣
2
max \frac{2}{\mid\mid w^2\mid\mid},\Longleftrightarrow min\frac{\mid\mid w^2\mid\mid}{2}
max∣∣w2∣∣2,⟺min2∣∣w2∣∣
s
.
t
.
\bf s.t.
s.t.
y
i
(
w
T
x
i
+
b
)
≥
1
y_i(w^Tx_i+b)\geq1
yi(wTxi+b)≥1
⟹
\Longrightarrow
⟹拉格朗日乘数法
L
=
1
2
∣
∣
w
2
∣
∣
−
∑
n
=
1
N
a
n
∗
(
y
n
(
w
T
x
n
+
b
)
−
1
)
L=\frac{1}{2}\mid\mid w^2\mid\mid-\sum_{n=1}^Na_n*{(y_n(w^Tx_n+b)-1)}
L=21∣∣w2∣∣−n=1∑Nan∗(yn(wTxn+b)−1)
若标注不是线性可分?两个思路
1)容忍一部分错误归类
2)扩维
在扩维时怎么知道该如何扩维?扩到几维?实际上,由于标注不确定性比较大,我们并不知道如何扩维,所以只能把所有可能扩到的维度都考虑到!比如下面的多项式扩维法:二维扩到5维,三维扩到19维。
例子:
最初进行扩维的方式思路是先映射到高维空间,再进行计算,这样会产生维度灾难。后来转变了思路,先在低维空间进行计算,再进行扩维。而扩维的方式是使用一种转换函数来进行,而这种转换函数就是核函数。
常用的核函数有以下几种:
几个问题:
加入松弛变量;看场景定方案(对不同的标注赋予不同的权值)
5)集成方法
1.熵增益的决策树适合解决离散值多的问题;
2.基于基尼系数的cart决策树适合解决连续值分类的问题;
3.若果标注在空间内,隔离性比较好,那么KNN比较合适;SVM也能解决这类问题,而且有一定的抗过拟合的能力;
**集成学习:**组合多个模型,以获得更好的效果
算法复杂度:
1)多项式复杂度:
O
(
n
p
)
O(n^p)
O(np),
2)阶乘复杂度:
O
(
n
!
)
O(n!)
O(n!),
3)指数级复杂度:
O
(
m
n
)
O(m^n)
O(mn)
…
故设计的算法应尽可能小的设计后几种算法,尽可能多的设计多项式复杂度的算法。
模型的集成方法就是将几个弱可学习的分类器集合成一个强可学习的分类器的过程。
强可学习:多项式学习算法的效果较为明显;
弱可学习:多项式学习算法的效果不很明显
集成思想用到的具体的方法有两类:
1)袋装法(Bagging)
袋装法就是用训练集同时训练出几个模型,而在进行预测和判断时,我们分别让几个子模型去进行判断,然后对于分类问题,让他门去投票,投票选出的最多的结果就是我们最终判断的结果。回归时,取各个结果的参数。
袋装法典型应用就是随机森林算法
2)提升法(boost)
如果我们把这些子模型串联起来,一个模型以另一个模型的结果为基础,进行训练和预测, 然后多个模型级联,最终将每个模型训练的结果进行加权求和,得到判决结果。
提升法的一个例子:Adaboost
Given:
(
x
1
,
y
1
)
,
.
.
.
,
(
x
m
,
y
m
)
w
h
e
r
e
x
i
∈
X
,
y
i
∈
Y
=
{
−
1
,
1
}
(x_1,y_1),...,(x_m,y_m)\;where x_i\in X,y_i\in Y=\lbrace -1,1\rbrace
(x1,y1),...,(xm,ym)wherexi∈X,yi∈Y={−1,1}
Initialize
D
1
(
i
)
=
1
/
m
D_1(i)=1/m
D1(i)=1/m
For
t
=
1
,
.
.
.
.
,
T
:
t=1,....,T:
t=1,....,T:
Train weak learner using distribution
D
t
D_t
Dt
Get weak hypothesis h t : X → { − 1 , + 1 } h_t:X\rightarrow \lbrace -1,+1 \rbrace ht:X→{−1,+1} with error
ε
t
=
P
r
i
−
D
t
[
h
t
(
x
i
)
≠
y
i
]
\varepsilon_t=Pr_{i-D_t}[h_t(x_i)\not=y_i]
εt=Pri−Dt[ht(xi)=yi]
Adaboost的优点:
精度高,且灵活可调;
几乎不用担心过拟合;
简化特征工程流程;
4.回归算法
**简介:**线性回归,决策树,支持向量机,集成方法,逻辑斯特映射,人工神经网络
回归分析(regression analysis)是确定多个变量间相互依赖的定量关系的一种统计分析方法。
回归分析中,把自变量叫特征,因变量叫标注。只是这个标注是连续值而不是离散值,如果多个变量间的关系我们用线性关系去考量,那就是线性回归;如果多个变量间的关系用多项式关系去考量,那就是多项式回归。
回归要考虑模型的复杂度和准确度两个方面,当然最重要的是它对未知数据预测的准确性。所以我们也可以像分类学习一样,将回归的数据也进行区分,用验证集的误差大小去评判回归效果最终的好坏。极端的情况,我们可以用一种非常高阶的函数对它进行拟合,比如说我们有n个点,我们就可以用n+1阶多项式去拟合,一定可以在训练集上把它的误差降为零。但是这种拟合面对未知数据的预测大概率是不会太好的
1)线性回归
五、模型评估
1.分类评估
1)二分类评估
关注的类为正类:离职,刷单,
混淆矩阵:
关键指标:
A
c
c
u
r
a
c
y
R
a
t
e
:
(
T
P
+
T
N
)
/
(
T
P
+
T
N
+
F
N
+
F
P
)
Accuracy Rate:(TP+TN)/(TP+TN+FN+FP)
AccuracyRate:(TP+TN)/(TP+TN+FN+FP)