datawhale 学习笔记——特征工程入门

拖延症又犯了,最后一天才写博客,。,不想给自己找啥理由了000。

特征工程介绍

我理解的特征工程是一种更深层的数据分析,为特定的数据做处理,深挖其中的信息,以便后续模型得到更好的效果。

本文是参考天池二手车预测比赛的教程——特征工程做的笔记,task3 中提到了很多特征处理的方法,主要介绍了其中的五种方法:

  • 异常数据处理
  • 特征构造
  • 数据分桶
  • 特征归一化
  • 特征筛选

首先,导入需要的包,并载入数据:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from operator import itemgetter

path = './data/'
Train_data = pd.read_csv(path+'used_car_train_20200313.csv', sep=' ')
Test_data = pd.read_csv(path+'used_car_testA_20200313.csv', sep=' ')

接下来就是各种特征处理的方法了。

异常数据处理

这部分教程中使用的是箱线图来处理,箱线图的理解可以参考 箱线图的理解

总的来讲,就是根据四分位线,设置一个上边界线和下边界线。上下边界线分别是由上四分位数和下四分位数加减“箱子”的高度得到的。这里的箱子高度是指特定倍数的四分位数差,教程里设置的是三倍四分位数差。

数值超出上下边界线的数据被划分为异常数据。对于异常数据,我们可以选择截断或是直接删除的方式来处理,教程中采用的是后者。

对 power 特征进行数据处理,由于其中得异常值共九百多个,数量较多,因此,截断的方式更好。我对代码做了点更改,改成了截断的方式。代码如下(默认已经导入需要的包):

