项目说明:
在这个项目中,我们需要根据不同商店的各商品在2013/01-2015/10中的日常销售数据来预测这些商店的各商品在2015年11月的销售数据(是根据日销售量来预测月销售量)。
项目评估要求
- 将均方根误差(RMSE)作为评估提交的度量指标;
- 真实销售量被限制在[0,20]范围以内。
Kaggle一共提供了5个文件,具体信息如下:
数据字段说明:
- ID - 表示测试集中的(Shop,Item)元组的Id
- shop_id - 商店的唯一标识符
- item_id - 商品的唯一标识符
- item_category_id - 商品类别的唯一标识符
- item_cnt_day - 销售的产品数量。
- item_price - 商品的当前价格
- date - 日期格式为dd / mm / yyyy
- date_block_num - 一个连续的月号,用于方便。2013年1月是0,- 2013年2月是1,...,2015年10月是33
- item_name - 商品名称
- shop_name - 商店名称
- item_category_name - 商品类别的名称
其他说明:
- 编程语言:Python
- 编译工具:jupyter notebook
- 涉及到的库:pandas,numpy,matplotlib,sklearn,seaborn,datetime,dateutil
- Kaggle链接:https://www.kaggle.com/c/competitive-data-science-predict-future-sales
1.0 数据概览
#设置jupyter可以多行输出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all' #默认为'last'
#jupyter绘图魔术方法
%matplotlib notebook
#导入相关库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from dateutil.parser import parse
import warnings
warnings.filterwarnings("ignore")
import os
print(os.listdir())
output:
['.ipynb_checkpoints', 'items.csv', 'item_categories.csv', 'Predict Future Sales-Copy1.ipynb', 'Predict Future Sales.ipynb', 'predict sale.csv', 'predict sale_1.1.csv', 'predict sale_1.csv', 'predict sale_2.csv', 'sales_train_v2.csv', 'sample_submission.csv', 'shops.csv', 'test.csv']
item_categories = pd.read_csv('item_categories.csv')
items = pd.read_csv('items.csv')
shop = pd.read_csv('shops.csv')
train = pd.read_csv('sales_train_v2.csv')
test = pd.read_csv('test.csv')
def eda(data):
print("----------Top-5- Record----------")
print(data.head(3))
print("-----------Information-----------")
print(data.info(null_counts=True))
print("----------Shape of Data----------")
print(data.shape)
print("---------------------------------")
eda(train)
eda(test)
output:
----------Top-5- Record----------
date date_block_num shop_id item_id item_price item_cnt_day
0 02.01.2013 0 59 22154 999.0 1.0
1 03.01.2013 0 25 2552 899.0 1.0
2 05.01.2013 0 25 2552 899.0 -1.0
-----------Information-----------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2935849 entries, 0 to 2935848
Data columns (total 6 columns):
date 2935849 non-null object
date_block_num 2935849 non-null int64
shop_id 2935849 non-null int64
item_id 2935849 non-null int64
item_price 2935849 non-null float64
item_cnt_day 2935849 non-null float64
dtypes: float64(2), int64(3), object(1)
memory usage: 134.4+ MB
None
----------Shape of Data----------
(2935849, 6)
---------------------------------
----------Top-5- Record----------
ID shop_id item_id
0 0 5 5037
1 1 5 5320
2 2 5 5233
-----------Information-----------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 214200 entries, 0 to 214199
Data columns (total 3 columns):
ID 214200 non-null int64
shop_id 214200 non-null int64
item_id 214200 non-null int64
dtypes: int64(3)
memory usage: 4.9 MB
None
----------Shape of Data----------
(214200, 3)
---------------------------------
items[:3]
item_categories[:3]
shop[:3]
output:
item_name item_id item_category_id
0 ! ВО ВЛАСТИ НАВАЖДЕНИЯ (ПЛАСТ.) D 0 40
1 !ABBYY FineReader 12 Professional Edition Full... 1 76
2 ***В ЛУЧАХ СЛАВЫ (UNV) D 2 40
item_category_name item_category_id
0 PC - Гарнитуры/Наушники 0
1 Аксессуары - PS2 1
2 Аксессуары - PS3 2
shop_name shop_id
0 !Якутск Орджоникидзе, 56 фран 0
1 !Якутск ТЦ "Центральный" фран 1
2 Адыгея ТЦ "Мега" 2
可以看到数据集和训练集都没有缺失数据,所以这里我们主要是对异常值和重复数据进行处理。
2.0 数据处理
由于训练集中,date_block_num,shop_id,item_id都是表示类别的,所以这里只需要对item_price, item_cnt_day进行异常值分析处理。
sns.pairplot(train[['item_price','item_cnt_day']])
output:
可以看到item_price有一个数值特别大,大于300000,明显游离在外,可以认为这是一个异常值,另外item_cnt_day也有两个值明显偏大,可以一并去掉。
#去除异常值
train = train[train['item_cnt_day']<1000]
train = train[(train.item_price>0) & (train.item_price<300000)]
train.shape
output:
(2935845, 6)
下面利用.drop_duplicates()方法将训练集中的重复数据去除。
train = train.drop_duplicates()
train.shape
output:
(2935839, 6)
现在我们再来观察下训练集和测试集:
train[:3]
test[:3]
output:
date date_block_num shop_id item_id item_price item_cnt_day
0 02.01.2013 0 59 22154 999.0 1.0
1 03.01.2013 0 25 2552 899.0 1.0
2 05.01.2013 0 25 2552 899.0 -1.0
ID shop_id item_id
0 0 5 5037
1 1 5 5320
2 2 5 5233
我们需要预测的是各商店的各个商品在11月的月销售量,而训练集中的数据是各商店各商品在前面33个月中的日常销售数据,所以我们需要对训练集进行聚合运算,得到各商店各商品每月的销售量。
p_df = train.pivot_table(index=['shop_id','item_id'],columns='date_block_num',values='item_cnt_day',aggfunc='sum').fillna(0.0).reset_index()
然后再增加一个feature,就是将p_df与items数据集合并,增加items_category_id列。
train_cleaned_df = p_df.merge(items[['item_id','item_category_id']],how='inner')
train_cleaned_df[:3]
其实到这里我们的训练集就处理完了,我们得到了各商店各商品在不同月份的销售量,现在我们对测试集也做这样的处理。
test = test.merge(train_cleaned_df,how='left')
test = test.fillna(0.0)
test
3.0 建模预测
现在我们就可以直接建模了,我们生成回归器,然后将训练集中的(0-32)33个月的销售数据作为X_train,将第34个月的数据作为X_test。
那么当我们对test进行预测时,我们就是将拟合好的回归器对(1-32)33个月的数据进行预测。
X_train = train_cleaned_df.iloc[:,(train_cleaned_df.columns != 33)].values
y_train = train_cleaned_df.iloc[:,train_cleaned_df.columns==33].values
X_test = test.iloc[:, (test.columns != 'ID') & (test.columns != 0)].values
from sklearn.model_selection import GridSearchCV, cross_val_score
from xgboost import XGBRegressor
xgbrfr = XGBRegressor()
xgbrfr.fit(X_train,y_train)
y_pred = xgbrfr.predict(X_test)
y_pred = list(map(lambda x: min(20,max(x,0)), list(y_pred)))
sub_df = pd.DataFrame({'ID':test.ID,'item_cnt_month': y_pred })
sub_df.to_csv('predict sale_1.1.csv',index=False)
然后将结果提交到Kaggle上。
成绩还是有一定上升空间的,我们可以对模型进行网格搜索调参,另外我们并对items,shop,item_categories这三个补充文件进行处理,如果进一步研究的话,应该也是能挖掘一些信息的,增加一些新的Feature,我们是使用的是xgboost,我感觉feature还是有点偏少。其实这个数据集非常适合使用时间序列算法AR,MA,ARMA,ARIMA进行建模预测,可能使用时间序列算法效果会更好。