特征工程之入门总结

最近在做天池项目过程中,涉及到最重要的一步骤就是特征工程。本文旨在总结特征工程知识点,项目实战请移步:特征工程详解及实战项目(2)

简单介绍特征工程

特征工程是将数据转换成更好表示潜在问题的特征,提高机器学习的性能。
所谓“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已”。

目录

    1. 数据预处理
      1.1 缺失值处理
      1.11 直接删除
      1.12 填补法:统计值填补,插值法,预测值填充
      1.2 异常值处理
      1.21 箱型图判别
      1.22 基于常识
      1.3 去除唯一性
      1.4 量钢化
      1.41 归一化min-max
      1.42 标准化z_score
      1.5 连续数据离散化
      1.51 二值化:设定阈值得到1或0
      1.52 分桶:等宽法,等频法
      1.6 类别数据处理
      1.61 序号编码label
      1.62 one-hot、哑变量get_dummies
    1. 特征构造
      2.1 统计值构造:单变量、多变量构造新特征,验证相关性
      2.2 函数变化法
    1. 特征提取
      3.1 过滤法
      3.11 方差过滤法
      3.12 相关系数
      3.13 卡方检验
      3.2 嵌入法
      3.3 包装法
    1. 类别不平衡
    1. 降维
      5.1 无监督之PCA主成分分析
    1. 交叉验证

1.数据预处理

1.1缺失值处理
1.1.1直接删除

如果某字段缺失值占比较大,对训练模型没有明显特征可言,可以选择直接删除该字段(只能删除训练集,慎用)
如果缺失值较少可以删除记录,但是会把其他字段不缺失也一并删除掉,所以不建议使用

python实现:df.dropna()
1.1.2 填补
  • 统计值填补:用均值,中位数,众数等。或者可以用空值、自定义数值、向前向后填补。当特征具有强类别信息时,比如男女性别身高填补,要分类统计效果才会好
    注:由于众数可能存在多个,要考虑到这点
    注:向前后填补要考虑到第/最后一行为空值情况
df.fillna(method=' ')
  • 插值法填补:在X个范围内求出一个值填至缺失值中
def f(s,n,k=5):#s=df,n缺失值位置,k范围=5
    y = s[list(range(n-k,n+1+k))]#y求出缺失值前5后5的df数值
    y = y[y.notnull()]#确保里面没有nan
    return(lagrange(y.index,list(y))(n))#lagrange计算出第n个值

for i in range(len(df)):
    if df.isnull()[i]:
        data[i] = f(df,i)
        print(f(df,i))   
1.1.3预测值填充

a.把需要填充的某列作为新特征列Label_A
b.选出与Label_A相关性强的组成模型
c.非缺失值作为训练集,缺失值作为测试集
d.连续性数据用回归预测,离散型进行分类学习

dataset['Label_A']=dataset['A']#a
dataset.corr()#b
x_trian,y_train,x_test,y_test = train_test_split(x,y)#c,x是b中相关性较强的特征列,y是结果列
lr = LinearRegression()
lr.fit(x_train, y_train)
y_train_pred = lr.predict(x_train)
print('>>>在训练集中的表现:', r2_score(y_train_pred, y_train))
y_valid_pred = lr.predict(x_valid)
print('>>>在验证集中的表现:', r2_score(y_valid_pred, y_valid))
y_test_pred = lr.predict(test.iloc[:, :2])
test.loc[:, 'Label_petal_length'] = y_test_pred
df_no_nan = pd.concat([train, test], axis=0)
1.2异常值处理
  • 基于常识,判定为异常值,比如:年龄不能有负数
  • 箱型图判断异常值
    IQR=Q3-Q1
    极端异常值上限>Q3+3IQR
    极端异常值下限<Q1-3IQR
    一般异常值上限>Q3+1.5IQR
    一般异常值下限<Q1-1.5IQR
  • 3西格玛方法:
    如果数据符合正态分布,那么缺失值被定义为值与均值之差的绝对值大于3倍标准差

异常值处理方法一般是删除填补

1.3去除唯一属性

一般都是ID类属性不能反映样本特征情况,直接删除即可

