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

在这里插入图片描述

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

在这里插入图片描述

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

brand高频类别频数

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()

bodyType

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
'''

fuelType

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()

gearbox

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

power

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

在这里插入图片描述

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()

在这里插入图片描述

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
'''

在这里插入图片描述
regDate的分布就有点讨人厌了,范围1910年到2019年,先做个箱型图看看

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

在这里插入图片描述
这是我很讨厌的特征分布,如果按箱型图识别异常值(这里指的是相对于特征分布中心偏离很大的值),那么非常多的数据会被认为是异常值。然而将过多数据按异常值处理了,有可能“损坏”了数据本身蕴含的信息。目前我尚未发现|找到什么好的处理方法,只能做一般的标准化|归一化了。

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的范围相对小得多。
在这里插入图片描述
在这里插入图片描述
很少的日期在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

regionCode

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
'''

在这里插入图片描述

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
    在这里插入图片描述
  • 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
    在这里插入图片描述

特征与回归目标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里已经提到了,这也可以在散点图里看出
图片上传的顺序受上传速度等的影响,与代码跑出的顺序不一样

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

处理异常值

箱型图识别与处理:

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

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

在这里插入图片描述

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

在这里插入图片描述

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

博主写到这里想偷懒了

箱型图处理异常值原理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值