Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

天天向上小队-天天,task2,EDA学习笔记

目录

数据处理总结

  • 缺失值处理

    • 该数据集缺失的都是类别特征里的,且部分类别特征与某些匿名变量线性相关性强
    • 考虑填充新的值,比如-1
    • 填充众数、平均数(需要取整),knn邻近(速度慢)
  • 异常值处理

    • 识别:
      • 箱型图识别
      • 3σ识别
    • 处理:
      • 边界值替换
      • 映射到新维度μ,μ(正常值)=0,μ(异常值)= function(异常值)
      • 不处理,与原数据一起归一化|标准化
      • 分桶法(分箱法),单正常值要一起处理
  • 特征选择:

    • PCA
    • 相关性分析,剔除相关性高的类别,仅保留其中一类或少数类
    • 通过添加噪声体现特征重要性
    • 使用一些基于树的模型训练,可得到参数重要性
  • 特征构造:

    • 构造统计量特征
      • 计数
      • 求和
      • 比例
      • 标准差
      • 上述计量特征的组合
    • 时间特征
      • 绝对时间
      • 时间差
      • 特殊时间:春节、国庆节等等节假日,是否会对价格造成影响,因为商家可能进行促销等等
    • 地理信息
      • 分箱
      • 分布编码?
      • 高频统计,取高频
      • 简单的标准化|归一化,暂时没有想到更好的方法了
    • 非线性变换,包括 log/ 平方/ 根号等
    • 多项式组合
    • 根据已有二手车测评指标以及现有数据,构造有理论支撑的新特征
  • 24个匿名变量:

    • 特性:
      • 24个匿名变量都没有缺失值
      • 部分匿名变量与一些非匿名变量强线性相关,存在冗余
        • 可以考虑删除强线相关的非匿名变量,比如notRepairedDamage,因为它有缺失值
        • 对匿名变量二值化|分桶编码,据此填充相关类别特征的缺失值
      • 部分匿名变量之间强线性相关,存在冗余
      • 二手车交易价格预测在阿里天池上有两场:零基础入门数据挖掘(以下简称0基础)、以及河北高校邀请赛(以下简称邀请赛),两者的数据集所含非匿名特征一致,不同的是邀请赛的匿名变量更多。并且,在两个比赛的排行榜上,0基础B榜第一MAE>300 ,邀请赛(4月16日)A榜第一MAE=148,且top选手的MAE基本上都是邀请赛的更小,而且小得多,这说明了什么:
        • 匿名变量的处理本次数据挖掘比赛是关键之一,这里的处理强调特征构造

数据探索性分析

读取数据

# train_path 本地训练集绝对路径
import pandas
import matplotlib.pyplot as plt
train_csv = pd.read_csv(train_path,sep=  ' ')

查看训练集列属性:

index_se = pd.Series([i for i in range(len(train_csv['SaleID']))])
train_csv.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', 'v_15', 'v_16', 'v_17', 'v_18', 'v_19', 'v_20', 'v_21',
       'v_22', 'v_23'],
      dtype='object')
