机器学习中的特征工程

目录

    • 一、特征工程目标
    • 二、特征工程内容
      • (一)异常处理
      • (二)特征标准化/归一化
      • (三)数据分桶
      • (四)缺失值处理
      • (五)特征构造
      • (六)特征筛选(特征选择)
      • (七)降维
    • 三、代码示例
      • (一)导入数据
      • (二)删除异常值
      • (三)特征构造
      • (四)特征筛选
          • 1.过滤式
          • 2.包裹式

一、特征工程目标

对于特征进行进一步分析,并对于数据进行处理。完成对于特征工程的分析,并对于数据进行一些图表或者文字总结。

特征工程的主要目的还是在于将数据转换为能更好地表示潜在问题的特征,从而提高机器学习的性能。比如,异常值处理是为了去除噪声,填补缺失值可以加入先验知识等。

二、特征工程内容

(一)异常处理

目的是减少脏数据。

  • 简单统计,如describe()的统计描述,散点图等;
  • 通过箱线图(或 3-Sigma)分析删除异常值;
  • BOX-COX 转换(处理有偏分布);
  • 长尾截断;
  • 利用模型进行离群点监测,如聚类、K近邻、One Class SVM、Islation Forest

(二)特征标准化/归一化

  • 标准化(转换为标准正太分布);
  • 归一化(转换到[0,1]区间);
  • 针对幂律分布,可以采用公式: l o g ( 1 + x 1 + m e d i a n ) log(\frac{1+x}{1+median}) log(1+median1+x)

(三)数据分桶

  • 等频分桶;
  • 等距分桶;
  • Best-KS分桶(类似利用基尼指数进行二分类);
  • 卡方分桶;

(四)缺失值处理

-不处理(针对类似XGBoost等树模型)少量样本确实时。

  • 删除(缺失数据过多)。
  • 插值补全,包括均值/中位数/中数/建模预测/多重插补/压缩感知补全/矩阵补全(R-SVD)/高维映射(One-hot)等;
  • 分箱,缺失值一个箱;

(五)特征构造

目的是增强数据表达,添加先验知识。

  • 构造统计量特征,报告计数、求和、比例、标准差等;
  • 时间特征,包括相对时间和绝对时间,节假日,双休日等;
  • 地理信息,包括分箱,分布编码等方法;
  • 非线性变换,包括 log/ 平方/ 根号等;
  • 特征组合/特征交叉

(六)特征筛选(特征选择)

  • 过滤式(filter):先对数据进行特征选择,然后再训练学习器,常见的方法有 Relief/方差选择法/相关系数法/卡方检验法/互信息法;
  • 包裹式(wrapper):直接把最终要使用的学习器的性能作为特征子集的评价准则,常见方法有LVM(Las Vegas Wrapper);
  • 嵌入式(Embedding):结合过滤式和包裹式,将特征选择宇学习器训练过程融为一体,两者在同一优化中完成,即学习器训练过程中自动进行了特征选择,常见的有lasso回归,LR+L1或决策树;

(七)降维

  • PCA/LDA/ICA;
  • 特征选择也是一种降维。

三、代码示例

(一)导入数据

import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
from operator import itemgetter
from os import getcwd
#导入数据
current_path=getcwd()
train_data =  pd.read_csv(current_path+"\\used_car_train_20200313.csv",sep=' ')
test_data = pd.read_csv(current_path+"\\used_car_testA_20200313.csv",sep=' ')
print(train_data.head(),train_data.tail())#查看数据的头和尾

(二)删除异常值

