数据挖掘入门基础——特征工程

发布于 2023-08-01  106 次阅读


本文参考《零基础入门数据挖掘 - 二手车交易价格预测》部分内容和代码,数据集为自建数据集。读者可使用原文数据集进行阅读试验。

常见特征工程

异常处理

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

特征归一化/标准化

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

数据分桶

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

缺失值处理:

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

特征构造

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

特征筛选

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

降维

  • PCA/LDA/ICA;
  • 特征选择

部分特征工程实现代码

通过箱线图删除异常值

在箱型图中,数据被从大到小按顺序排列,图中出现的元素代表含义如下:

  • 上四分位数Q3:75%分位点所对应的值
  • 中位数Q2:50%分位点对应的值
  • 下四分位数Q1:25%分位点所对应的值
  • 上边缘(须):Q3+1.5(Q3-Q1)
  • 下边缘(须):Q1+1.5(Q3-Q1)

典型箱线图

那么,在箱线图中,位于合理范围的x值为:

$$Q1-1.5(Q3-Q1)\leqslant x \leqslant Q3+1.5(Q3-Q1)$$

和使用3σ准则剔除异常值相比,箱线图不需要数据服从正态分布,能真实直观的表现数据形状;箱线图以四分位数和四分位距作为判断异常值的标准,四分位数具有一定的耐抗性,多达25%的数据可以变得任意远而不会很大地扰动四分位数,使得异常值无法对数据形状造成巨大影响,因此箱形图识别异常值的结果比较客观。

下面给出使用箱线图进行异常值剔除的代码块:

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]
    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])
    return data_n