文章目录
引言
数据预处理与特征工程包括Data PreProcessing(数据预处理)、Feature Extraction(特征提取)、Feature Selection(特征选择)和Feature construction(特征构造)等步骤
1.数据预处理
数据预处理又包括数据清洗与特征预处理两步
1.1 数据清洗
数据清洗主要是删除原始数据集中的无关数据、重复数据,平滑噪声数据,筛选掉与挖掘主题无关的数据,处理缺失值、异常值等
1.1.1 异常值处理
异常值是否需要处理需要视具体情况而定,因为有些异常值可能蕴含着有用的信息。
-
简单统计量分析
在进行异常值分析时,可以先对变量做一个描述性统计,进而查看哪些数据是不合理的。最常用的统计量是最大值和最小值,用来判断这个变量的取值是否超出了合理范围。如客户年龄的最大值为199岁,则判断该变量的取值存在异常。 -
通过箱线图分析删除异常值;
箱型图提供了识别异常值的一个标准:异常值通常被定义为小于 Q L − 1.5 I Q R Q_L-1.5IQR QL−1.5IQR或大于 Q U + 1.5 I Q R Q_U+1.5IQR QU+1.5IQR的值。 Q L Q_L QL称为下四分位数,表示全部观察值中有四分之一的数据取值比它小; Q U Q_U QU称为上四分位数,表示全部观察值中有四分之一的数据取值比它大;IQR称为四分位数间距,是上四分位数 Q U Q_U QU与下四分位数 Q L Q_L QL之差,其间包含了全部观察值的一半。这里的1.5
可以根据问题的不同进行改变。箱型图依据实际数据绘制,对数据没有任何限制性要求,如服从某种特定的分布形式,它只是真实直观地表现数据分布的本来面貌;另一方面,箱型图判断异常值的标准以四分位数和四分位距为基础,四分位数具有一定的鲁棒性:多达25%的数据可以变得任意远而不会严重扰动四分位数,所以异常值不能对这个标准施加影响。由此可见,箱型图识别异常值的结果比较客观,在识别异常值方面有一定的优越性
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns def box_plot_outliers(data_ser, box_scale): """ 利用箱线图去除异常值 :param data_ser: 接收 pandas.Series 数据格式 :param box_scale: 箱线图尺度, :return: """ iqr = box_scale*(data_ser.quantile(0.75) - data_ser.quantile(0.25)) # Q_L - 1.5IQR为下界 val_low = data_ser.quantile(0.25) - iqr # Q_U + 1.5IQR为上界 val_up = data_ser.quantile(0.75) + iqr rule_low = (data_ser < val_low) rule_up = (data_ser > val_up) return (rule_low, rule_up), (val_low, val_up) def outliers_proc(data, col_name, scale=1.5): """ 用于清洗异常值,默认用 box_plot(scale=1.5)进行清洗 :param data: 接收 pandas 数据格式 :param col_name: pandas 列名 :param scale: 尺度 :return: """ # 复制数据 data_n = data.copy() # 针对哪一个特征清洗异常值 data_series = data_n[col_name] # 返回异常值索引(bool格式)与上下边界 rule, value = box_plot_outliers(data_series, box_scale=scale) # 得到异常值得索引 # |会先转化成二进制,然后相同位数的数字有1则为1,否则为0 index = np.arange(data_series.shape[0])[rule[0] | rule[1]] print("Delete number is: {}".format(len(index))) # 删除异常值 data_n = data_n.drop(index) # 重置索引 data_n.reset_index(drop=True, inplace=True) print("Now column number is: {}".format(data_n.shape[0])) # 统计低于下界的异常值 index_low = np.arange(data_series.shape[0])[rule[0]] outliers = data_series.iloc[index_low] print("Description of data less than the lower bound is:") print(pd.Series(outliers).describe()) # 统计高于上界的异常值 index_up = np.arange(data_series.shape[0])[rule[1]] outliers = data_series.iloc[index_up] print("Description of data larger than the upper bound is:") print(pd.Series(outliers).describe()) # 查看删除异常值前后图形的区别 fig, ax = plt.subplots(1, 2, figsize=(10, 7)) sns.boxplot(y=data[col_name], data=data, palette="Set1", ax=ax[0]) sns.boxplot(y=data_n[col_name], data=data_n, palette="Set1", ax=ax[1]) return data_n
-
通过 3 σ 3σ 3σ原则删除异常值
如果数据服从正态分布,在 3 σ 3σ 3σ原则下,异常值被定义为一组测定值中与平均值的偏差超过3倍标准差的值。在正态分布的假设下,距离平均值 3 σ 3σ 3σ之外的值出现的概率为P(x-u |> 3 σ 3σ 3σ)≤0.003,属于极个别的小概率事件。
如果数据不服从正态分布,也可以用远离平均值的标准差倍数来描述。 -
长尾截断;
长尾截断主要也是分布不符合正态分布,而是类似于“长尾”。例如房价中,低价占大部分,豪宅属于小部分。应对这种数据分布,一般可以通过神奇的log化处理转化类近似正态分布或者使用Box-Cox转换将数据转换为正态。数据预处理—5.box-cox变换及python实现
-
将异常值视为缺失值,利用缺失处理方法进行处理
1.1.2 缺失值处理
缺失值处理的方法可分为3种:删除记录、数据插补和不处理
- 不处理(针对类似 XGBoost 等树模型);
- 删除(缺失数据太多);
- 插值补全,包括均值/中位数/众数/使用固定值/最近邻插补/回归方法/插值法(拉格朗日插值法、牛顿插值法)等;
前面几种方面使用pandas
中的fillna
函数可以轻松实现
插值法:数据预处理—7.数据插补之拉格朗日插值法、牛顿差值法及python实现
回归方法:对带有缺失值的变量,根据已有数据和其有关的其他变量(因变量)的数据建立拟合模型来预测缺失的属性值。6.4 随机森林实战这里面使用了随机森林来预测年龄中的缺失值作为填充 - 分箱,缺失值一个箱;
见后面
1.2 特征预处理
1.2.1 数值型特征无量纲化
数值型特征无量纲化是为了消除样本不同属性具有不同量级(大小)时的影响,不仅提高精度,而且提高迭代精度
-
标准化(转换为标准正态分布);
from sklearn.preprocessing import MinMaxScaler,StandardScaler # 标准化 scaler = StandardScaler() result = scaler.fit_transform(data) # 将data标准化 scaler.inverse_transform(result) # 将标准化结果逆转
优点:
标准化最大的优点就是简单,容易计算,Z-Score能够应用于数值型的数据,并且不受数据量级(数据多少)的影响,因为它本身的作用就是消除量级给分析带来的不便。
缺点:-
估算Z-Score需要总体的平均值与方差,但是这一值在真实的分析与挖掘中很难得到,大多数情况下是用样本的均值与标准差替代。
-
Z-Score对于数据的分布有一定的要求,正态分布是最有利于Z-Score计算的。
-
Z-Score消除了数据具有的实际意义,属性A的Z-Score与属性B的Z-Score与他们各自的分数不再有关系,因此Z-Score的结果只能用于比较数据间的结果,数据的真实意义还需要还原原值。
-
在存在异常值时无法保证平衡的特征尺度。
-
-
归一化(转换到 [0,1] 区间);
from sklearn.preprocessing import MinMaxScaler,StandardScaler # 归一化 scaler = MinMaxScaler() result = scaler.fit_transform(data) # 将data归一化 scaler.inverse_transform(result) # 将归一化结果逆转
缺点:
-
这种方法有一个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义。
-
MinMaxScaler对异常值的存在非常敏感。
-
-
MaxAbs归一化
定义:单独地缩放和转换每个特征,使得训练集中的每个特征的最大绝对值将为1.0,将属性缩放到[-1,1]。它不会移动/居中数据,因此不会破坏任何稀疏性。
from sklearn.preprocessing import MaxAbsScaler maxAbsScaler = MaxAbsScaler().fit(X_train) maxAbsScaler.transform(X_train)
缺点:
- 这种方法有一个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义;
- 对异常值的存在非常敏感
-
正态分布化(Normalization)
定义:正则化的过程是将每个样本缩放到单位范数(每个样本的范数为1)。Normalization主要思想是对每个样本计算其p-范数,然后对该样本中每个元素除以该范数,这样处理的结果是使得每个处理后样本的p-范数(l1-norm,l2-norm)等于1。
适用情形:如果要使用如二次型(点积)或者其它核方法计算两个样本之间的相似性这个方法会很有用。该方法是文本分类和聚类分析中经常使用的向量空间模型(Vector Space Model)的基础。from sklearn.preprocessing import Normalizer #正态归一化,返回值为正态归一化后的数据 normalizer = Normalizer(norm='l2').fit(X_train) normalizer.transform(X_train)
-
针对幂律分布,可以采用公式: l o g 1 + x 1 + m e d i a n log\frac{1+x}{1+median} log1+median1+x
1.2.2 连续数值型特征分箱
一些数据挖掘算法,特别是某些分类算法,如ID3算法、Apriori算法等,要求数据是分类属性形式。这样,常常需要将连续属性变换成分类属性,即连续属性离散化。
连续属性离散化的优势:
- 离散后稀疏向量内积乘法运算速度更快,计算结果也方便存储,容易扩展;
- 离散后的特征对异常值更具鲁棒性,如 age>30 为 1 否则为 0,对于年龄为 200 的也不会对模型造成很大的干扰;
- LR 属于广义线性模型,表达能力有限,经过离散化后,每个变量有单独的权重,这相当于引入了非线性,能够提升模型的表达能力,加大拟合;
- 离散后特征可以进行特征交叉,提升表达能力,由 M+N 个变量编程 M*N 个变量,进一步引入非线形,提升了表达能力;
- 特征离散后模型更稳定,如用户年龄区间,不会因为用户年龄长了一岁就变化
连续属性离散化就是在数据的取值范围内设定若干个离散的划分点,将取值范围划分为一些离散化的区间,最后用不同的符号或整数值代表落在每个子区间中的数据值。所以,连续属性离散化涉及两个子任务:确定分类数以及如何将连续属性值映射到这些分类值。特征分箱可分为无监督分箱与有监督分箱方法。
1.2.2.1 无监督分箱法
-
自定义分箱
定义:自定义分箱,是根据业务经验或者常识等自行设定划分的区间,然后将原始数据归类到各个区间中。 -
等距分箱
将属性的值域分成具有相同宽度的区间,区间的个数由数据本身的特点决定或者用户指定。等距分箱只考虑区间宽度相同,每个区间里面的实例数量可能不等。import pandas as pd import numpy as np df = pd.DataFrame([[22, 1], [13, 1], [33, 1], [52, 0], [16, 0], [42, 1], [53, 1], [39, 1], [26, 0], [66, 0]], columns=['age', 'Y']) k = 4 # 等宽离散化,各个类别依次命名为0,1,2,3 df['age_bin'] = pd.cut(df['age'], k) df['age_bin_label'] = pd.cut(df['age'], k, labels=range(k)) print(df)
age Y age_bin age_bin_label 0 22 1 (12.947, 26.25] 0 1 13 1 (12.947, 26.25] 0 2 33 1 (26.25, 39.5] 1 3 52 0 (39.5, 52.75] 2 4 16 0 (12.947, 26.25] 0 5 42 1 (39.5, 52.75] 2 6 53 1 (52.75, 66.0] 3 7 39 1 (26.25, 39.5] 1 8 26 0 (12.947, 26.25] 0 9 66 0 (52.75, 66.0] 3
缺点:一方面需要人为规定划分的区间个数,另一方面,它对离群点比较敏感,倾向于不均匀地把属性值分布到各个区间。有些区间包含许多数据,而另外一些区间的数据极少,这样会严重损坏建立的决策模型。
-
等频分箱;
将相同数量的记录放进每个区间。import pandas as pd import numpy as np df = pd.DataFrame([[22, 1], [13, 1], [33, 1], [52, 0], [16, 0], [42, 1], [53, 1], [39, 1], [26, 0], [66, 0]], columns=['age', 'Y']) k = 5 # 等频离散化,各个类别依次命名为0,1,2,3,4 df['age_bin'] = pd.qcut(df['age'], k) df['age_bin_label'] = pd.qcut(df['age'], k, labels=range(k)) print(df)
age Y age_bin age_bin_label 0 22 1 (20.8, 30.2] 1 1 13 1 (12.999, 20.8] 0 2 33 1 (30.2, 40.2] 2 3 52 0 (40.2, 52.2] 3 4 16 0 (12.999, 20.8] 0 5 42 1 (40.2, 52.2] 3 6 53 1 (52.2, 66.0] 4 7 39 1 (30.2, 40.2] 2 8 26 0 (20.8, 30.2] 1 9 66 0 (52.2, 66.0] 4
缺点:
等频法虽然避免了等距分箱问题的产生,却可能将相同的数据值分到不同的区间,以满足每个区间中固定的数据个数。 -
基于聚类分箱
定义:基于k均值聚类的分箱方法,k均值聚类法将观测值聚为k类,但在聚类过程中需要保证分箱的有序性,第一个分箱中所有观测值都要小于第二个分箱中的观测值,第二个分箱中所有观测值都要小于第三个分箱中的观测值,以此类推。
聚类分箱具体步骤:- 对预处理后的数据进行归一化处理。
- 将归一化处理过的数据,应用k-means聚类算法,划分为多个区间:采用等距法设定k-means聚类算法的初始中心,得到聚类中心。
- 在得到聚类中心后将相邻的聚类中心的中点作为分类的划分点,将各个对象加入到距离最近的类中,从而将数据划分为多个区间。
- 重新计算每个聚类中心,然后重新划分数据,直到每个聚类中心不再变化,得到最终的聚类结果。
import pandas as pd import numpy as np from sklearn.cluster import KMeans k = 4 df = pd.DataFrame([[22, 1], [13, 1], [33, 1], [52, 0], [16, 0], [42, 1], [53, 1], [39, 1], [26, 0], [66, 0]], columns=['age', 'Y']) # k为聚成几类 kmodel = KMeans(n_clusters=k) # 训练模型 kmodel.fit(df['age'].values.reshape(len(df), 1)) # 求聚类中心 c = pd.DataFrame(kmodel.cluster_centers_,columns=['聚类中心']) # 排序 c = c.sort_values(by='聚类中心') # 用滑动窗口求均值的方法求相邻两项求中点,作为边界点 w = c.rolling(window=2).mean().iloc[1:] # 把首末边界点加上 w = [0] + list(w['聚类中心'].values) +[df['age'].max()] # df['age_bins'] = pd.cut(df['age'], w) df['age_bins_label'] = pd.cut(df['age'], w, labels=range(k)) print(df)
age Y age_bins age_bins_label 0 22 1 (0.0, 28.625] 0 1 13 1 (0.0, 28.625] 0 2 33 1 (28.625, 45.25] 1 3 52 0 (45.25, 59.25] 2 4 16 0 (0.0, 28.625] 0 5 42 1 (28.625, 45.25] 1 6 53 1 (45.25, 59.25] 2 7 39 1 (28.625, 45.25] 1 8 26 0 (0.0, 28.625] 0 9 66 0 (59.25, 66.0] 3
-
二值化分箱
定义:二值化可以将数值型(numerical)的特征进行阀值化得到boolean型数据。这对于下游的概率估计来说可能很有用(比如:数据分布为Bernoulli分布时)。定量特征二值化的核心在于设定一个阈值,大于阈值的赋值为1,小于等于阈值的赋值为0。from sklearn.preprocessing import Binarizer # Binarizer函数也可以设定一个阈值,结果数据值大于阈值的为1,小于阈值的为0 binarizer = Binarizer(threshold=0.0).fit(X_train) binarizer.transform(X_train)
1.2.2.2 有监督分箱法
- 卡方分箱
定义:自底向上的(即基于合并的)数据离散化方法。它依赖于卡方检验:具有最小卡方值的相邻区间合并在一起,直到满足确定的停止准则。
基本思想:对于精确的离散化,相对类频率在一个区间内应当完全一致。因此,如果两个相邻的区间具有非常类似的类分布,则这两个区间可以合并,否则,它们应当保持分开(组内的差别很小,组间的差别很大)。而低卡方值表明它们具有相似的类分布。
卡方分箱的具体步骤:
连续变量的卡方分箱,返回分箱点,下面是在申请评分卡中,进行卡方分箱的函数def AssignGroup(x, bin): """ 将超过100个的属性值调整到100个 :param x: 属性值 :param bin: 99个分割点 :return: 调整后的值 """ N = len(bin) if x <= min(bin): return min(bin) elif x