#删除异常值
def outliers_proc(data,col_name,scale=3):
    """
    用于清晰异常值,默认用box_plot(scale=3)进行清洗
    :param data: 接收pandas数据格式
    :param col_name: pandas列名
    :param scale: 尺度
    :return:
    """
    def box_plot_outliers(data_ser,box_scale):
        """
        利用箱线图去除异常值
        :param data_ser: 接收pandas.Series数据格式
        :param box_scale:箱线图的尺度,
        :return:
        """
        iqr = box_scale * (data_ser.quantile(0.75) - data_ser.quantile(0.25))
        val_low = data_ser.quantile(0.25) - iqr
        val_up = data_ser.quantile(0.75) + iqr
        rule_low = ( data_ser < val_low )
        rule_up = ( data_ser > val_up )
        return (rule_low,rule_up) , (val_low,val_up)
    data_n = data.copy( )
    data_series = data_n[col_name]
    rule,value = box_plot_outliers( data_series , box_scale=scale)
    index = np.arange(data_series.shape[0])[rule[0] | rule[1]] #改变索引,按位或
    print("Delete number is :{}".format(len(index)))
    data_n = data_n.drop( index )
    data_n.reset_index(drop=True,inplace=True)
    print("Now column number is : {}".format((data_n.shape[0])))
    index_low = np.arange(data_series.shape[0])[rule[0]]
    outliers = data_series.iloc[index_low]#提取第index_low行数据
    print("Description of data less than the lower bound is:")
    print(pd.Series(outliers).describe())
    index_up = np.arange(data_series.shape[0])[rule[1]]
    outliers = data_series.iloc[index_up]
    print("Description of data larger than upper bound is :")#高于上界的数据描述为:
    print(pd.Series(outliers).describe())

    fig,ax = plt.subplots(1,2, figsize=(10,7))#其中参数1和2分别代表子图的行数和列数,一共有 1x2 个子图像。函数返回一个figure图像和子图ax的array列表。figsize设置子图的宽度,即生成一个1行2个10*7大小的子图
    sns.boxplot(y=data[col_name],data=data,palette="Set1",ax=ax[0])
    sns.boxplot(y=data_n[col_name],data=data_n,palette="Set1",ax=ax[1])
    plt.show()
    return data_n

#删除异常数据。PS:test数据不可删
train_data = outliers_proc(train_data,"power",scale=3)

结果展示
机器学习中的特征工程_第1张图片
机器学习中的特征工程_第2张图片

(三)特征构造

train_data["train"] = 0# 训练集和测试集放在一起,方便构造特征
test_data["train"] = 1
data = pd.concat([train_data,test_data],ignore_index=True)
data["used_time"] = (pd.to_datetime(data["creatDate"],format('%Y%m%d'),errors="coerce")- pd.to_datetime(data["regDate"],format("%Y%m%d"),errors="coerce")).dt.days#使用时间:data['creatDate'] - data['regDate'];数据里有时间出错的格式,所以我们需要 errors='coerce'
print(data["used_time"].isnull().sum())

扩充
pandas.to_datetime(arg,errors ='raise',utc = None,format = None,unit = None )

参数 意义
error 三种取值,‘ignore’, ‘raise’, ‘coerce’,默认为raise。 ‘raise’,则无效的解析将引发异常;‘coerce’,那么无效解析将被设置为NaT;‘ignore’,那么无效的解析将返回输入值
utc 布尔值,默认为none。返回utc即协调世界时。
format 格式化显示时间的格式。
unit
# 从邮编中提取城市信息,相当于加入了先验知识
data["city"] = data["regionCode"].apply(lambda x:str(x)[:-3])
data = data

#计算某品牌的销售统计量,还可以计算其它特征统计量
train_gb = train_data.groupby("brand")
all_info = {}
for kind,kind_data in train_gb:
    info = {}
    kind_data =  kind_data[kind_data["price"]>0]
    info["brand_amount"] = len(kind_data)
    info["brand_price_max"] = kind_data.price.max()
    info["brand_price_median"] = kind_data.price.median()
    info["brand_price_min"] = kind_data.price.min()
    info["brand_price_sum"] = kind_data.price.sum()
    info["brand_price_std"] = kind_data.price.std()
    info["brand_price_average"] = round(kind_data.price.sum()/(len(kind_data)+1),2)#保留两位小数
    all_info[kind] = info
brand_fe = pd.DataFrame(all_info).T.reset_index().rename(columns={"index":"brand"})
data = data.merge(brand_fe,how = "left", on= "brand")

PS:详解pandas库的pd.merge函数

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
         left_index=False, right_index=False, sort=True,  suffixes=('_x', '_y'), py=True, indicator=False,
         validate=None)
