ML–数据表达与特征工程
主要涉及的知识点有:
- 使用哑变量对类型特征进行转化
- 对数据进行装箱处理
- 几种常用的数据"升维"方法
- 常用的自动特征选择方法
一.数据表达
1.使用哑变量转化类型特征
哑变量(Dummy Variables)
,也被称为虚拟变量,是一种在统计学和经济领域非常常用的,用来把某些类型变量转化为二值变量的方法,在回归分析中的使用尤其广泛
# 导入pandas
import pandas as pd
# 手工输入一个数据表
fruits=pd.DataFrame({'数值特征':[5,6,7,8,9],
'类型特征':['西瓜','香蕉','橘子','苹果','葡萄']
})
# 显示
display(fruits)
数值特征 | 类型特征 | |
---|---|---|
0 | 5 | 西瓜 |
1 | 6 | 香蕉 |
2 | 7 | 橘子 |
3 | 8 | 苹果 |
4 | 9 | 葡萄 |
下面我们使用get_dummies
来将类型特征转化为只有0和1的二值数值特征,输入代码如下
# 转化数据表中的字符串为数值
fruits_dum=pd.get_dummies(fruits)
# 显示转化后的数据表
display(fruits_dum)
数值特征 | 类型特征_橘子 | 类型特征_苹果 | 类型特征_葡萄 | 类型特征_西瓜 | 类型特征_香蕉 | |
---|---|---|---|---|---|---|
0 | 5 | 0 | 0 | 0 | 1 | 0 |
1 | 6 | 0 | 0 | 0 | 0 | 1 |
2 | 7 | 1 | 0 | 0 | 0 | 0 |
3 | 8 | 0 | 1 | 0 | 0 | 0 |
4 | 9 | 0 | 0 | 1 | 0 | 0 |
假如我们就是希望把数值特征也进行get_dummies
转换怎么办?没问题,我们可以先将数值特征转换为字符串,然后通过get_dummies
的columns
参数来转换
# 令程序将数值也看作字符串
fruits['数值特征']=fruits['数值特征'].astype(str)
# 再用get_dummies转化字符串
fruits_dum2=pd.get_dummies(fruits,columns=['数值特征'])
display(fruits_dum2)
类型特征 | 数值特征_5 | 数值特征_6 | 数值特征_7 | 数值特征_8 | 数值特征_9 | |
---|---|---|---|---|---|---|
0 | 西瓜 | 1 | 0 | 0 | 0 | 0 |
1 | 香蕉 | 0 | 1 | 0 | 0 | 0 |
2 | 橘子 | 0 | 0 | 1 | 0 | 0 |
3 | 苹果 | 0 | 0 | 0 | 1 | 0 |
4 | 葡萄 | 0 | 0 | 0 | 0 | 1 |
注意 实际上,如果我们不用fruits[‘数值特征’]=fruits[‘数值特征’].astype(str)这行代码把数值转换为字符串类型,依然会得到同样的结果。但是在大规模数据集中,还是建议大家进行转换字符串的操作,避免产生不可预料的错误
2.对数据进行装箱处理
首先我们生成许多随机数点在图像中
# 导入numpy
import numpy as np
# 导入画图工具
import matplotlib.pyplot as plt
# 生成随机数列
rnd=np.random.RandomState(38)
x=rnd.uniform(-5,5,size=50)
# 向数据中添加噪声
y_no_noise=(np.cos(6*x)+x)
X=x.reshape(-1,1)
y=(y_no_noise+rnd.normal(size=len(x)))/2
plt.plot(X,y,'o',c='r')
plt.show()
下面我们分别用MLP
和KNN
算法对这个数据集进行回归分析
# 导入神经网络
from sklearn.neural_network import MLPRegressor
# 导入KNN
from sklearn.neighbors import KNeighborsRegressor
# 生成一个等差数列
line=np.linspace(-5,5,1000,endpoint=False).reshape(-1,1)
# 分别用两种算法拟合数据
mlpr=MLPRegressor().fit(X,y)
knr=KNeighborsRegressor().fit(X,y)
# 绘制图形
plt.plot(line,mlpr.predict(line),label='MLP')
plt.plot(line,knr.predict(line),label='KNN')
plt.plot(X,y,'o',c='r')
plt.legend(loc='best')
plt.show()
[结果分析] MLP
产生的回归线非常接近线性模型的结果,而KNN
则相对更复杂一些,它视图覆盖更多的数据点
接下来我们对数据进行一下"装箱处理(binning)",这种处理方法也称为"离散化处理(discretization)"
# 设置箱体数为11
bins=np.linspace(-5,5,11)
# 将数据进行装箱操作
target_bin=np.digitize(X,bins=bins)
# 打印装箱数据范围
print('装箱数据范围:
{}'.format(bins))
# 打印前十个数据的特征值
print('
前十个数据点的特征值:
{}'.format(X[:10]))
# 找到它们所在的箱子
print('
前十个数据点所在的箱子:
{}'.format(target_bin[:10]))
装箱数据范围:
[-5. -4. -3. -2. -1. 0. 1. 2. 3. 4. 5.]
前十个数据点的特征值:
[[-1.1522688 ]
[ 3.59707847]
[ 4.44199636]
[ 2.02824894]
[ 1.33634097]
[ 1.05961282]
[-2.99873157]
[-1.12612112]
[-2.41016836]
[-4.25392719]]
前十个数据点所在的箱子:
[[ 4]
[ 9]
[10]
[ 8]
[ 7]
[ 7]
[ 3]
[ 4]
[ 3]
[ 1]]
由于我们在生成这个实验数据集的时候,是在-5到5之间随机生成了50个数据点,因此我们在生成"箱子"(如果觉得这么叫有点土的话,也可以叫它"容器")的时候,也指定范围是从-5到5之间,生成11个元素的等差数列,这样每两个数值之间就形成一个箱子,一共10个
[结果分析] 从结果中可以看到,第一个箱子是-5到-4之间,第二个箱子是-4到-3之间,以此类推。第1个数据点-1.1522688所在的箱子是第4个,第2个数据点3.59707847所在的箱子是第9个,以此类推
接下来我们要做的事情,就是用新的方法来表达已经装箱的数据,所要用到的方法就是scikit-learn
的独热编码OneHotEncoder
。OneHotEncoder
和pandas
的get_dummies
功能基本上是一样的,但是OneHotEncoder
目前只能用于整型数值的类型变量
# 导入独热编码
from sklearn.preprocessing import OneHotEncoder
onehot=OneHotEncoder(sparse=False)
onehot.fit(target_bin)
# 使用独热编码转化数据
X_in_bin=onehot.transform(target_bin)
print('装箱后的数据形态:{}'.format(X_in_bin.shape))
print('
装箱后的前十个数据点:{}'.format(X_in_bin[:10]))
装箱后的数据形态:(50, 10)
装箱后的前十个数据点:[[ 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[ 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
[ 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[ 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
E:Anacondaenvsmytensorflowlibsite-packagessklearnpreprocessing\_encoders.py:368: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values.
If you want the future behaviour and silence this warning, you can specify "categories='auto'".
In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly.
warnings.warn(msg, FutureWarning)
[结果分析] 虽然数据集中样本的数量仍然是50个,但特征数变成了10个。这是因为我们生成的箱子是10个,而新的数据点的特征是用其所在的箱子号码来表示的
我们用MLP
和KNN
算法重新进行回归分析
# 使用独热编码进行数据表达
new_line=onehot.transform(np.digitize(line,bins=bins))
# 使用新的数据来训练模型
new_mlpr=MLPRegressor().fit(X_in_bin,y)
new_knr=KNeighborsRegressor().fit(X_in_bin,y)
# 绘制图形
plt.plot(line,new_mlpr.predict(new_line),label='New MLP')
plt.plot(line,new_knr.predict(new_line),label='New KNN')
plt.plot(X,y,'o',c='r')
plt.legend(loc='best')
plt.show()
注意 这种对于样本数据进行装箱的操作对于基于决策树的算法(如随机森林,梯度上升决策树,当然也包括决策树本身)没有太多的作用,因为这算法本身就是不停在拆分样本的特征数据,所以不需要再使用装箱操作
二.数据"升维"
1.向数据集添加交互式特征
介绍两种在统计建模中常用的方法–交互式特征(Interaction Features)
和多项式特征(Polynomial Features)
# 手工生成两个数组
array_1=[1,2,3,4,5]
array_2=[6,7,8,9,0]
# 使用hstack将两个数组进行堆叠
array_3=np.hstack((array_1,array_2))
print('将数组2添加到数据1中后得到:{}'.format(array_3))
将数组2添加到数据1中后得到:[1 2 3 4 5 6 7 8 9 0]
接下来我们继续用之前的数据集来进行实验,看对特征进行交互式操作会对模型产生什么样的影响
# 将原始数据和装箱后的数据进行堆叠
X_stack=np.hstack([X,X_in_bin])
print(X_stack.shape)
(50, 11)
# 将数据进行堆叠
line_stack=np.hstack([line,new_line])
# 重新训练模型
mlpr_interact=MLPRegressor().fit(X_stack,y)
plt.plot(line,mlpr_interact.predict(line_stack),label='MLP for interaction')
plt.ylim(-4,4)
for vline in bins:
plt.plot([vline,vline],[-5,5],':',c='k')
plt.legend(loc='lower right')
plt.plot(X,y,'o',c='r')
plt.show()
# 使用新的堆叠方式处理数据
X_multi=np.hstack([X_in_bin,X*X_in_bin])
print(X_multi.shape)
print(X_multi[0])
(50, 20)
[ 0. 0. 0. 1. 0. 0. 0. 0.
0. 0. -0. -0. -0. -1.1522688 -0. -0.
-0. -0. -0. -0. ]
# 重新训练模型
mlpr_multi=MLPRegressor().fit(X_multi,y)
line_multi=np.hstack([new_line,line*new_line])
plt.plot(line,mlpr_multi.predict(line_multi),label='MLP Regressor')
plt.ylim(-4,4)
for vline in bins:
plt.plot([vline,vline],[-5,5],':',c='gray')
plt.legend(loc='lower right')
plt.plot(X,y,'o',c='r')
plt.show()
2.向数据集添加多项式特征
# 导入多项式特征工具
from sklearn.preprocessing import PolynomialFeatures
# 向数据添加多项式特征
poly=PolynomialFeatures(degree=20,include_bias=False)
X_poly=poly.fit_transform(X)
print(X_poly.shape)
(50, 20)
在这段代码中,首先我们指定了PolynomialFeatures
的degree
参数为20,这样可以生成20个特征。include_bias
设定为False
,如果设定为True
的话,PolynomialFeatures
只会为数据集添加数值为1的特征
print('原始数据集中的第一个样本特征:
{}'.format(X[0]))
print('
处理后的数据集中的第一个样本特征:
{}'.format(X_poly[0]))
原始数据集中的第一个样本特征:
[-1.1522688]
处理后的数据集中的第一个样本特征:
[ -1.1522688 1.3277234 -1.52989425 1.76284942 -2.0312764
2.34057643 -2.6969732 3.10763809 -3.58083443 4.1260838
-4.75435765 5.47829801 -6.3124719 7.27366446 -8.38121665
9.65741449 -11.12793745 12.82237519 -14.77482293 17.02456756]
# 打印多项式特征处理的方式
print('PolynomialFeatures对原始数据的处理:
{}'.format(poly.get_feature_names()))
PolynomialFeatures对原始数据的处理:
['x0', 'x0^2', 'x0^3', 'x0^4', 'x0^5', 'x0^6', 'x0^7', 'x0^8', 'x0^9', 'x0^10', 'x0^11', 'x0^12', 'x0^13', 'x0^14', 'x0^15', 'x0^16', 'x0^17', 'x0^18', 'x0^19', 'x0^20']
# 导入线性回归
from sklearn.linear_model import LinearRegression
# 使用处理后的数据训练线性回归模型
LNR_poly=LinearRegression().fit(X_poly,y)
line_poly=poly.transform(line)
plt.plot(line,LNR_poly.predict(line_poly),label='Linear Regressor')
plt.xlim(np.min(X)-0.5,np.max(X)+0.5)
plt.xlim(np.min(y)-0.5,np.max(y)+0.5)
plt.plot(X,y,'o',c='r')
plt.legend(loc='lower right')
plt.show()