DATAWHALE-数据挖掘竞赛入门-task3-特征工程

背景

在这次Datawhale的组队学习中,我们主要学习数据竞赛的相关知识,其中task3是有关于特征工程的知识。

特征工程

数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已

从上面这句流传已久的说法中,不难看出特征工程在机器学习中起到的巨大作用。下面就来简单介绍一下什么是特征工程。

根据维基百科的介绍(链接在这), 特征工程是利用某个领域的知识,通过数据挖掘技术从原始数据中提取特征的过程。这些特征可以用来提高机器学习算法的性能。特征工程可以看作是应用机器学习算法的过程。

简而言之,就是将原始数据转化为各种特征,用来作为模型的输入。由此看出,在模型相同的情况下,构建好的特征有利于模型得到更好的输出,获得更好的效果。这也体现出特征工程的重要性。

具体可以参考:细说特征工程

特征工程的常见内容

根据Datawhale学习手册中作者阿泽的介绍,常见的内容如下:

1. 异常处理

  • 通过箱型图(box plot)分析删除异常值
  • BOX-COX转换(处理有偏分布)
  • 长尾截断

2. 特征归一化/标准化

  • 标准化(近似正态可用,转换为标准正态分布)
  • 归一化(转换到 [0, 1] 区间内)
  • 针对幂律分布,可以采用公式 log(\frac{1+x}{1+median})

3. 数据分桶

  • 等频分桶
  • 等距分桶
  • Best-KS 分桶
  • 卡方分桶

4. 缺失值处理

  • 不处理 (针对类似于XGBoost等树模型)
  • 删除(缺失数据太多)
  • 插值补全(均值/ 中位数/ 众数/ 建模预测/ 多重插补/ 压缩感知补全/ 矩阵补全等)
  • 分箱,把缺失值分到一个箱

5. 特征构造

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

6. 特征筛选

  • 过滤式(filter):先对数据进行特征选择,然后再训练学习器,常见的方法有Relief/ 方差选择法/ 相关系数法/ 卡方检验法/ 互信息法
  • 包裹式(wrapper):直接把最终将要使用的学习器的性能作为特征子集的评价准则,常见方法有LVM(Las Vegas Wrapper)
  • 嵌入式(embedding):结合过滤式和包裹式,学习器训练过程中自动进行了特征选择,常见的有 lasso回归

7. 降维

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

具体代码

Notebook内容链接:Notebook代码

通过df.describe()可以查看数据的概况,但是列数过多可能会省略几列,那么如果要查看全部列的话,可以添加这一句代码

pd.set_option('display.max_columns', None)

而df.head()只能查看前5行,那么通过以下代码可以查看首尾5行(上一篇也有提到过)(train是一个DataFrame):

train.head().append(train.tail())

通过train.columns 可以查看列索引,类型为Index。再调用 tolist()可以转化为列表:

train.columns
Index(['SaleID', 'name', 'regDate', 'model', 'brand', 'bodyType', 'fuelType',
       'gearbox', 'power', 'kilometer', 'notRepairedDamage', 'regionCode',
       'seller', 'offerType', 'creatDate', 'price', 'v_0', 'v_1', 'v_2', 'v_3',
       'v_4', 'v_5', 'v_6', 'v_7', 'v_8', 'v_9', 'v_10', 'v_11', 'v_12',
       'v_13', 'v_14'],
      dtype='object')
train.columns.tolist()
['SaleID', 'name', 'regDate', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'power', 'kilometer', 
'notRepairedDamage', 'regionCode', 'seller', 'offerType', 'creatDate', 'price', 'v_0', 'v_1', 'v_2', 'v_3', 'v_4', 'v_5', 'v_6', 
'v_7', 'v_8', 'v_9', 'v_10', 'v_11', 'v_12', 'v_13', 'v_14']

阿泽大佬在手册中写了一个删除异常值的代码,其原理是利用箱型图中的异常处理机制,将3IQR以外的值删除(又称极端异常值),其中IQR为0.75分位点与0.25分位点的差值。

函数如下,我稍微加了一点注释:

# 异常值处理的函数,用箱型图去除极端异常值(在3IQR(外限)之外)
def processing_outliers(data, col_name, scale=3):
    
    def boxplot_outliers(data_Series, box_scale):
        iqr = box_scale * (data_Series.quantile(0.75) - data_Series.quantile(0.25))
        val_low = data_Series.quantile(0.25) - iqr
        val_up = data_Series.quantile(0.75) + iqr
        
        rule_low = (data_Series < val_low) # 返回的是True 或者 FALSE
        rule_up = (data_Series > val_up)
        
        return (rule_low, rule_up), (val_low, val_up)

    data_tmp = data.copy()
    data_series = data_tmp[col_name]
    rule, value = boxplot_outliers(data_series, box_scale=scale)
    
    # 骚操作,用bool序列筛选data_series的值
    index = np.arange(data_series.shape[0])[rule[0] | rule[1]]
    # index 就是那些异常值的索引,大于up 或者 小于low
    print("Total deleted number is: {} ".format(len(index)))
    # 打印删除的样本个数
    data_tmp = data_tmp.drop(index)
    data_tmp.reset_index(drop=True, inplace=True)
    print("The remaining number is: {}".format(data_tmp.shape[0]))
    # 给超出下界的样本打印一个describe
    index_low = np.arange(data_series.shape[0])[rule[0]]
    # 7 iloc loc ix https://www.cnblogs.com/smartmsl/p/11161027.html
    outliers = data_series.iloc[index_low]
    print("Description of the data below the {} IQR bound is:".format(scale))
    print(outliers.describe())
    index_up = np.arange(data_series.shape[0])[rule[1]]
    outliers = data_series.iloc[index_up]
    print("Description of data above the {} IQR bound is:".format(scale))
    print(outliers.describe())

    fig, ax = plt.subplots(1, 2, figsize=(10, 7))
    sns.boxplot(y=data[col_name], data=data, ax=ax[0])
    ax[0].set_title('before')
    sns.boxplot(y=data_tmp[col_name], data=data_tmp, ax=ax[1])
    ax[1].set_title('after')

    return data_tmp

这段代码有几个值得注意的地方:

筛选条件时,可以对Series取布尔值:

se = pd.Series([1, 2, 3, 4, 5])
a = (se > 2) # 假设规则就是只保留大于2的值
a

返回:

0    False
1    False
2     True
3     True
4     True
dtype: bool

返回的bool值使后续的索引筛选更加方便,这也是numpy数组的一个特点或者优点:

idx = np.arange(3)
d = [True, False, True] # 假设d就是那个处理后的bool序列
idx[d]
array([0, 2])

由此,我们可以根据自己的需要设置不同的规则,十分方便!

另外函数df.reset_index()(重置行索引,尤其是drop删除了几行后)里面的两个参数也值得留意,inplace参数比较好理解,为True的话意思为覆盖原实例,不创建新对象,那么drop=True则可以避免多出一个index列:

默认情况下,drop=False,多了一个原索引列:

 

 设置drop=True后,index列就没有了:

     class	max_speed
0	bird	389.0
1	bird	24.0
2	mammal	NaN

函数最后会输出处理前与处理后的箱型图图像,如图:

可见数据分布得到了优化,箱型图的“箱子”也能看到了!

总结

由于时间原因,目前这篇文章代码部分只展示了一部分成果,还待进一步更新。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值