特征工程
Comment离散变量处理
离散型变量一般有两种处理方式:LabelEncoder和OneHotEncoder
LabelEncoder
LabelEncoder既可以对文字型内容进行编码,也可以对数值型内容进行编码。(从0开始)
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit_transform(['天津','北京','上海','南京','北京'])
#array([3, 1, 0, 2, 1], dtype=int64)
le.transform(['天津','北京','上海','南京','北京'])
#array([3, 1, 0, 2, 1], dtype=int64)
OneHotEncoder
离散特征中有多少不同的值,就用多少维来表示该特征,是分类变量作为二进制向量的表示。
假如我们要计算的文本中一共出现了4个词:猫、狗、牛、羊。向量里每一个位置都代表一个词。所以用 one-hot 来表示就是:
猫:[1,0,0,0]
狗:[0,1,0,0]
牛:[0,0,1,0]
羊:[0,0,0,1]
one-hot 的缺点如下:
- 无法表达词语之间的关系
- 这种过于稀疏的向量,导致计算和存储的效率都不高
from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder()
x=[['天津'],['北京'],['上海'],['南京'],['北京']]
ohe.fit_transform(x).toarray()
#array([[0., 0., 0., 1.],
# [0., 1., 0., 0.],
# [1., 0., 0., 0.],
# [0., 0., 1., 0.],
# [0., 1., 0., 0.]])
ohe.transform(x).toarray()
#array([[0., 0., 0., 1.],
# [0., 1., 0., 0.],
# [1., 0., 0., 0.],
# [0., 0., 1., 0.],
# [0., 1., 0., 0.]])
import pandas as pd
#get dummy
dummy_feature = pd.get_dummies(df['label'],drop_first=True)
pd.concat([df[['feature','feature1']],dummy_feature,axis=1])
合并稀疏特征
做特征工程时,我们可能会遇到一个特征我们假设其特征列的符号值为v,其特征存在多种取值,标签label设为y,特征v如果有很多特征值对应标签y是相同的,那么这些v之间是没有意义的,我们称之为稀疏特征,这个时候可以进行合并稀疏特征,可以降低计算成本和错误分类的可能性。
import pandas as pd
def merge_sparse_feature(df):
df.loc[(df['x'] == 'one')
| (df['x'] == 'two')
| (df['x'] == 'three')
| (df['x'] == 'four')
, 'x'] = 'x_1'
df.loc[(df['x'] == 'five'
| (df['x'] == 'six'
| (df['x'] == 'seven'
| (df['x'] == 'eight')
| (df['x'] == 'nine')
, 'x'] = 'x_2'
return df
统计特征
当前样本集的各种统计信息,包括均值、最大值、1分位数、4分位数等。
import pandas as pd
import numpy as np
series = pd.Series(np.random.randn(500))
series.describe()
'''
count 500.000000
mean -0.063427
std 1.007670
min -3.032698
25% -0.749055
50% 0.001290
75% 0.607432
max 2.900834
dtype: float64
'''
#例子
temp = data.groupby(['file_id',feat]
[feat].count().groupby(['file_id']).agg(['min','max','mean','median','std',pd.Series.mad,pd.Series.skew,pd.Series.kurt]).add_prefix(feat+'_cnt_')
dealed_train = pd.concat([dealed_train, temp],axis=1)
方法 | 说明 |
---|---|
count | 非NA的值数量 |
describe | 针对Series和DataFrame列计算汇总统计 |
min、max | 计算最小值和最大值 |
argmin、argmax | 计算能够获取到最小值和最大值的索引位置 |
idxmin、indxmax | 计算能够获取到最小值和最大值的索引值 |
quantile | 计算四分位数 |
sum | 值的总和 |
mean | 值的平均数 |
median | 值的算术中位数(第50百分位数) |
mad | 根据平均值计算平均绝对离差 |
var | 样本值的方差 |
std | 样本值的标准差 |
skew | 样本值的偏度 |
kurt | 样本值的峰度 |
cumsum | 样本值的累计和 |
cummin、cummax | 累计最大值和累计最小值 |
cumprod | 累计积 |
diff | 计算一阶差分(对时间序列很有用) |
pct_change | 计算百分数变化 |
特征组合&交叉特征
采用原始特征 + 组合特征的方法,让模型学习到更复杂的非线性特征。
离散特征:笛卡尔积
比如属性A有三个特征,属性B有两个特征,笛卡尔积后就有六个组合特征,然后用one hot 或其他embedding方式给新的特征编码。这种暴力做交叉很可能导致特征稀疏的问题。
连续特征:
除了一般对于连续型特征的加减乘除生成新的特征以外,还可以对多个特征(连续特征离散化)进行组合。
简单特征组合:拼接
1.user_id&&category: 10001&&女裙,10002&&男士牛仔
2.user_id&&style:10001&&蕾丝,10002&&全棉
实际应用过程中不会直接将用户id号和商品组合而产生非常稀疏的矩阵,会事先对用户做一个聚类。
构造多项式
很多情况下,多项式特征是通过考虑输入数据中的非线性特征来增加模型的复杂性,它能捕捉到特征中高阶和相互作用的项。
多项式生成函数:
sklearn.preprocessing.PolynomialFeatures(degree=2, interaction_only=False, include_bias=True)
degree:默认为2,多项式次数(就同几元几次方程中的次数一样)
interaction_only:是否包含单个自变量**n(n>1)特征数据标识,默认为False,为True则表示去除与自己相乘的情况
include_bias:是否包含偏差标识,默认为True,为False则表示不包含偏差项
from sklearn.preprocessing import PolynomialFeatures
x = np.arange(6).reshape(3,2)
print x
[[0 1]
[2 3]
[4 5]]
poly = PolynomialFeatures()#默认输入两个参数
help(poly)
poly.fit_transform(x)
array([[ 1., 0., 1., 0., 0., 1.],
[ 1., 2., 3., 4., 6., 9.],
[ 1., 4., 5., 16., 20., 25.]])
# 设置参数interaction_only = True,不包含单个自变量****n(n>1)特征数据
poly = PolynomialFeatures(degree = 2, interaction_only = True)
poly.fit_transform(X)
array([[ 1., 0., 1., 0.],
[ 1., 2., 3., 6.],
[ 1., 4., 5., 20.]])
# 再添加 设置参数include_bias= False,不包含偏差项数据
poly = PolynomialFeatures(degree = 2, interaction_only = True, include_bias=False)
array([[ 0., 1., 0.],
[ 2., 3., 6.],
[ 4., 5., 20.]])
模型特征交叉
GBDT+LR、FM、FFM、DeepFM等
特征归一化
对房屋售价进行预测时,我们的特征仅有房屋面积一项,但是,在实际生活中,卧室数目也一定程度上影响了房屋售价。房屋面积及卧室数量两个特征在数值上差异巨大,如果直接将该样本送入训练,则代价函数的轮廓会是“扁长的”,在找到最优解前,梯度下降的过程不仅是曲折的,也是非常耗时的。数据归一化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。
- 归一化(normalization):
- 标准化(standardization):
其中 和 代表样本的均值和标准差, 为最大值, 为最小值。
如果对输出结果范围有要求,用归一化。数据较为稳定,不存在极端的最大最小值,用归一化。数据存在异常值和较多噪音,用标准化,可以间接通过中心化避免异常值和极端值的影响。
归一化
from sklearn.preprocessing import MinMaxScaler
def mm():
"""
归一化处理
:return: NOne
"""
mm = MinMaxScaler(feature_range=(2,3))
data = mm.fit_transform([[90, 2, 10, 40], [60, 4, 15, 45], [75, 3, 13, 46]])
print(data)
return None
if __name__ == "__main__":
mm()
[[3. 2. 2. 2. ]
[2. 3. 3. 2.83333333]
[2.5 2.5 2.6 3. ]]
Process finished with exit code 0
标准化
from sklearn.preprocessing import StandardScaler
def stand():
"""
标准化缩放
:return:
"""
std = StandardScaler()
data = std.fit_transform([[ 1., -1., 3.],[ 2., 4., 2.],[ 4., 6., -1.]])
print(data)
return None
if __name__ == "__main__":
stand()
[[-1.06904497 -1.35873244 0.98058068]
[-0.26726124 0.33968311 0.39223227]
[ 1.33630621 1.01904933 -1.37281295]]
Process finished with exit code 0
连续特征离散化
特征离散化主要针对的是数值型特征,是一种在模型中加入非线性因素的常用手段。
离散化优势
链接:https://www.zhihu.com/question/31989952/answer/54184582
离散特征的增加和减少都很容易,易于模型的快速迭代;
稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展;
离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合;
离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力;
特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问;
特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。
常用方法
等区间
d1 = pd.cut(data,k,labels = range(k))#等宽分成k类
#平均分为5分
pd.cut(a,5)
#按照给定区间划分
pd.cut(a,bins=[0,10,20,40,60,100])
#指定labels
pd.cut(a,bins=[0,10,20,40,60,100],labels=["婴儿","青年","中年","壮年","老年"])
#返回分割后的bins
pd.cut(a,bins=[0,10,20,40,60,100],labels=["婴儿","青年","中年","壮年","老年"],retbins=True)
#返回x中的数据位于第几个bin
pd.cut(a,bins=[0,10,20,40,60,100],labels=False)
缺点:对噪点过于敏感,倾向于不均匀的把属性值分布到各个区间,导致有些区间的数值极多,而有些区间极少,严重损坏离散化之后建立的数据模型。
等频
k = 5 #设置离散之后的数据段为5
#等频率离散化
w = [1.0*i/k for i in range(k+1)]#计算百分比
w = data.describe(percentiles = w)[4:4+k+1] #计算各个百分位数
d2 = pd.cut(data, w, labels = range(k)) #将集合分组
聚类
一维聚类离散包括两个过程:通过聚类算法(K-Means算法)将连续属性值进行聚类,处理聚类之后的到的k个簇,得到每个簇对应的分类值(类似这个簇的标记)。
from sklearn.cluster import KMeans #导入kmeans
kmodel = KMeans(n_clusters = k) #确定族数
kmodel.fit(data.values.reshape(len(data),1)) #训练数据集
c = pd.DataFrame(np.sort(kmodel.cluster_centers_)) #确定中心并排序
w = c.rolling(2).mean().iloc[1:] #取移动平均值
w = [0]+list(w[0])+[data.max()] #加上最大最小值作为边界值
w = list(np.sort(w)) #再次排序
d3 = pd.cut(data,w,labels = range(k))