1.4 无量纲化
  • 归一化min-max
    x* = (x-x.min)/(x.max-x.min)
    又叫区间缩放法,把值缩放至[0,1]区间
from sklearn.preprocessing import MinMaxScaler

x_scaler = MinMaxScaler()
y_scaler = MinMaxScaler()

#特征归一化
x_train_sca = x_scaler.fit_transform(X_train)
x_test_sca = x_scaler.transform(X_test)
y_train_sca = y_scaler.fit_transform(pd.DataFrame(y_train))

  • 标准化z_score
    x* = (x-x.mean)/σ
from sklearn.preprocessing import StandardScaler

#一般把train和test集放在一起做标准化,或者在train集上做标准化后,用同样的标准化器去标准化test集
scaler = StandardScaler()
train = scaler.fit_transform(train)
test = scaler.transform(test)
1.5 连续变量离散化

需要离散化的原因:

  • 算法需要,如决策树,朴素贝叶斯是基于离散型数据
  • 离散化数据较连续型更容易被理解
  • 强鲁棒性,客服数据缺陷
  • 离散化后数据稳定性强,可以进行特征交叉,对于逻辑回归来说离散后每个特征具有权重,相当于为模型引入非线性
二值化:设定阈值,大于阈值为1,反之为0
from sklearn.preprocessing import Binarizer

# 阈值自定义为 3.0
# 大于阈值映射为 1,反之为 0
b = Binarizer(threshold=3.0)  
x_binarizer = b.fit_transform(df.iloc[:, :4])
x_binarizer[:5]
分桶

*等宽:按照特征值划分pd.cut
*等频:将样本数量等频数划分pd.qcut

聚类划分

KMeans无监督学习,定义k个簇不断增加样本值改变簇的位置,直到簇不再改变。

# 聚类划分
import seaborn as sns
import numpy as np
from sklearn.cluster import KMeans

data = sns.load_dataset('iris')
X = data.iloc[:,1]
kmeans = KMeans(n_clusters=4)  # 离散为 4 等份
kmeans.fit_transform(np.array(X).reshape(-1, 1))  # 只取一个特征进行聚类离散化
1.6 类别数据处理
  • 序号编码,可以理解为打标签
from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
x_le = le.fit_transform(data['species'])
x_le
  • One-hot编码
from sklearn import preprocessing
x = np.array([[1,2,3],[3,4,6],[1,5,6]])
l = preprocessing.OneHotEncoder()
l.fit(x)
y = np.array([3,2,6])
print(l.transform(y.reshape(1,-1)).toarray())

result: [[0. 1. 1. 0. 0. 0. 1.]]

注:可能会造成特征灾害,需要用特征选择降维
LR模型一般会用到连续数据离散化,再进行one-hot编码,使其具有非线性能力
one-hot与哑变量的区别

2. 特征构造

属性分割结合是特征构造中常用的方法,可以尝试组合二个、三个不同的属性构造新的特征。如果存在时间相关性,可以得到同一属性下不同时间的特征。

2.1 统计值构造

比如:统计单个变量次数作为新特征,多变量统计值作为新特征

# 多变量
name = {'count': 'petal_width_count', 'min':'petal_width_min', 
        'max':'petal_width_max', 'mean':'petal_width_mean', 
        'std':'petal_width_std'}
newF2 = df.groupby(by=['sepal_length'])['petal_width'].agg(['count', 'min', 'max', 'mean', 'std']).rename(columns=name).reset_index()
df_newF2 = pd.merge(df, newF2, on='sepal_length', how='inner')
# 由于聚合分组之后有一些样本的 std 会存在缺失值,所以统一填充为 0
df_newF2['petal_width_std'] = df_newF2['petal_width_std'].fillna(0)
df_newF2.head()

验证相关性:

df_newF2.corr()

通过验证相关系数表可以发现,新构建的 5 个特征,除了 count、std 之外,其余 3 个特征跟目标相关性系数较高,
可以初步认为这 3 个特征是有实际意义的特征,下面进行模型训练简单验证一下

2.3 函数变化法

