Predict Future Sales(时间序列)——Kaggle银牌(TOP 4%)基础方案(二):EDA和数据预处理

 

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


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

test = pd.read_csv('test.csv', dtype={'ID': 'int32', 'shop_id': 'int32', 
                                                  'item_id': 'int32'})
item_categories = pd.read_csv('item_categories.csv', 
                              dtype={'item_category_name': 'str', 'item_category_id': 'int32'})
items = pd.read_csv('items.csv', dtype={'item_name': 'str', 'item_id': 'int32', 
                                                 'item_category_id': 'int32'})
shops = pd.read_csv('shops.csv', dtype={'shop_name': 'str', 'shop_id': 'int32'})
sales = pd.read_csv('sales_train_v2.csv', parse_dates=['date'], 
                    dtype={'date': 'str', 'date_block_num': 'int32', 'shop_id': 'int32', 
                          'item_id': 'int32', 'item_price': 'float32', 'item_cnt_day': 'int32'})

这里有一点扩展的是pandas的read_csv函数在读取大数据集的时候是比较费时的,这里可以参考阿水的建议:Pandas常见的性能优化方法

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

train = 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掉。我们查看一下训练数据:

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

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

 数据leakages:

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

# 数据泄漏
test_shop_ids = test['shop_id'].unique()
test_item_ids = test['item_id'].unique()

lk_train = train[train['shop_id'].isin(test_shop_ids)]

lk_train = lk_train[lk_train['item_id'].isin(test_item_ids)]

train_monthly = lk_train[['date', 'date_block_num', 'shop_id', 'item_category_id', 'item_id', 'item_price', 'item_cnt_day']]
train_monthly.head().append(train_monthly.tail())

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

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

##  重复值
print(train_monthly.duplicated().any())
print('**'*30)
## 缺失值
print(train_monthly.isnull().sum())

这里无重复值和缺失值。

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


train_monthly = train_monthly.sort_values('date').groupby(['date_block_num', 'shop_id', 'item_category_id', 'item_id'], as_index=False)

train_monthly = train_monthly.agg({'item_price':['sum', 'mean'], 'item_cnt_day':['sum', 'mean','count']})
# Rename features.
train_monthly.columns = ['date_block_num', 'shop_id', 'item_category_id', 'item_id', 'item_price', 'mean_item_price', 'item_cnt', 'mean_item_cnt', 'transactions']
train_monthly.head().append(train_monthly.tail())

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

shop_ids = train_monthly['shop_id'].unique()
item_ids = train_monthly['item_id'].unique()
empty_df = []
for i in range(34):
    for shop in shop_ids:
        for item in item_ids:
            empty_df.append([i, shop, item])
    
empty_df = pd.DataFrame(empty_df, columns=['date_block_num','shop_id','item_id'])

train_monthly = pd.merge(empty_df, train_monthly, on=['date_block_num','shop_id','item_id'], how='left')

train_monthly.fillna(0, inplace=True)

train_monthly['year'] = train_monthly['date_block_num'].apply(lambda x: ((x//12) + 2013))
train_monthly['month'] = train_monthly['date_block_num'].apply(lambda x: (x % 12))

EDA

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

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

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

f, axes = plt.subplots(2, 1, figsize=(22, 10), sharex=True)
sns.lineplot(x="month", y="item_cnt", data=gp_month_mean, ax=axes[0]).set_title("Monthly mean")
sns.lineplot(x="month", y="item_cnt", data=gp_month_sum, ax=axes[1]).set_title("Monthly sum")
plt.show()

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

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

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

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

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

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

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

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

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值