【机器学习】特征工程一些小trick

特征工程

1.特征无量纲化

1.1归一化 (Normalization)

属性缩放到一个指定的最大和最小值(通常是1-0)之间,这可以通过sklearn.preprocessing.MinMaxScaler类实现。
常用的最小最大规范化方法(x-min(x))/(max(x)-min(x))。
在构造类对象的时候也可以直接指定最大最小值的范围:feature_range=(min, max),此时应用的公式变为:

X_std=(X-X.min(axis=0))/(X.max(axis=0)-X.min(axis=0)) 

X_scaled=X_std/(max-min)+min

作用:

  • 1、对于方差非常小的属性可以增强其稳定性。
  • 2、维持稀疏矩阵中为0的条目

1.2标准化(Standardization)

将数据按比例缩放,使之落入一个小的特定区间内,标准化后的数据可正可负,一般绝对值不会太大。
计算时对每个属性/每列分别进行
将数据按期属性(按列进行)减去其均值,并除以其方差。得到的结果是,对于每个属性/每列来说所有数据都聚集在0附近,方差为1。

使用z-score方法规范化

(x-mean(x))/std(x)

也可以使用sklearn.preprocessing.scale()函数。

1.3正则化

正则化的过程是将每个样本缩放到单位范数(每个样本的范数为1),如果后面要使用如二次型(点积)或者其它核方法计算两个样本之间的相似性这个方法会很有用。

Normalization主要思想是对每个样本计算其p-范数,然后对该样本中每个元素除以该范数,这样处理的结果是使得每个处理后样本的p-范数(l1-norm,l2-norm)等于1。

 p-范数的计算公式:||X||p=(|x1|^p+|x2|^p+...+|xn|^p)^1/p

该方法主要应用于文本分类和聚类中。例如,对于两个TF-IDF向量的l2-norm进行点积,就可以得到这两个向量的余弦相似性。
(1)可以使用preprocessing.normalize()函数对指定数据进行转换

preprocessing.normalize(X, norm='l2', axis=1, copy=True, return_norm=False)
利用normalize方法进行标准化

(2)可以使用processing.Normalizer()类实现对训练集和测试集的拟合和转换

sklearn.preprocessing.Normalizer(norm=’l2’, copy=True)
norm:可以为l1、l2或max,默认为l2
若为l1时,样本各个特征值除以各个特征值的绝对值之和
若为l2时,样本各个特征值除以各个特征值的平方之和
若为max时,样本各个特征值除以样本中特征值最大的值

2.生成交叉项

它是使用多项式的方法来进行的,如果有a,b两个特征,那么它的2次多项式为(1,a,b,a^2,ab, b^2),这个多项式的形式是使用poly的效果。

PolynomialFeatures有三个参数

degree:控制多项式的度(默认为2)

interaction_only: 默认为False,如果指定为True,那么就不会有特征自己和自己结合的项,上面的二次项中没有a^2 和 b^2。

include_bias:默认为True。如果为True的话,那么就会有上面的 1那一项。

例子1,interaction_only为默认的False时
c=[[5,10]]   #c=[[a,b]],这里要注意a的shape,如果是list形式,则将a.shape=-1,1
pl=PolynomialFeatures()
b=pl.fit_transform(c)
b
输出:array([[ 1., 5., 10., 25., 50., 100.]])   #符合(1,a,b,a^2,ab, b^2)

3.对数的数据变换

一般使用log(1+x)对数转换来修正原先的非正态数据分布。
主要的作用:

  • 取完对数之后可以缩小数据的绝对数值,方便计算;
  • 取完对数之后可以把乘法计算转换为加法计算;
  • 还有就是分布改变带来的意想不到的效果。

numpy库里对数转换的方法:

  • log:计算自然对数
  • log10:底为10的log
  • log2:底为2的log
  • log1p:底为e的log

4.处理缺失值

4.1计算缺失值个数

# 查看有多少缺失值
print(data.isnull().sum())
print('\n')
# 查看缺失值占比
print(data.isnull().sum()/len(data))

4.2删除有缺失值的数据

