基于Jupyter的特征工程手册
常见的特征类型有:
数据预处理部分:介绍了如何利用 scikit-learn
处理静态的连续变量,利用 Category Encoders
处理静态的类别变量以及利用 Featuretools
处理常见的时间序列变量。
数值特征最为常见,如一些统计类特征:ctr、click_num等。稠密特征加入模型的方法
很明显,case3计算得到的ctr会更接近真实值,因为曝光基数足够大。
1. Min-Max标准化
import numpy as np
from sklearn import preprocessing
x = np.array([1.,2.,3.],[2.,0.,0.],[0.,1.,-1])
x_max_min_scaled = preprocessing.MinMaxScaler().fit_transform(x)
2. z-score标准化:
前提是特征值服从正态分布,标准化后,其转换成标准的正太分布
import numpy as np
from sklearn import preprocessing
x = np.array([1.,2.,3.],[2.,0.,0.],[0.,1.,-1])
x_scaled = preprocessing.scale(x)
3. 指数变换(log转换)
一般对数变换后特征分布更平稳,能够很好的解决随着自变量的增加,因变量方差增大的问题。另一方面,将非线性的数据通过对数变换,转换为线性数据,便于使用线性模型进行学习。
离散化连续变量可以使模型更加稳健。例如,当预测客户的购买行为时,一个已有 30 次购买行为的客户可能与一个已有 32 次购买行为的客户具有非常相似的行为。有时特征中的过精度可能是噪声,这就是为什么在 LightGBM 中,模型采用直方图算法来防止过拟合。离散连续变量有两种方法。此外,像点击量、点赞量、收藏量这类连续型统计特征,需要征映射到N个桶内,再进行one-hot编码。知识点预习p分位数
df = pd.DataFrame(np.array([[1, 1], [2, 10], [3, 100], [4, 100]]),
columns=['a', 'b'])
res = df.quantile(q=0.1, axis=0, numeric_only=True, interpolation='linear')
print(df.quantile([0.2, 0.5]))
但这种方式需要注意两个点:
factors = np.random.randn(9)
res = pd.cut(factors, bins=[-3, -2, -1, 0, 1, 2, 3])
print(pd.cut(factors, 3).value_counts()) # 计算每个分组中含有的数的数量
先对数据的等分点进行统计,然后根据每个特征等分点的情况大致确定分桶阈值,这样可以基本保证以下两个原则:
factors = np.random.randn(9)
res= pd.qcut(factors, 3, labels=["a", "b", "c"],retbins=True) # 返回每个数对应的分组,但分组名称由label指示)
cnt = pd.qcut(factors, 3).value_counts() # 计算每个分组中含有的数的数量
参数 | 说明 |
---|---|
x | ndarray 或Series |
q | integer ,指示划分的数组 |
labels | array 或bool,默认为None。当传入数组时,分组的名称由label指示;当传入False时,仅显示分组下标 |
retbins | bool,是否返回bins,默认为False。当传入True时,额外返回bins,即每个边界值 |
precision | int,精度,默认为3 |
SQL实现
ntile()
函数的作用是实现等频分箱,ntile(n) over(order by col) as bucket_numn
是指定的分箱数量。
select
col,
ntile(3) over( order by col) as group1, -- NULL默认为最小值
if(col is null, null, ntile(3) over( partition by if(col is null, 1, 0) order by col)) as group2 -- 将NULL单独为1组
from VALUES (5), (0.1), (0.15),(0.20), (0.25),(0.3),(null) AS tab(col);
补充:percent_rank() over(order by col)
:先得出每个值对应的百分位数,再根据实际需求分箱
独热编码(哑变量 dummy variable)是因为大部分算法是基于向量空间中的度量来进行计算的。使用one-hot编码,将离散特征的取值扩展到了欧式空间,离散特征的某个取值就对应欧式空间的某个点,常用的距离或相似度的计算都是在欧式空间的相似度计算,计算余弦相似性,基于的就是欧式空间。将离散型特征使用one-hot编码,会让特征之间的距离计算更加合理。离散特征进行one-hot编码后,编码后的特征,其实每一维度的特征都可以看做是连续的特征。就可以跟对连续型特征的归一化方法一样,对每一维特征进行归一化。
通过sklearn库实现
import numpy as np
import sklearn import preprocessing
one_hot_enc = preprocessing.OneHotEncoder()
one_hot_enc.fit([[1,1,2],[0,1,0],[0,1,2],[1,0,3]])
after_one_hot = one_hot_enc.transform([[0,1,3]]).toarray()
通过pandas库实现
one_hot_feature = pd.get_dummies(df[col], # 输入数据
prefix=col, # 列名前缀
prefix_sep='_') # 分隔符
# transformed_train = pd.get_dummies(train_df.drop(label_columns, axis=1), columns=categorical_columns)
# 合并至原始数据
df = df.join(one_hot_feature)
有些基于树的算法在处理变量时,并不是基于向量空间度量,数值只是个类别符号,即没有偏序关系,所以不用进行独热编码。 Tree Model不太需要one-hot编码: 对于决策树来说,one-hot的本质是增加树的深度
。
利用LabelEncoder()
将转换成连续的数值型变量。即是对不连续的数字或者文本进行编号例如:
from sklearn import preprocessing
data = ['电脑','手机','手机','手表']
enc = preprocessing.LabelEncoder()
data = enc.fit_transform(data)
print(data) #[2 0 0 1]
用户的行为兴趣特征就是多值类别特征,也就是一个用户可以有多个类别的兴趣,比如打篮球,乒乓球和跳舞等,并且不同用户的兴趣个数不一样。具体方案细节参考
features=[['话题1'],
['话题1','话题3','话题2'],
['话题2']]
对这种多值类别特征的常用处理方法总结归纳如下:
非加权法
这样对多值类别特征进行处理之后,可以把每个多值类别特征转换在同一维度空间中,这样输入到神经网络中不用为了保持输入维度一致而进行padding,使输入变稀疏,也方便和其他特征做交叉特征。