'''

数据分布可视化

ps:由于某些特征范围过于广泛,我仅可视化了这些特征的高频部分

name:汽车交易名称,已脱敏

name = pd.DataFrame(train_csv['name'].value_counts())
print('该类别种类数',len(set(train_csv['name'])))
name.sort_values(by='name',ascending=False,inplace=True)
name.head(40).plot.bar()
plt.show()
# 输出:该类别种类数:164312

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

model:车型编码,已脱敏

model = pd.DataFrame(train_csv['model'].value_counts())
print('该类别种类数',len(set(train_csv['model'])))
model.sort_values(by='model',ascending=False,inplace=True)
model.head(40).plot.bar()
plt.show()
# 输出:该类别种类数:251

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

brand:汽车品牌,已脱敏

brand = pd.DataFrame(train_csv['brand'].value_counts())
print('该类别种类数',len(set(train_csv['brand'])))
brand.sort_values(by='brand',ascending=False,inplace=True)
brand.head(20).plot.bar()
plt.show()
# 输出: 该类别种类数:40

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

bodyType:车身类型

豪华轿车:0,微型车:1,厢型车:2,大巴车:3,敞篷车:4,双门汽车:5,商务车:6,搅拌车:7

bodyType = pd.DataFrame(train_csv['bodyType'].value_counts())
bodyType.sort_values(by='bodyType',ascending=False,inplace=True)
bodyType.head(20).plot.bar()
plt.show()

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

fuelType:燃油类型

汽油:0,柴油:1,液化石油气:2,天然气:3,混合动力:4,其他:5,电动:6

fuelType = pd.DataFrame(train_csv['fuelType'].value_counts())
fuelType.sort_values(by='fuelType',ascending=False,inplace=True)
fuelType.head(20).plot.bar()
plt.show()
print(train_csv['fuelType'].value_counts())
'''
# 输出:
0.0    150664
5.0     72494
4.0      3577
3.0       385
2.0       183
1.0       147
6.0        60
Name: fuelType, dtype: int64
'''

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

gearbox:变速箱

手动:0,自动:1

gearbox = pd.DataFrame(train_csv['gearbox'].value_counts())
gearbox.sort_values(by='gearbox',ascending=False,inplace=True)
gearbox.head(20).plot.bar()
plt.show()

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

power:发动机功率

有很多车子的power=0,意思是车子报废了?还是没有这个数据,拿0填充的?这是我的疑问之一。

power = pd.DataFrame(train_csv['power'].value_counts())
print('该类别范围',train_csv['power'].min(),'~',train_csv['power'].max())
power.sort_values(by='power',ascending=False,inplace=True)
power.head(20).plot.bar()
plt.show()
# 输出:该类别范围: 0 ~ 20000

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

kilometers:汽车已行驶公里数

kilometer = pd.DataFrame(train_csv['kilometer'].value_counts())
print('该类别范围',train_csv['kilometer'].min(),train_csv['kilometer'].max())
kilometer.sort_values(by='kilometer',ascending=False,inplace=True)
kilometer.head(20).plot.bar()
plt.show()
# 输出: 该类别范围0.5 ~ 15.0

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

kilometers,肉眼可见的长尾分布

notRepairedDamage:汽车有尚未修复的损坏

汽车有尚未修复的损坏:是:0,否:1

notRepairedDamage = pd.DataFrame(train_csv['notRepairedDamage'].value_counts())
notRepairedDamage.sort_values(by='notRepairedDamage',ascending=False,inplace=True)
notRepairedDamage.head(20).plot.bar()
plt.show()

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

regDate:注册日期

注意:这里的regDate和createDate这两个日期属性中,都包含一类错误时间格式数据,比如 19700003,实际上是1970年3月的一天,缺失的是具体的天数。这可能与数据来源的平台处理有关。我将错误格式的日期放在一起,发现它们某两位的取值范围是1~12,据此认为这是月份。

regDate = train_csv['regDate']
for i in range(len(regDate)):
    item = str(regDate.loc[i])
    if item[4:6] == '00':
        item = item[:4] + item[-2:] + '15'
        regDate.loc[i] = int(item)
regDate = pd.to_datetime(regDate,format='%Y%m%d')
print('该离散特征种类数',len(set(regDate)))
print('该离散特征范围',regDate.min(),regDate.max())
regDate = pd.DataFrame(regDate.value_counts())
regDate.sort_values(by='regDate',ascending=False,inplace=True)
regDate.head(30).plot.bar()
plt.show()
'''
输出:
该离散特征种类数 7537
该离散特征范围 1910-01-03 00:00:00 2019-12-12 00:00:00
'''

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
regDate的分布就有点讨人厌了,范围1910年到2019年,先做个箱型图看看

regDate.plot(kind='box')
plt.show()

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
这是我很讨厌的特征分布,如果按箱型图识别异常值(这里指的是相对于特征分布中心偏离很大的值),那么非常多的数据会被认为是异常值。然而将过多数据按异常值处理了,有可能“损坏”了数据本身蕴含的信息。目前我尚未发现|找到什么好的处理方法,只能做一般的标准化|归一化了。

creatDate:汽车上线时间,即开始售卖时间

对于二手车而言,createDate - rDate 就是这辆车的已使用时间

creatDate = train_csv['creatDate']
for i in range(len(creatDate)):
    item = str(creatDate.loc[i])
    if item[4:6] == '00':
        item = item[:4] + item[-2:] + '15'
        creatDate.loc[i] = int(item)
creatDate = pd.to_datetime(creatDate,format='%Y%m%d')
print('该离散特征种类数',len(set(creatDate)))
print('该离散特征范围',creatDate.min(),creatDate.max())
creatDate = pd.DataFrame(creatDate.value_counts())
creatDate.sort_values(by='creatDate',ascending=False,inplace=True)
creatDate.head(30).plot.bar()
plt.show()
'''
输出:
该离散特征种类数 107
该离散特征范围 2014-03-10 00:00:00 2016-04-07 00:00:00
'''

ps:这里是截图,plt.saveflg()存的图片没有正常显示x轴上的时间
可以看到creatDate的范围相对小得多。
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
很少的日期在2014年,大部分数据在2016年,还有一部分在2015年,虽然箱型图看起来挺别扭,但是这个分布比regDate好得多,一般的的标准化|归一化还是可以接受的
另外,汽车已使用时间usedtime = creatDate - regDate,不了解二手车交易的小伙伴可以自行百度

regionCode:地区编码,已脱敏

regionCode = pd.DataFrame(train_csv['regionCode'].value_counts())
print('该类别种类数',len(set(train_csv['regionCode'])))
regionCode.sort_values(by='regionCode',ascending=False,inplace=True)
regionCode.head(30).plot.bar()
plt.show()
# 输出: 该类别种类数:8081

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

price:汽车交易价格

price = pd.DataFrame(train_csv['price'].value_counts())
print('该连续变量取值可能种类数',len(set(train_csv['price'])))
print('该离散特征范围',train_csv['price'].min(),train_csv['price'].max())
price.sort_values(by='price',ascending=False,inplace=True)
price.head(20).plot.bar()
plt.show()
'''
输出:
该连续变量取值可能种类数 4585
该离散特征范围 0 100000
'''

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

v_0 ~ v_23 :匿名特征

处理好匿名变量,包括匿名变量本身的数据处理,以及从匿名变量发掘出新特征,可能会提高模型效果。这是我的主观看法,做过主成分分析后,还会发现,一些匿名变量与几个非匿名变量之间具有很强的相关性,比如notRepairedDamage 与一个匿名变量相关系数大于0.9,而notRepairedDamage这个属性中有不少缺失值,据此,我们可以直接删除notRepairedDamage这一个属性,因为冗余了,或者对那个匿名变量二值化,代替notRepairedDamage。

v_col = ['v_'+ str(i) for i in range(24)]
v_ = pd.DataFrame(train_csv.get(v_col))
v_des = pd.DataFrame(v_.describe())
v_t = v_des.T
for col in v_col:
    v_t.loc[col][1:].plot.bar()
    plt.show()
  • v_0
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
  • v_1

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_2

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_3
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_4

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_5
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_6

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_7

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_8

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_9
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_10

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_11
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_12
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_13
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_14
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_15
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_16
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_17
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_18
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_19
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_20
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_21
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_22
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

  • v_23
    Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

特征与回归目标price的散点图

data = train_csv
plot_kind = ['line','bar','barh','hist','box','kde','density','area','pie','scatter','hexbin']

columns = ['SaleID','name','regDate','model','brand','bodyType','fuelType','gearbox','power','kilometer','notRepairedDamage','regionCode',
          'seller','offerType','creatDate','price'] + ['v_' + str(i) for i in range(0,24)]
data.columns
for col in columns:
    plt.scatter(data[col],data['price'],c=data['price'],s = 10 , cmap='rainbow')
    plt.xlabel(col)
    plt.xlabel(col)
    plt.ylabel('price')
    plt.show()

ps:seller,offerType这种分布严重不均衡的特征在Datawhale的baseline里已经提到了,这也可以在散点图里看出
图片上传的顺序受上传速度等的影响,与代码跑出的顺序不一样

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2
Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

我的感觉是图片很好看,但是不知道绘制出来有什么用?
我的理解是:特征构造在无先验理论的支撑下,构造出新特征(尤其是有用的特征)是需要灵感的,也是有难度的,可视化能帮助我们产生奇思妙想。

处理异常值

箱型图识别与处理:

使用pands.DataFrame.plot(kind=‘box’),可绘制箱型图。
注意要一个特征一个特征的绘制,比如↓

for i,col in enumerate(train_csv.columns):
    train_csv[col].plot(kind='box')
    plt.show()

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

特征过多,仅展示部分,情况是很多特征有很多异常值,心累。看看我们训练集price的分布(只是看看,不会用箱型图处理回归目标的,有时会用box-cox):

Datawhale组队学习-河北高校邀请赛-天天向上-天天-task2

感觉就想是箱型图不是很适合识别本数据集

博主写到这里想偷懒了

箱型图处理异常值原理

很简单就可以实现,博主因为参加了邀请赛,现在不适合直接上自己封装的方法,读者可以自行实现。

3σ准则高中数学就说过,
3σ准则

具体方法博主可能等到比赛结束后会更新,但是这无关紧要,重要的是处理数据的原理、方法。

离散变量-类别取高频 ,数值分桶|分箱

  • 类别取高频

  • 地理位置:聚类,算出几个中心点,计算曼哈顿距离、欧氏距离可能有神奇的效果

  • 分桶方法:

    • 等频分桶;
    • 等距分桶;
    • Best-KS 分桶(类似利用基尼指数进行二分类)
    • 卡方分桶
  • 为什么要进行分桶(个人理解):

  • 分箱如何实现,pandas库里就有:
    如何使用pandas进行数据分箱

相关性分析:

  • 为什么要进行相关性分析:
    • 减小特征之间的冗余,模型的参数的更新可能是去处理冗余特征之间的权重去了,没有实质上的进步。
    • data.corr(),即可计算各个特征之间的相关系数
    • 分析完,在相互相关的特征中选取一个或者多个,从而降维、处理冗余特征

主成分分析:

from sklearn.decomposition import IncrementalPCA
from sklearn.decomposition import SparsePCA
from sklearn.decomposition import PCA

sklearn 优雅的进行数据处理,建立模型、调参

具体使用方法,及参数含义

类别变量处理

博主花了很多很多很多时间处理类别变量,实践了我所了解的以下全部编码方式,但是就交叉检验效果与A榜成绩而言,效果好方式的都差不多,没有什么实质上的提升。这也是我觉得匿名变量是本次比赛的关键的想法来源,也让我感觉**特征构造是关键,特征构造是关键,特征构造是关键。**因为我一直局限在原有特征上,怎么处理原数据、怎么调模型的参数、怎么尝试模型,效果好的时候MAE也差不多,效果差MAE能升天

  • one-hot,简单、粗暴、有效

    • 特征取值少,one-hot编码
    • 特征取值太多,统计高频,低频取值归为一类,或者分箱,然后one-hot编码
  • 其他类别编码方式:

    • 升维编码方式: BackwardDifferenceEncoder,BinaryEncoder,HashingEncoder,HelmertEncoder,SumEncoder,
    • 维度不变:
      CatBoostEncoder(),CountEncoder(),GLMMEncoder(),JamesSteinEncoder(),LeaveOneOutEncoder(),MEstimateEncoder(),OrdinalEncoder()TargetEncoder()

使用方法:
使用方法
部分API
老马的一个关于类别变量的博客

题外话,分享一个博主比赛时的趣事:
有一天,我像往常一样将处理数据好的数据喂给模型,模型经过几轮训练后,交叉检验的MAE只有68,我当时大喜,心想,A榜第一100多,我现在就68了。于是我赶紧给模型喂入处理好的testA数据,提交到天池。在我多次刷新之后,看到MAE=3300多。我大惊,仔细检查后发现,我不小心将price放进了训练集。哈哈哈…
END of task2

上一篇:我的0基础入门-二手车交易预测河北高校邀请赛-Task2


下一篇:【CV实践】之零基础入门语义分割---Task2数据增强