def outliers_proc(data, col_name, scale=3):
    """
    用于清洗异常值,默认用 box_plot(scale=3)进行清洗
    :param data: 接收 pandas.DataFrame 数据格式
    :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  # type = <class 'pandas.core.series.Series'>
        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]]  # 取出所有为 True 的元素下标,即异常值的元素下标(第一次遇到这种操作,神奇)
    print(f"abnormal data number is: {len(index)}")
    # data_n = data_n.drop(index)  # 这里不做删除操作,做截断操作
    # data_n.reset_index(drop=True, inplace=True)  # 重置索引,删除异常值时需要将索引重置,drop=True 表示不保留以前的索引

    for i in range(2):
        row = np.arange(data_series.shape[0])[rule[i]]  # 取出异常值的下标
        data_n.loc[row, [col_name]] = value[i]
        # print(data_n.loc[row, [col_name]], value[i])
        print(f"change {'low' if i == 0 else 'high'} data number is : {len(row)}")

    ##########---------- 到这里数据就已经截断完了,下面是数据的展示  ----------##########

    print(f"Now column number is: {data_n.shape[0]}")
    index_low = np.arange(data_series.shape[0])[rule[0]]
    outliers = data_series.iloc[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 the upper bound is:")
    print(pd.Series(outliers).describe())
    
    fig, ax = plt.subplots(1, 2, figsize=(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

Train_data = outliers_proc(Train_data, 'power', scale=3)

运行结果如下:

abnormal data number is: 963
change low data number is : 0
change high data number is : 963
Now column number is: 150000
Description of data less than the lower bound is:
count 0.0
mean NaN
std NaN
min NaN
25% NaN
50% NaN
75% NaN
max NaN
Name: power, dtype: float64
Description of data larger than the upper bound is:
count 963.000000
mean 846.836968
std 1929.418081
min 376.000000
25% 400.000000
50% 436.000000
75% 514.000000
max 19312.000000
Name: power, dtype: float64

power特征箱线图的图像

可以看到,一共 963 个异常数据,而且异常类型全都是超出上边界线。

特征构造

教程里在特征构造方面,主要是新构造了一些特征,构造一些认为对模型有帮助的特征。

第一个新特征是使用时间:data[‘creatDate’] - data[‘regDate’],反应汽车使用时间,一般来说价格与使用时间成反比。这里的 creatDate 是广告发布时间;regDate 是汽车注册时间。二者相减,就是汽车的使用时间。由于时间数据有的格式解析有问题,在前天晚上的直播中,作者也解释了这个问题,因此,会有部分非法数据。一共有 15101 个非法数据,占总样本量过大,7.5%,教程里不建议删除。

第二个新特征是城市信息,作者根据邮编信息,提取出邮编内的城市信息。(直播中,作者解释了为什么知道这是德国的邮编格式)

最后就是计算某品牌的销售统计量,教程作者根据训练集中的数据信息,进行了一系列的统计计算。

特征如果使用完了,认为没有什么更多的信息了,就可以将其删除,降低一下维度。

数据分桶

数据分桶可以简单地理解成将数据离散化,其优点可以总结成如下三点:

  1. 稀疏向量,计算速度更快。
  2. 离散后的特征更稳定,鲁棒性更强,增强了对异常数据的抗干扰性。
  3. 离散化可以引入非线性,类似于神经网的激活函数。

教程中对 power 特征进行了分桶操作:

bin = [i*10 for i in range(31)]
data['power_bin'] = pd.cut(data['power'], bin, labels=False)
print(data[['power_bin', 'power']].head())

运行结果:

power_bin power
0 5.0 60
1 NaN 0
2 16.0 163
3 19.0 193
4 6.0 68

特征归一化

特征归一化还是对刚才的 power 特征做的操作,可以先不对其进行异常值截断,先查看其特征分布:

Train_data['power'].plot.hist()
plt.show()

运行结果:

power特征可视化

可以看到,特征很符合长尾分布的特点。长尾分布就是尾巴很长的分布,这种分布的尾巴处数据很少,如果数据出现不准的情况,会造成较大影响,毕竟尾巴处可参考的数据很少。

可以先对特征做 log 操作,然后再做归一化操作。

from sklearn import preprocessing
min_max_scaler = preprocessing.MinMaxScaler()
data['power'] = np.log(data['power'] + 1) 
data['power'] = ((data['power'] - np.min(data['power'])) / (np.max(data['power']) - np.min(data['power'])))
data['power'].plot.hist()
plt.show()

注意这里的 log+1 的操作,目的是使计算后的数据值都大于 0。运行结果如下:

power特征归一化

特征筛选

特征筛选有三种方法:(1)过滤式(2)包裹式(3)嵌入式

详细介绍请参考:特征选择/筛选方法总结

过滤式(Filter)

过滤式的思想就是给每个特征“打分”,相当于给特征赋予权值,权值越大,特征越重要。主要的“打分”方法有三种:

  • 信息增益
  • 卡方检验
  • 相关系数

教程中使用的是相关系数进行打分,计算每个特征与 price 的相关系数。由于负相关也是一种强相关,因此打分时,需要将相关系数取绝对值。

教程中画了相关系数的热力图,可以很直观地感受到各特征之间的相关性强弱。

包裹式(Wrapper)

包裹式的主要思想是选择不同的特征子集,对不同的特征子集组合进行打分。将特征选择的问题转换成优化问题,寻找分值较高的特征子集。使用要使用的分类器作为一个评价函数,对特征子集进行打分。

教程里使用的 mlxtend.feature_selection 下的 SequentialFeatureSelector 方法进行特征选择,其中评价函数使用 LinearRegression

嵌入式(Embedded)

在模型既定的情况下学习出对提高模型准确性最好的特征。也就是在确定模型的过程中,挑选出那些对模型的训练有重要意义的特征。

这个教程中没有给出案例,以后会有,留待下次记录。

最后

整个教程让人对特征有了一个更深的认识,通过教程,直接和间接地了解到了很多知识,很赞。有点遗憾的是,最近没有花太多时间在这上面,仅仅只是达到了一个入门的状态。不过每天都在进步的感觉还是很有成就感的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于 jQuery 的事件,我可以和你分享一些学习笔记。 1. 绑定事件 在 jQuery 中,我们可以通过以下方式来绑定事件: ``` $(selector).event(function(){ // 事件处理程序 }) ``` 其中,`selector` 表示要绑定事件的元素,`event` 表示要绑定的事件类型,比如 `click`、`mouseover` 等等。事件处理程序则是在事件触发时要执行的代码块。 2. 多个事件绑定 我们可以通过 `on()` 方法来同时绑定多个事件: ``` $(selector).on({ event1: function(){ // 事件处理程序1 }, event2: function(){ // 事件处理程序2 } }) ``` 这样,当 `event1` 或 `event2` 中任意一个事件触发时,对应的处理程序都会被执行。 3. 解除事件 如果需要解除某个元素的事件处理程序,可以使用 `off()` 方法: ``` $(selector).off(event); ``` 其中,`event` 表示要解除的事件类型。如果不指定事件类型,则会解除该元素上所有的事件处理程序。 4. 事件委托 在 jQuery 中,我们可以使用事件委托来提高性能。事件委托是指将事件绑定到父元素上,而不是绑定到子元素上,然后通过事件冒泡来判断是哪个子元素触发了该事件。这样,当子元素数量较多时,只需要绑定一次事件,就可以监听到所有子元素的事件。 ``` $(selector).on(event, childSelector, function(){ // 事件处理程序 }) ``` 其中,`selector` 表示父元素,`event` 表示要绑定的事件类型,`childSelector` 表示要委托的子元素的选择器,事件处理程序则是在子元素触发事件时要执行的代码块。 以上是 jQuery 中事件的一些基本操作,希望对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值