贝壳er

知乎 https://www.zhihu.com/people/bei-ke-er-1-34/activities

csdn:https://blog.csdn.net/wlx19970505

所谓的EDA,即为数据探索,这里主要指的是赛前的数据探索(第二种是对模型的分析,包括LightGBM/XgBoost的feature importance,LR,SVM的coeff等)。那木赛前数据的EDA要做那些呢,第一个是对数据集的宏观分析,包括数据缺失,数据重复,异常值检测以及一些数据的清洗工作。还有就是变量之间相互关系的分析,包括计算相关性,变量可视化等等,合适的EDA可以帮助我们发现数据中的规律,而预处理可以清洗构造出一个更可用的数据,这对于特征工程和模型构造训练是很重要的一个环节。


对于Predict Future Sales这个赛题,首先读入数据:

 1test = pd.read_csv('test.csv', dtype={'ID': 'int32', 'shop_id': 'int32', 
 2                                                  'item_id': 'int32'})
 3item_categories = pd.read_csv('item_categories.csv', 
 4                              dtype={'item_category_name': 'str', 'item_category_id': 'int32'})
 5items = pd.read_csv('items.csv', dtype={'item_name': 'str', 'item_id': 'int32', 
 6                                                 'item_category_id': 'int32'})
 7shops = pd.read_csv('shops.csv', dtype={'shop_name': 'str', 'shop_id': 'int32'})
 8sales = pd.read_csv('sales_train_v2.csv', parse_dates=['date'], 
 9                    dtype={'date': 'str', 'date_block_num': 'int32', 'shop_id': 'int32', 
10                          'item_id': 'int32', 'item_price': 'float32', 'item_cnt_day': 'int32'})

这里有一点扩展的是pandas的read_csv函数在读取大数据集的时候是比较费时的

将训练集的补充数据和sales合并到一起,组成训练数据。

1train = sales.join(items, on='item_id', rsuffix='_').join(shops, on='shop_id', rsuffix='_').join(item_categories, on='item_category_id', rsuffix='_').drop(['item_id_', 'shop_id_', 'item_category_id_'], axis=1)

这里的rsuffix是用来标记重复的column列,合并之后会被drop掉。我们查看一下训练数据:

640?wx_fmt=png

这里由于原始数据date列是02.01.2013这样的格式,在读取数据的时候之间转换成date型会将月份和日子调换,所以这里的2013-02-01实际上是2013年1月2日,由于后面我并没有用到这个date,而是用到用于月份计数的date_block_num,所以这里就没有跟正了,仅作说明,不影响后面。

可以看到训练数据一共有2935849个样本,10列原始特征列,记录的销量日期从2013年1月到2015年10月,而这里需要我们预测的是2015年11月的销量信息。测试集如下:

640?wx_fmt=png

 数据leakages:

什么是leakages?这里实际上是个tricks,从测试集可以看出,测试集只有shop_id和item_id,且行数少于训练集行数,考虑模型只训练测试集所包含的(shop_id,item_id)对,其他的匹配对不予以考虑,在数据竞赛中这个tricks可以提升不少分数。

 1# 数据泄漏
 2test_shop_ids = test['shop_id'].unique()
 3test_item_ids = test['item_id'].unique()
 4
 5lk_train = train[train['shop_id'].isin(test_shop_ids)]
 6
 7lk_train = lk_train[lk_train['item_id'].isin(test_item_ids)]
 8
 9train_monthly = lk_train[['date', 'date_block_num', 'shop_id', 'item_category_id', 'item_id', 'item_price', 'item_cnt_day']]
10train_monthly.head().append(train_monthly.tail())

这里只考虑用于构造特征的列,现在的数据如下:

640?wx_fmt=png

查看一下重复值和缺失值:

1##  重复值
2print(train_monthly.duplicated().any())
3print('**'*30)
4## 缺失值
5print(train_monthly.isnull().sum())

640?wx_fmt=png

这里无重复值和缺失值。

为方便后续的EDA,这里按照月份,商店和商品来计算出销量和价格的总值和均值:

