特征分析

导入数据:

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

%matplotlib inline
#显示所有列
pd.set_option('display.max_columns', None)
#显示所有行
pd.set_option('display.max_rows', None)
#设置value的显示长度为100,默认为50
pd.set_option('max_colwidth',100)

train = pd.read_csv('data/used_car_train_20200313.csv', sep=' ')
test = pd.read_csv('data/used_car_testA_20200313.csv', sep=' ')

print(train.shape)
print(test.shape)

train.head()

train.columns
test.columns

删除异常值:
定义一个删除异常值的函数

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

删除power中的异常值:

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

特征构建

特征可以分为三个部分:日期特征、类别特征、数值特征。
使用时间:data[‘creatDate’] - data[‘regDate’],反应汽车使用时间,一般来说价格与使用时间成反比,不过数据里有时间出错的格式,所以我们需要 errors=‘coerce’

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

有15k个样本的数据有问题,可以删除但不建议删除,毕竟占了总数将近十分之一。
从邮编中提取城市信息,因为是德国的数据,所以参考德国的邮编。

data['city'] = data['regionCode'].apply(lambda x : str(x)[:-3])

计算某品牌的销售统计量还有其他特征的统计量,这里要以 train 的数据计算统计量:

train_gb = train.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')
print(all_info)

这边的结果提取一小部分:
brand:

{0: {'brand_amount': 31429, 'brand_price_max': 68500, 'brand_price_median': 3199.0, 'brand_price_min': 13, 'brand_price_sum': 173719698, 'brand_price_std': 6261.371627193883, 'brand_price_average': 5527.19},
 1: {'brand_amount': 13656, 'brand_price_max': 84000, 'brand_price_median': 6399.0, 'brand_price_min': 15, 'brand_price_sum': 124044603, 'brand_price_std': 8988.865406006838, 'brand_price_average': 9082.86},
 2: {'brand_amount': 318, 'brand_price_max': 55800, 'brand_price_median': 7500.0, 'brand_price_min': 35, 'brand_price_sum': 3766241, 'brand_price_std': 10576.224443852676, 'brand_price_average': 11806.4},

model:

{0.0: {'model_amount': 11743, 'model_price_max': 40000, 'model_price_median': 2900.0, 'model_price_min': 13, 'model_price_sum': 60634760, 'model_price_std': 5737.838198390163, 'model_price_average': 5163.04}, 
1.0: {'model_amount': 5926, 'model_price_max': 99999, 'model_price_median': 1650.0, 'model_price_min': 11, 'model_price_sum': 22521045, 'model_price_std': 5939.746711764458, 'model_price_average': 3799.74}, 
2.0: {'model_amount': 283, 'model_price_max': 55800, 'model_price_median': 6390.0, 'model_price_min': 150, 'model_price_sum': 2549787, 'model_price_std': 9036.969110088265, 'model_price_average': 8978.12},

数据分桶
分区提供了一个隔离数据和优化查询的便利方式,不过并非所有的数据都可形成合理的分区,尤其是需要确定合适大小的分区划分方式,(不合理的数据分区划分方式可能导致有的分区数据过多,而某些分区没有什么数据的尴尬情况)试试分桶是将数据集分解为更容易管理的若干部分的另一种技术。

以 power 为例,这时候我们的缺失值也进桶了,为什么要做数据分桶呢,原因有很多:

1.离散后稀疏向量内积乘法运算速度更快,计算结果也方便存储,容易扩展;
2.离散后的特征对异常值更具鲁棒性,如 age>30 为 1 否则为 0,对于年龄为 200 的也不会对模型造成很大的干扰;
3.LR 属于广义线性模型,表达能力有限,经过离散化后,每个变量有单独的权重,这相当于引入了非线性,能够提升模型的表达能力,加大拟合;
4.离散后特征可以进行特征交叉,提升表达能力,由 M+N 个变量编程 M*N 个变量,进一步引入非线形,提升了表达能力;
5.特征离散后模型更稳定,如用户年龄区间,不会因为用户年龄长了一岁就变化
当然还有很多原因,LightGBM 在改进 XGBoost 时就增加了数据分桶,增强了模型的泛化性。

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

利用好了就可以删除原始数据了:

data = data.drop(['creatDate', 'regDate', 'regionCode'], axis=1)
print(data.shape)
data.columns
(199037, 53)
27
Index(['SaleID', 'name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox',
       'power', 'kilometer', 'notRepairedDamage', 'seller', 'offerType',
       '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', 'train', 'used_time',
       'city', 'brand_amount_x', 'brand_price_max_x', 'brand_price_median_x',
       'brand_price_min_x', 'brand_price_sum_x', 'brand_price_std_x',
       'brand_price_average_x', 'brand_amount_y', 'brand_price_max_y',
       'brand_price_median_y', 'brand_price_min_y', 'brand_price_sum_y',
       'brand_price_std_y', 'brand_price_average_y', 'model_amount',
       'model_price_max', 'model_price_median', 'model_price_min',
       'model_price_sum', 'model_price_std', 'model_price_average',
       'power_bin'],
      dtype='object')

目前的数据其实已经可以给树模型使用了,所以我们导出一下

data.to_csv('data_for_tree.csv', index=0)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值