包括:平方(小数值-大数值)、开方(大数值-小数值)、对数(非正态-正态分布)、指数、差分(常用于时间序列)

2.31 对数法
2.32 差分法
plt.rcParams['font.sans-serif'] = ['SimHei']#避免中文乱码
plt.rcParams['axes.unicode_minus'] = False#避免中文乱码

y = np.array([1.3, 5.5, 3.3, 5.3, 3.4, 8.0, 6.6, 8.7, 6.8, 7.9])
x = np.array(list(range(y.shape[0])))
plt.figure(1,figsize = (5,3)),plt.title('原数据')
plt.plot(x,y)
#print('----log----')
y_log = np.log(y)
plt.figure(2,figsize = (5,3)),plt.title('log变换后数据')
plt.plot(x,y_log)
#print('----差分---')
def diff(data):
    DIFF = []
    DIFF.append(data[0])
    for i in range(1,data.shape[0]):# 1 次差分
        value = data[i]-data[i-1]
        DIFF.append(value)
    for i in range(1,data.shape[0]):# 2 次差分
        value = DIFF[i]-DIFF[i-1]
        DIFF.append(value)
    x = list(range(1,len(DIFF)+1))
    plt.figure(3,figsize = (5,3)),plt.title('2次差分后数据')
    plt.plot(x,DIFF)
    return DIFF
DIFF = diff(y)
image.png

从上面图像发现经过对数变换的数据明显比差分变换的效果更好,对数变换后的数据更加的平稳。

2.33 算术运算法

如featureA/featureB

2.4 连续数据离散化

见预处理部分

2.5 离散数据编码

见预处理部分

3. 特征提取

选取对模型预测重要性的特征,减少模型预测时间提高精确度。

3.1 过滤法Filter

单个特征目标特征的相关性。适用于离散型变量

3.11 方差过滤法

方差是与偏离均值的程度大小,方差越大离散程度越大,意味着该变量对模型贡献作用越明显,应该保留。

# 调用 sklearn 模块 API 接口
from sklearn.feature_selection import VarianceThreshold

vt = VarianceThreshold(0.6)#设定阈值,默认为0
x_vt = vt.fit_transform(df.iloc[:,:4])
print(vt.variances_)#查看所有方差
x_vt[:5]
3.12 相关系数
  • 特征与特征之间的相关系数
    corr<0,代表负相关,互补特征;
    corr=0,代表无相关;
    corr>0,代表正相关,替代特征;
    corr的绝对值在0.9-1之间,就可以保留其中一个特征即可。

  • 目标与特征之间的相关系数及p值
    适用于回归问题的特征选择。先检验显著性,如果显著再看相关系数。

# 调用 sklearn 模块 API 接口
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
from numpy import array

fun = lambda X, Y: tuple(map(tuple, array(list(map(lambda x: pearsonr(x, Y), X.T))).T))
sb = SelectKBest(fun, k=2)#返回k个最佳特征
x_sb = sb.fit_transform(df.iloc[:,:4], df.iloc[:, 4])
print('>>>检验统计值(相关系数):\n', sb.scores_)
print('\n>>>P值:\n', sb.pvalues_)
x_sb[:5]
3.13 卡方检验

卡方检验是比较理论频数和实际频数的吻合程度,适用于分类问题
就是统计样本的实际观测值与理论推断值之间的偏离程度,实际观测值与理论推断值之间的偏离程度就决定卡方值的大小**(对于分类问题(y离散)),卡方值越大,越不符合;卡方值越小,偏差越小,越趋于符合

# 调用 sklearn 模块 API 接口
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

skb = SelectKBest(chi2, k=2)
x_skb = skb.fit_transform(df.iloc[:,:4], df.iloc[:,4])
print('>>>检验统计值(卡方值):\n', skb.scores_)
print('\n>>>P值:\n', skb.pvalues_)
x_skb[:5]
3.2 嵌入法Embedde

嵌入法是特征和算法同时进行,让算法决定使用哪种特征。
先使用算法和模型进行训练,得到各个特征的权重系数由大到小,这个系数代表特征对模型的重要性。比如决策树和树的集成模型中的feature_importances_属性,可以列出各个特征对树的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征。
因此相比于过滤法,嵌入法会精确到模型本身。但是过滤法会通过统计常识查找范围,比如p值0.05,而嵌入法的权值系数最佳临界值需要判断