1train_monthly = train_monthly.sort_values('date').groupby(['date_block_num', 'shop_id', 'item_category_id', 'item_id'], as_index=False)
2
3train_monthly = train_monthly.agg({'item_price':['sum', 'mean'], 'item_cnt_day':['sum', 'mean','count']})
4# Rename features.
5train_monthly.columns = ['date_block_num', 'shop_id', 'item_category_id', 'item_id', 'item_price', 'mean_item_price', 'item_cnt', 'mean_item_cnt', 'transactions']
6train_monthly.head().append(train_monthly.tail())

考虑到测试集可能会有不同的商店和商品的组合,这里我们对训练数据按照shop_id和item_id的组合进行扩充,缺失数据进行零填充,同时构造出具体的年,月信息:

 1shop_ids = train_monthly['shop_id'].unique()
 2item_ids = train_monthly['item_id'].unique()
 3empty_df = []
 4for i in range(34):
 5    for shop in shop_ids:
 6        for item in item_ids:
 7            empty_df.append([i, shop, item])
 8
 9empty_df = pd.DataFrame(empty_df, columns=['date_block_num','shop_id','item_id'])
10
11train_monthly = pd.merge(empty_df, train_monthly, on=['date_block_num','shop_id','item_id'], how='left')
12
13train_monthly.fillna(0, inplace=True)
14
15train_monthly['year'] = train_monthly['date_block_num'].apply(lambda x: ((x//12) + 2013))
16train_monthly['month'] = train_monthly['date_block_num'].apply(lambda x: (x % 12))

640?wx_fmt=png

EDA:

首先,分别按照month,category和shop分组,得到各组的销量。

1gp_month_mean = train_monthly.groupby(['month'], as_index=False)['item_cnt'].mean()
2gp_month_sum = train_monthly.groupby(['month'], as_index=False)['item_cnt'].sum()
3gp_category_mean = train_monthly.groupby(['item_category_id'], as_index=False)['item_cnt'].mean()
4gp_category_sum = train_monthly.groupby(['item_category_id'], as_index=False)['item_cnt'].sum()
5gp_shop_mean = train_monthly.groupby(['shop_id'], as_index=False)['item_cnt'].mean()
6gp_shop_sum = train_monthly.groupby(['shop_id'], as_index=False)['item_cnt'].sum()

分别查看各组和销量之间的关系曲线:


  

640?wx_fmt=png

可以看到时间上,下半年(年末)的销量是增加的。

1f, axes = plt.subplots(2, 1, figsize=(22, 10), sharex=True)
2sns.barplot(x="item_category_id", y="item_cnt", data=gp_category_mean, ax=axes[0], palette="rocket").set_title("Monthly mean")
3sns.barplot(x="item_category_id", y="item_cnt", data=gp_category_sum, ax=axes[1], palette="rocket").set_title("Monthly sum")
4plt.show()

640?wx_fmt=png

我们发现只有部分category对销量具有突出的贡献。

1f, axes = plt.subplots(2, 1, figsize=(22, 10), sharex=True)
2sns.barplot(x="shop_id", y="item_cnt", data=gp_shop_mean, ax=axes[0], palette="rocket").set_title("Monthly mean")
3sns.barplot(x="shop_id", y="item_cnt", data=gp_shop_sum, ax=axes[1], palette="rocket").set_title("Monthly sum")
4plt.show()

640?wx_fmt=png

有三个突出的商店销量比较高,考虑到可能是大商场或者比较出名的零售店,可以用来后续特征工程的构造。

通过散点关联和箱图查看异常值(这里是最直接的方法,更多的异常值检测算法是比较麻烦的,可以自行尝试)

640?wx_fmt=png

640?wx_fmt=png

640?wx_fmt=png

这里,从EDA,我们认为销量不在[0,20],售价超过40000的为异常值,剔除异常值即可。预处理之后的训练集如下所示:

640?wx_fmt=png

接下来一篇需要构造更多的特征,即特征工程。

公众号:AI蜗牛车

保持谦逊、保持自律、保持进步

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