参数 意义
left 拼接的左侧DataFrame对象
right 拼接的右侧DataFrame对象
on 要加入的列或索引级别名称。 必须在左侧和右侧DataFrame对象中找到。 如果未传递且left_index和right_index为False,则DataFrame中的列的交集将被推断为连接键。
left_on 左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
right_on 左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
left_index 如果为True,则使用左侧DataFrame中的索引(行标签)作为其连接键。 对于具有MultiIndex(分层)的DataFrame,级别数必须与右侧DataFrame中的连接键数相匹配。
right_index 与left_index功能相似。
how One of ‘left’, ‘right’, ‘outer’, ‘inner’. 默认inner。inner是取交集,outer取并集。比如left:[‘A’,‘B’,‘C’];right[’'A,‘C’,‘D’];inner取交集的话,left中出现的A会和right中出现的买一个A进行匹配拼接,如果没有是B,在right中没有匹配到,则会丢失。'outer’取并集,出现的A会进行一一匹配,没有同时出现的会将缺失的部分添加缺失值。
sort 按字典顺序通过连接键对结果DataFrame进行排序。 默认为True,设置为False将在很多情况下显着提高性能。
suffixes 用于重叠列的字符串后缀元组。 默认为(‘x’,’ y’)。
copy 始终从传递的DataFrame对象复制数据(默认为True),即使不需要重建索引也是如此。
indicator 将一列添加到名为_merge的输出DataFrame,其中包含有关每行源的信息。 _merge是分类类型,并且对于其合并键仅出现在“左”DataFrame中的观察值,取得值为left_only,对于其合并键仅出现在“右”DataFrame中的观察值为right_only,并且如果在两者中都找到观察点的合并键,则为left_only。

数据分桶

数据分桶原因:

  • 离散后稀疏向量内积乘法运算速度更快,计算结果也方便存储,容易扩展;
  • 离散后的特征对异常值更具鲁棒性,如 age>30 为 1 否则为 0,对于年龄为 200 的也不会对模型造成很大的干扰;
  • LR 属于广义线性模型,表达能力有限,经过离散化后,每个变量有单独的权重,这相当于引入了非线性,能够提升模型的表达能力,加大拟合;
  • 离散后特征可以进行特征交叉,提升表达能力,由 M+N 个变量编程 M*N 个变量,进一步引入非线形,提升了表达能力;
  • 特征离散后模型更稳定,如用户年龄区间,不会因为用户年龄长了一岁就变化
#数据分桶 以power为例
bin = [i*10 for i in range(31)]
data["power_bin"] = pd.cut(data["power"],bin, labels=False)  #用cut函数对于power进行分段分组,用bins来对年龄进行分段,左开右闭
print(data[["power_bin","power"]].head())

机器学习中的特征工程_第3张图片

(四)特征筛选

1.过滤式
#(一)相关性分析
print(data["power"].corr(data["price"],method="spearman"))#检查两个变量之间变化趋势的方向以及程度,值范围-1到+1,0表示两个变量不相关,正值表示正相关,负值表示负相关,值越大相关性越强。
print(data["kilometer"].corr(data["price"],method ="spearman"))
print(data["brand_amount"].corr(data["price"],method="spearman"))
print(data["brand_price_average"].corr(data["price"],method="spearman"))
print(data["brand_price_max"].corr(data["price"],method="spearman"))
print(data["brand_price_median"].corr(data["price"],method="spearman"))

机器学习中的特征工程_第4张图片
PS:
DataFrame.corr(method='pearson', min_periods=1)pandas相关系数详解。主要用于检查两个变量之间变化趋势的方向以及程度,值范围-1到+1,0表示两个变量不相关,正值表示正相关,负值表示负相关,值越大相关性越强。

参数 意义
method 可填写 'pearson’、‘kendall’、“spearman”。 pearson:Pearson相关系数来衡量两个数据集合是否在一条线上面,即针对线性数据的相关系数计算,针对非线性数据便会有误差。kendall:
min_periods 样本最少的数据量

或者用图像表示

data_numeric = data[["power","kilometer","brand_amount","brand_price_average","brand_price_max","brand_price_median"]]
correlation = data_numeric.corr()
f , ax = plt.subplots(figsize=(7,7))
plt.title('Correlation of Numeric Features with Price',y=1,size=16)
sns.heatmap(correlation,annot=True,square=True,vmax=0.8)
plt.show()

机器学习中的特征工程_第5张图片

2.包裹式
#二、包裹式
from mlxtend.feature_selection import SequentialFeatureSelector as SFS
from sklearn.linear_model import LinearRegression
sfs = SFS(LinearRegression(),
           k_features=10,
           forward=True,
           floating=False,
           scoring = 'r2',
           cv = 0)
x = data.drop(['price'], axis=1)
x = x.fillna(0)
y = data['price']
sfs.fit(x, y)
sfs.k_feature_names_

#画图,可以看到边际效益
from mlxtend.plotting import plot_sequential_feature_selection as plot_sfs
import matplotlib.pyplot as plt
fig1 = plot_sfs(sfs.get_metric_dict(), kind='std_dev')
plt.grid()
plt.show()

你可能感兴趣的:(机器学习,python,数据分析,人工智能)