3.3 包装法Wrapper

未完待续

4. 类别不平衡

未完待续

5. 降维

5.1 无监督主成成分PCA

通过线性变换将原始数据变换为一组各维度线性无关的表示,用于提取主要特征。是一个过程结果,用于辅助模型。


image.png

如上图所示,将100个维度降至10个维度,10个维度中每个维度=特征值b1*新变量a1,而新变量a1又是通过原100个维度得来的,无法形容a1到底是什么,但却与100个维度都相关。其中b就是特征向量,将b从大到小排序,计算累计贡献率,一般选取贡献率达到85%以上的为主成分。

from sklearn.datasets import load_digits
digit = load_digits()
pca = PCA(n_components=10)
pca.fit(digit['data'])
print(pca.explained_variance_)#特征值b
print(pca.components_)#特征向量e
s = pca.explained_variance_
df = pd.DataFrame({'z':s,
                  'z_sum':s.cumsum()/s.sum()})
df['z_sum'].plot(linestyle = '--',marker = 'o')
plt.axhline(0.85)

6. 交叉验证

交叉验证就是将样本数据分位训练集和测试集,训练集用来训练模型测试集用来评估模型预测好坏。得到多组训练集和测试集,这次是训练集下次可能会成为测试集,这就是所谓的“交叉”。
一般在样本数量级少于1w条时,会用到交叉验证。当样本集较大时,会将数据集分为三份,训练集用来训练模型,验证集用来验证模型调参,把最终得到的模型再用于测试集,最终决定使用哪个模型以及对应参数。

主要介绍简单交叉验证、k折交叉验证和分层交叉验证

6.1 简单交叉验证train_test_split

随机将样本集分为训练集和测试集

from sklearn.model_selection import train_test_split
'''
(1)random_state不填或者为0时,每次都不同;其余值表示不同随机数
(2)shuffle表示是否在分割之前对数据进行洗牌(默认True)
'''
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30,random_state=42,shuffle=True)

测试集占30%训练集70%.
这种方法处理简单,缺点是没有达到交叉的思想。

6.2 k折交叉验证KFold

将数据集均分k分,其中k-1份作为训练集,1分作为测试集。重复k次后,将所得出模型准确率的均值作为当前 k 折交叉验证下模型的性能指标。

from sklearn.model_selection import KFold
kf = KFold(n_splits=2)
for train_index, test_index in kf.split(X):
    print('X_train:%s ' % X[train_index])
    print('X_test: %s ' % X[test_index])

6.3 分层交叉验证StratifiedShuffleSplit

对于非平衡数据选择分层交叉

from sklearn.model_selection import StratifiedKFold,StratifiedShuffleSplit # 分层分割

X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1,2]])
Y = np.array([0, 0, 0, 1, 1])
ss = StratifiedShuffleSplit(n_splits=5, test_size=0.5, random_state=seed) 

for train_index, test_index in ss.split(X, Y):
    print(train_index, test_index)
   
 结果:
[4 0] [1 2 3]
[1 4] [0 2 3]
[3 1] [0 2 4]
[2 3] [1 4 0]
[3 0] [4 1 2]

举个简单的例子:
n_splits分成多少组
test_size测试集占比多少
这里数据集为5行3列,重复5组
(1)test_size占比0.5,那么5组0.5=2.5,四舍五入=3,那么测试集在5行中占3行,训练集就占2行。
(2)看y值中包括3个负样本2个正样本,那么负样本占比3/5,正样本占比2/5
(3)那么训练样本2个中负样本有2
3/5=1.2约等于1个,正样本有22/5=0.8约等于1个;测试样本3个中负样本有33/5=1.8约等于2个,正样本有3*2/5=1.2约等于1个;

参考:

http://www.elecfans.com/d/816121.html连续变量离散化
https://blog.csdn.net/snowdroptulip/article/details/78770088卡方检验
https://blog.csdn.net/weixin_43172660/article/details/84340164特征选择

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容