更多总结查看Github
只要数据好,模型随便搞。所以说来相对于机器学习和建模,准备好数据是极其重要的。那如何准备数据呢,讨论一下。
对于数据特征工程,可以这样划分:
1. 特征使用 <数据选择,可用性>
2. 特征获取 <特征来源,特征存储>
3. 特征处理 <数据清洗,特征预处理>
4. 特征监控 <现有特征,新特性>
今天说说最复杂的一步骤,特征预处理,包括以下几项:
- 特征选择
- 特征变换
- 特征降维
- 特征衍生
一. 特征选择
简单来说,就是依靠统计学方法或者模型进行与标注影响大小的排序后,移除排名靠后的特征,实现降维。或者说是剔除与标注不相关或冗余的特征。
常用的三个思想:过滤思想, 包裹思想, 嵌入思想。
import pnadas as pd
import numpy as np
import scipy.stats as ss
from sklearn.svm import SVR # 线性回归器
from sklearn.tree import DecisionTreeRegressor # 决策树回归器
from sklearn.feature_selection import SelectKBest, RFE, SelectFromModel
"""
SelectKBest是过滤思想使用的包,
RFE 是包裹思想使用的包,
SelectFromModel是嵌入思想使用的包
"""
# "D"为label,"A","B","C" 是三個特征
>>> 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(0,2,size=10)})
>>> df
>>>
A B C D
0 0.923187 0.384042 -0.261672 1
1 0.619342 -1.068809 0.279934 0
2 0.284038 1.037739 -0.574052 0
3 -0.445716 0.605456 1.297583 1
4 0.336248 0.563767 -0.482365 0
5 0.481252 -0.033403 0.091762 0
6 -1.377989 0.091305 -0.762167 0
7 -0.892782 -0.892560 1.571945 1
8 -1.210382 0.131639 -1.090856 1
9 -0.192815 0.180512 -1.328096 1
>>> X=df.loc[:, ["A","B","C"]]
>>> Y=df.loc[:, "D"]
>>> skb=SelectKBest(k=2) # 留下兩個特征
>>> skb.fit_transform(X, Y) # 拟合 转换
>>>
array([[ 0.92318731, -0.26167169],
[ 0.61934239, 0.27993381],
[ 0.28403782, -0.57405165],
[-0.44571598, 1.29758274],
[ 0.3362478 , -0.48236513],
[ 0.48125228, 0.0917617 ],
[-1.37798928, -0.76216674],
[-0.89278198, 1.57194517],
[-1.21038202, -1.09085592],
[-0.19281496, -1.32809554]])
# 可以看到经过嵌入思想的方法,把关联性差的"B"特征去掉了。
>>> rfe=RFE(estimator=SVR(kernel="linear"), n_features_to_select=2, step=1) # 同样留下两个特征
>>> rfe.fit_transform(X, Y)
>>>
array([[ 0.92318731, -0.26167169],
[ 0.61934239, 0.27993381],
[ 0.28403782, -0.57405165],
[-0.44571598, 1.29758274],
[ 0.3362478 , -0.48236513],
[ 0.48125228, 0.0917617 ],
[-1.37798928, -0.76216674],
[-0.89278198, 1.57194517],
[-1.21038202, -1.09085592],
[-0.19281496, -1.32809554]])
# 这是经过包裹思想的方法,结果和上面一样
>>> sfm=SelectFromModel(estimator=DecisionTreeRegressor(), threshold=0.01) # 指定阈值可以调节
>>> sfm.fit_transform(X, Y)
>>>
array([[ 0.92318731],
[ 0.61934239],
[ 0.28403782],
[-0.44571598],
[ 0.3362478 ],
[ 0.48125228],
[-1.37798928],
[-0.89278198],
[-1.21038202],
[-0.19281496]])
# 这是嵌入思想过滤之后
二. 特征变换
特征变换常用的方法:对指化、离散化、归一化、数值化、正规化
1. 对指化就是对数据指数化或对数化的转化,用来扩大或缩放单位数据的变化,方便统计。
numpy.log
numpy.exp
2. 离散化是将连续数据转化为离散数据的操作。比如朴素贝叶斯的算法要求离散化的数据。
常用方法有等频,等距,自因变量优化(基本上看数据拐点)
等频等距方法都属于分箱技术(等频即等深, 等距即等宽)
>>> lst=[4, 6, 8, 11, 0, 54, 34, 16, 19]
>>> lst.sort()
# 分箱前一定排序
# 等深分箱
>>> pd.qcut(lst, q=3)
>>>
[(-0.001, 7.333], (-0.001, 7.333], (-0.001, 7.333], (7.333, 17.0], (7.333, 17.0], (7.333, 17.0], (17.0, 54.0], (17.0, 54.0], (17.0, 54.0]]
Categories (3, interval[float64]): [(-0.001, 7.333] < (7.333, 17.0] < (17.0, 54.0]]
>>> pd.qcut(lst, q=3, labels=["low", "medium", "high"]) # 可以指定label
>>>
[low, low, low, medium, medium, medium, high, high, high]
Categories (3, object): [low < medium < high]
# 等宽分箱
>>> pd.cut(lst, bins=3)
>>>
[(-0.054, 18.0], (-0.054, 18.0], (-0.054, 18.0], (-0.054, 18.0], (-0.054, 18.0], (-0.054, 18.0], (18.0, 36.0], (18.0, 36.0], (36.0, 54.0]]
Categories (3, interval[float64]): [(-0.054, 18.0] < (18.0, 36.0] < (36.0, 54.0]]
>>> pd.cut(lst, bins=3, labels=["low", "medium", "high"])
>>>
[low, low, low, low, low, low, medium, medium, high]
Categories (3, object): [low < medium < high]
3. 归一化便是把数据范围缩放到 0—1之间,数据间对比更加方便。
from sklearn.preprocessing import MinMaxScaler, StandardScaler
>>> MinMaxScaler().fit_transform(np.array([1, 4, 10, 15, 21]).reshape(-1, 1))
>>>
array([[0. ],
[0.15],
[0.45],
[0.7 ],
[1. ]])
# 归一化把数组转化成了0-1之间的数
>>> StandardScaler().fit_transform(np.array([1,1,1,1,0,0,0,0]).reshape(-1, 1))
>>>
array([[ 1.],
[ 1.],
[ 1.],
[ 1.],
[-1.],
[-1.],
[-1.],
[-1.]])
# 这是另一种标准化准换,x` = (x-xi)/std(), 就是每个数变为此数与平均数的差除以方差得到的结果
4.数值化就是将非数值数据转化为数值数据。
对于某些数据,比如 "UP", "DOWN"可以转化为0,1
而对于像 "红","黄","蓝","绿"这类数据简单转化为0,1,2,3 好像不太合适了。
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
>>> LabelEncoder().fit_transform(np.array(["Down","Down","Up","Up"]).reshape(-1, 1))
>>> array([0, 0, 1, 1], dtype=int64)
# 另一种转换就麻烦点了
>>> lb_tran_f=LabelEncoder().fit_transform(np.array(["red", "yellow", "blue", "green"]))
>>> lb_tran_f
>>>
array([2, 3, 0, 1], dtype=int64) # 如果这种结果我们不想要继续下面的操作
>>> oht_encoder = OneHotEncoder().fit(lb_tran_f.reshape(-1,1))
>>> oht_encoder.transform(lb_encoder.transform(np.array(["red", "yellow","blue", "green"])).reshape(-1,1))
>>> oht_encoder.transform(lb_encoder.transform(np.array(["red", "yellow","blue", "green"])).reshape(-1,1)).toarray()
>>>
array([[0., 0., 1., 0.],
[0., 0., 0., 1.],
[1., 0., 0., 0.],
[0., 1., 0., 0.]])
# 四种颜色就转化为了向量矩阵
5. 正规化分为L1和L2两种,具体公式不多解释。
from sklearn.preprocessing import Normalizer
>>> Normalizer(norm="l1").fit_transform(np.array([[1,1,3,-1,2]]))
>>>
array([[ 0.125, 0.125, 0.375, -0.125, 0.25 ]])
>>> Normalizer(norm="l2").fit_transform(np.array([[1,1,3,-1,2]]))
>>>
array([[ 0.25, 0.25, 0.75, -0.25, 0.5 ]])
# 需要注意的是正规化是针对行,如果是列就会产生错误,例如:
>>> Normalizer(norm="l1").fit_transform(np.array([1,1,3,-1,2]).reshape(-1,1))
>>>
array([[ 1.],
[ 1.],
[ 1.],
[-1.],
[ 1.]])
三. 特征降维
降维包括主成分分析(PCA) 和 奇异值分解(SVD), 两种降维都和标注无关
而LDA降维是和标注有关的,具体理论还真一句话解释不明白,看代码吧
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# LDA降维
# Y为标注,X为特征
>>> X=np.array([[-1,-1], [-2, -1], [-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=1降到1维
>>>
array([[-1.73205081],
[-1.73205081],
[-3.46410162],
[ 1.73205081],
[ 1.73205081],
[ 3.46410162]])
>>> clf = LinearDiscriminantAnalysis(n_components=1).fit(X, Y)
>>> clf.predict([[0.8, 1]])
>>>
array([2])
# 预测分类结果为 2
# fisher_classifier分类器
四. 特征衍生
这就很好理解了,多角度衍生出新的特征。方法有: 数据加减乘除、求导和高阶求导、人工归纳。
总结:数据的路还很长....