DataFrame.dropna(self, axis=0, how='any', thresh=None, subset=None, inplace=False)

从方法介绍可以看出,我们可以指定 axis 的值,如果是0,那就是按照行去进行空值删除,如果是1则是按照列去进行操作,默认是0。
同时,还有一个参数是how ,就是选择删除的条件,如果是 any则是如果存在一个空值,则这行(列)的数据都会被删除,如果是 all的话,只有当这行(列)全部的变量值为空才会被删除,默认的话都是any。

4.3填充缺失值

  • 用众数来填充缺失
  • 用某个特定值来填充缺失
  • 将缺失值看成其中一种值

简单的可以用fillna()

复杂的处理转换关系实例:
我们用到了TransformerMixin方法,然后自定义一个填充器来进行缺失值的填充

# 填充分类变量(基于TransformerMixin的自定义填充器,用众数填充)
from sklearn.base import TransformerMixin
class CustomCategoryzImputer(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def transform(self, df):
        X = df.copy()
        for col in self.cols:
            X[col].fillna(X[col].value_counts().index[0], inplace=True)
        return X
    
    def fit(self, *_):
        return self   
    
# 调用自定义的填充器
cci = CustomCategoryzImputer(cols=['city','boolean'])
cci.fit_transform(X)

5.通过正则提取字符串里的指定内容

import re
data['Title'] = data['Name'].map(lambda x: re.compile(", (.*?)\.").findall(x)[0])
data.head()

6.字典批量修改变量值

# 定义一个空字典来收集映射关系
title_Dict = {}
title_Dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer'))

data['Title'] = data['Title'].map(title_Dict)

7.类别型特征处理

7.1独热编码

7.1.1数值型类别变量
  • sklearn.preprocessing.LabelEncoder() 是对不连续的数字或者文本进行编号
le = LabelEncoder()
le.fit([1,5,67,100])
le.transform([1,1,100,67,5])
输出: array([0,0,3,2,1])
  • sklearn.preprocessing.OneHotEncoder() 用于将表示分类的数据扩维

OneHotEncoder的输入必须是 2-D array

from sklearn.preprocessing import OneHotEncoder

ohe = OneHotEncoder()
ohe.fit([[1],[2],[3],[4]])  #注意是两个方括号[ [ ] ].
ohe.transform([2],[3],[1],[4]).toarray()
输出:[ [0,1,0,0] , [0,0,1,0] , [1,0,0,0] ,[0,0,0,1] ]
7.1.2字符串型类别变量

OneHotEncoder无法直接对字符串型的类别变量编码,那么需要:

  • 方法一:先用 LabelEncoder() 转换成连续的数值型变量,再用 OneHotEncoder()
a = LabelEncoder().fit_transform(testdata['pet'])

OneHotEncoder( sparse=False).fit_transform(a.reshape(-1,1))# 注意: 这里把 a 用 reshape 转换成 2-D array

  • 方法二:直接用 LabelBinarizer() 进行二值化
LabelBinarizer().fit_transform(testdata['pet'])

7.2类型值且隐含大小或比较关系的类别变量

将其转换为相应的序数编码

8.分桶

下面把年龄按照我们的需求进行分组,顺便使用独热编码生成了新的字段。

# 确定阈值,写入列表
bins = [0, 12, 18, 30, 50, 70, 100]
data['Age_group'] = pd.cut(data['Age'], bins)

dummies_Age = pd.get_dummies(data['Age_group'], prefix= 'Age')
data = pd.concat([data, dummies_Age], axis=1)

9.时间窗口

比如一段时间内的总数,一段时间内的差值,一段时间内的拐点等

10.处理数据倾斜

10.1观察数据集中数据是否倾斜

我们对数据集进行分析,首先我们可以先看看特征的分布情况,看下哪些特征明显就是有数据倾斜的,然后可以找办法解决,因此,第一步就是要有办法找到这些特征

  • 首先可以通过可视化的方式,画箱体图,然后观察箱体情况,理论知识是:

在箱线图中,箱子的中间有一条线,代表了数据的中位数。箱子的上下底,分别是数据的上四分位数(Q3)和下四分位数(Q1),这意味着箱体包含了50%的数据。因此,箱子的高度在一定程度上反映了数据的波动程度。上下边缘则代表了该组数据的最大值和最小值。有时候箱子外部会有一些点,可以理解为数据中的“异常值”。而对于数据倾斜的,我们叫做“偏态”,与正态分布相对,指的是非对称分布的偏斜状态。在统计学上,众数和平均数之差可作为分配偏态的指标之一:如平均数大于众数,称为正偏态(或右偏态);相反,则称为负偏态(或左偏态)。

# 丢弃y值
all_features = train.drop(['SalePrice'], axis=1)

# 找出所有的数值型变量
numeric_dtypes = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
numeric = []
for i in all_features.columns:
    if all_features[i].dtype in numeric_dtypes:
        numeric.append(i)
        
# 对所有的数值型变量绘制箱体图
sns.set_style("white")
f, ax = plt.subplots(figsize=(8, 7))
ax.set_xscale("log")
ax = sns.boxplot(data=all_features[numeric] , orient="h", palette="Set1")
ax.xaxis.grid(False)
ax.set(ylabel="Feature names")
ax.set(xlabel="Numeric values")
ax.set(title="Numeric Distribution of Features")
sns.despine(trim=True, left=True)

可以看出有一些特征,有一些数据会偏离箱体外,因此属于数据倾斜。但是,我们从上面的可视化中虽然看出来了,但是想要选出来还是比较麻烦,所以这里引入一个偏态的概念,相对应的有一个指标skew,这个就是代表偏态的系数。

Skewness:描述数据分布形态的统计量,其描述的是某总体取值分布的对称性,简单来说就是数据的不对称程度。
偏度是三阶中心距计算出来的。

(1)Skewness = 0 ,分布形态与正态分布偏度相同。

(2)Skewness > 0 ,正偏差数值较大,为正偏或右偏。长尾巴拖在右边,数据右端有较多的极端值。

(3)Skewness < 0 ,负偏差数值较大,为负偏或左偏。长尾巴拖在左边,数据左端有较多的极端值。

(4)数值的绝对值越大,表明数据分布越不对称,偏斜程度大。

python实现

# 找出明显偏态的数值型变量
skew_features = all_features[numeric].apply(lambda x: skew(x)).sort_values(ascending=False)

high_skew = skew_features[skew_features > 0.5]
skew_index = high_skew.index

print("本数据集中有 {} 个数值型变量的 Skew > 0.5 :".format(high_skew.shape[0]))
skewness = pd.DataFrame({'Skew' :high_skew})
skew_features.head(10)

10.2修正数据倾斜

修正它的办法,可以使用 box-cox转换。

线性回归模型满足线性性、独立性、方差齐性以及正态性的同时,又不丢失信息,此种变换称之为Box—Cox变换。

Box-Cox变换是Box和Cox在1964年提出的一种广义幂变换方法,是统计建模中常用的一种数据变换,用于连续的响应变量不满足正态分布的情况。Box-Cox变换之后,可以一定程度上减小不可观测的误差和预测变量的相关性。Box-Cox变换的主要特点是引入一个参数,通过数据本身估计该参数进而确定应采取的数据变换形式,Box-Cox变换可以明显地改善数据的正态性、对称性和方差相等性,对许多实际数据都是行之有效的。—— 百度百科

# 通过 Box-Cox 转换,从而把倾斜的数据进行修正
for i in skew_index:
    all_features[i] = boxcox1p(all_features[i], boxcox_normmax(all_features[i] + 1))

11.PCA来划分数据且可视化

PCA,全称为Principal Component Analysis,也就是主成分分析方法,是一种降维算法,其功能就是把N维的特征,通过转换映射到K维上(K<N),这些由原先N维的投射后的K个正交特征,就被称为主成分。

# 导入相关库
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
%matplotlib inline

#解决中文显示问题,Mac
%matplotlib inline
from matplotlib.font_manager import FontProperties
# 设置显示的尺寸
plt.rcParams['font.family'] = ['Arial Unicode MS'] #正常显示中文

# 导入数据集
iris = load_iris()
iris_x, iris_y = iris.data, iris.target

# 实例化
pca = PCA(n_components=2)

# 训练数据
pca.fit(iris_x)
pca.transform(iris_x)[:5,]

# 自定义一个可视化的方法
label_dict = {i:k for i,k in enumerate(iris.target_names)}
def plot(x,y,title,x_label,y_label):
    ax = plt.subplot(111)
    for label,marker,color in zip(
    range(3),('^','s','o'),('blue','red','green')):
        plt.scatter(x=x[:,0].real[y == label],
                   y = x[:,1].real[y == label],
                   color = color,
                   alpha = 0.5,
                   label = label_dict[label]
                   )
        
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    
    leg = plt.legend(loc='upper right', fancybox=True)
    leg.get_frame().set_alpha(0.5)
    plt.title(title)

# 可视化
plot(iris_x, iris_y,"原始的iris数据集","sepal length(cm)","sepal width(cm)")
plt.show()

plot(pca.transform(iris_x), iris_y,"PCA转换后的头两个正交特征","PCA1","PCA2")

12.LDA来划分数据且可视化

LDA的全称为Linear Discriminant Analysis, 中文为线性判别分析,LDA是一种有监督学习的算法,和PCA不同。PCA是无监督算法,。LDA是“投影后类内方差最小,类间方差最大”,也就是将数据投影到低维度上,投影后希望每一种类别数据的投影点尽可能的接近,而不同类别的数据的类别中心之间的距离尽可能的大。

# 导入相关库
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
%matplotlib inline

#解决中文显示问题,Mac
%matplotlib inline
from matplotlib.font_manager import FontProperties
# 设置显示的尺寸
plt.rcParams['font.family'] = ['Arial Unicode MS'] #正常显示中文

# 导入数据集
iris = load_iris()
iris_x, iris_y = iris.data, iris.target

# 实例化
lda = LinearDiscriminantAnalysis(n_components=2)

# 训练数据
x_lda_iris = lda.fit_transform(iris_x, iris_y)


# 自定义一个可视化的方法
label_dict = {i:k for i,k in enumerate(iris.target_names)}
def plot(x,y,title,x_label,y_label):
    ax = plt.subplot(111)
    for label,marker,color in zip(
    range(3),('^','s','o'),('blue','red','green')):
        plt.scatter(x=x[:,0].real[y == label],
                   y = x[:,1].real[y == label],
                   color = color,
                   alpha = 0.5,
                   label = label_dict[label]
                   )
        
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    
    leg = plt.legend(loc='upper right', fancybox=True)
    leg.get_frame().set_alpha(0.5)
    plt.title(title)

# 可视化
plot(iris_x, iris_y,"原始的iris数据集","sepal length(cm)","sepal width(cm)")
plt.show()

plot(x_lda_iris, iris_y, "LDA Projection", "LDA1", "LDA2")

13.特征选取

  • 一般情况下,我们可以根据特征本身的离散程度,比如熵来计算特征权重。
  • 也可以根据特征与目标变量之间的某种定量的指标来计算特征的权重,比如Pearson相关系数,Gini-index(基尼指数),信息增益。
  • 也可以通过某些机器学习算法输出参与模型训练的特征的权重,比如决策树,随机森林算法等。
    在知道特征的权重之后,我们就可以基于权重对特征进行排名,取排名靠前或相关系数大于预设阈值的部分,从而达到特征筛选的目的。

评价函数:

  • Pearson相关系数
  • 基尼系数
  • 信息增益
  • 卡方检验
  • 距离度量

优点:计算时间上较为高效,对于过拟合问题具有高的鲁棒性。
缺点:倾向于选择冗余的特征,因为不考虑特征之间的相关性,会导致某个特征分类能力很差,但是和其他的特征组合起来效果不错,这样的特征被筛选掉了(多重共线性)。

参考文档:https://github.com/Pysamlam/Tips-of-Feature-engineering

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟知之

如果能帮助到你们,可否点个赞?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值