精华内容
下载资源
问答
  • Kaggle项目:Predict Future Sales(商品未来销量预测

    千次阅读 多人点赞 2020-06-19 23:32:48
    Predict Future Sales(商品未来销量预测)1. 关于项目1.1 背景介绍1.2项目数据集说明2. 目标3. 数据预处理3.1 项目数据集预处理3.1.1 训练集和测试集3.1.2 商店数据集3.1.3 商品数据集3.1.4 商品类目数据集3.2 训练...

    1. 关于项目

    1.1 背景介绍

    这是Kaggle竞赛上的一个项目。项目数据由俄罗斯最大的软件公司之一的 1C Company 提供。数据集包含了2013年1月1日到2015年10月31日该公司各商店的商品销售记录。
    项目目标是预测该公司接下来2015年11月的商品销量。
    项目得分使用RMSD(均方根误差,即得分越低代表预测结果的误差越小,预测效果越好。)进行评估。
    项目提交的预测值范围需要在[0,20]。
    项目链接:https://www.kaggle.com/c/competitive-data-science-predict-future-sales


    1.2项目数据集说明

    项目数据集简要说明
    数据集说明
    sales_train.csv训练集
    (包含2013年1月至2015年10月间每天各商店各商品的销量)
    items.csv商品的补充数据集
    (包含商品名称和所属分类字段)
    item_categories.csv商品类目的补充数据集
    (包含商品所属类目的详细信息)
    shops.csv商店信息的补充数据集
    (包含商店所在城市和商店规模的信息)
    shops.csv test.csv测试集
    (需要预测的接下来的2015年11月份的各商店的商品销量)
    sample_submission.csv项目提交数据的模板


    2. 目标

    分析该公司的经营状况,找出影响商品销量的相关因素,预测未来一个月该公司各商店不同商品的销量。


    3. 数据预处理

    3.1 项目数据集预处理

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns
    sns.set()
    

    3.1.1 训练集和测试集

    train = pd.read_csv('sales_train.csv')
    test= pd.read_csv('test.csv')
    train.head()
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    002.01.201305922154999.001.0
    103.01.20130252552899.001.0
    205.01.20130252552899.00-1.0
    306.01.201302525541709.051.0
    415.01.201302525551099.001.0
    test.head()
    
    IDshop_iditem_id
    0055037
    1155320
    2255233
    3355232
    4455268
    print('训练集的商店数量: %d ,商品数量: %d;\n' % (train['shop_id'].unique().size, train['item_id'].unique().size),
         '测试的商店数量: %d,商品数量: %d。' % (test['shop_id'].unique().size, test['item_id'].unique().size))
    
    训练集的商店数量: 60 ,商品数量: 21807;
     测试的商店数量: 42,商品数量: 5100。
    
    test[~test['shop_id'].isin(train['shop_id'].unique())]
    
    IDshop_iditem_id

    test[~test['item_id'].isin(train['item_id'].unique())]['item_id'].unique()[:10]
    
    array([5320, 5268, 5826, 3538, 3571, 3604, 3407, 3408, 3405, 3984],
          dtype=int64)
    

    测试集里面出现了训练集没有的商品,但是可以根据商店的营销情况和商品类目进行预测。如果直接设置为0的话,反而会使提交的数据成绩减分。这个估计也是要考察模型的泛化能力


    3.1.2 商店数据集

    shops = pd.read_csv('shops.csv')
    shops.head()
    
    shop_nameshop_id
    0!Якутск Орджоникидзе, 56 фран0
    1!Якутск ТЦ "Центральный" фран1
    2Адыгея ТЦ "Мега"2
    3Балашиха ТРК "Октябрь-Киномир"3
    4Волжский ТЦ "Волга Молл"4

    经过谷歌翻译和百度翻译得知这个是俄罗斯的语言。其中有几个相同商店名称但是不同ID的店铺
    - 39号: РостовНаДону ТРК "Мегацентр Горизонт"
    - 40号: РостовНаДону ТРК "Мегацентр Горизонт" Островной
    上面这两个商店名,差别在最后一个单词,翻译是“岛”,但是在谷歌地图中查找俄罗斯包含“Мегацентр Горизонт”这个名字的购物中心只有РостовНаДону ТРК这个地方上有且只有一个。推测这两个是同一个商店不同叫法。

    - 10号: Жуковский ул. Чкалова 39м?
    - 11号: Жуковский ул. Чкалова 39м2
    这两个推测是书写不一致导致的。

    - 0号: !Якутск Орджоникидзе, 56 фран
    - 57号: !Якутск Орджоникидзе, 56
    这两个也是书写的不一致的问题,类似某某街道56,和某某街道56号的区别。

    - 58号:Якутск ТЦ "Центральный"
    - 1号: !Якутск ТЦ "Центральный" фран
    同上。

    - 12 和 56 是线上商店


    # 查看测试集是否包含了这几个商店
    test[test['shop_id'].isin([39, 40, 10, 11, 0, 57, 58, 1, 12 ,56])]['shop_id'].unique()
    
    array([10, 12, 57, 58, 56, 39], dtype=int64)
    

    测试集中没有包含同一商店的不同ID, 需要对训练集重复商店的不同ID进行修改,修改的ID则以测试集为准。

    shop_id_map = {11: 10, 0: 57, 1: 58, 40: 39}
    train.loc[train['shop_id'].isin(shop_id_map), 'shop_id'] = train.loc[train['shop_id'].isin(shop_id_map), 'shop_id'].map(shop_id_map)
    train.loc[train['shop_id'].isin(shop_id_map), 'shop_id']
    
    Series([], Name: shop_id, dtype: int64)
    
    train.loc[train['shop_id'].isin([39, 40, 10, 11, 0, 57, 58, 1]), 'shop_id'].unique()
    
    array([57, 58, 10, 39], dtype=int64)
    


    对商店名称进行简单分析后,发现商店名称有一个命名规律

    大部分商店的名称:

    • 开头是一个地区的名称;
    • 中间是商店的规模(比如购物中心:ТЦ、大型购物娱乐中心:ТРЦ等);
    • 尾部带引号的是商店的名称,比如‘xxx’购物中心,大部分可以在谷歌地图上搜索到。
    shops['shop_city'] = shops['shop_name'].map(lambda x:x.split(' ')[0].strip('!'))
    shop_types = ['ТЦ', 'ТРК', 'ТРЦ', 'ТК', 'МТРЦ']
    shops['shop_type'] = shops['shop_name'].map(lambda x:x.split(' ')[1] if x.split(' ')[1] in shop_types else 'Others')
    shops.loc[shops['shop_id'].isin([12, 56]), ['shop_city', 'shop_type']] = 'Online'  # 12和56号是网上商店
    shops.head(13)
    
    shop_nameshop_idshop_cityshop_type
    0!Якутск Орджоникидзе, 56 фран0ЯкутскOthers
    1!Якутск ТЦ "Центральный" фран1ЯкутскТЦ
    2Адыгея ТЦ "Мега"2АдыгеяТЦ
    3Балашиха ТРК "Октябрь-Киномир"3БалашихаТРК
    4Волжский ТЦ "Волга Молл"4ВолжскийТЦ
    5Вологда ТРЦ "Мармелад"5ВологдаТРЦ
    6Воронеж (Плехановская, 13)6ВоронежOthers
    7Воронеж ТРЦ "Максимир"7ВоронежТРЦ
    8Воронеж ТРЦ Сити-Парк "Град"8ВоронежТРЦ
    9Выездная Торговля9ВыезднаяOthers
    10Жуковский ул. Чкалова 39м?10ЖуковскийOthers
    11Жуковский ул. Чкалова 39м²11ЖуковскийOthers
    12Интернет-магазин ЧС12OnlineOnline
    # 对商店信息进行编码,降低模型训练的内存消耗
    shop_city_map = dict([(v,k) for k, v in enumerate(shops['shop_city'].unique())])
    shop_type_map = dict([(v,k) for k, v in enumerate(shops['shop_type'].unique())])
    shops['shop_city_code'] = shops['shop_city'].map(shop_city_map)
    shops['shop_type_code'] = shops['shop_type'].map(shop_type_map)
    shops.head(7)
    
    shop_nameshop_idshop_cityshop_typeshop_city_codeshop_type_code
    0!Якутск Орджоникидзе, 56 фран0ЯкутскOthers00
    1!Якутск ТЦ "Центральный" фран1ЯкутскТЦ01
    2Адыгея ТЦ "Мега"2АдыгеяТЦ11
    3Балашиха ТРК "Октябрь-Киномир"3БалашихаТРК22
    4Волжский ТЦ "Волга Молл"4ВолжскийТЦ31
    5Вологда ТРЦ "Мармелад"5ВологдаТРЦ43
    6Воронеж (Плехановская, 13)6ВоронежOthers50

    3.1.3 商品数据集

    items = pd.read_csv('items.csv')
    items
    
    item_nameitem_iditem_category_id
    0! ВО ВЛАСТИ НАВАЖДЕНИЯ (ПЛАСТ.) D040
    1!ABBYY FineReader 12 Professional Edition Full...176
    2***В ЛУЧАХ СЛАВЫ (UNV) D240
    3***ГОЛУБАЯ ВОЛНА (Univ) D340
    4***КОРОБКА (СТЕКЛО) D440
    ............
    22165Ядерный титбит 2 [PC, Цифровая версия]2216531
    22166Язык запросов 1С:Предприятия [Цифровая версия]2216654
    22167Язык запросов 1С:Предприятия 8 (+CD). Хрустале...2216749
    22168Яйцо для Little Inu2216862
    22169Яйцо дракона (Игра престолов)2216969

    22170 rows × 3 columns


    # 数据集比较大,只分析有没有重复名称不同ID的商品
    items['item_name'] = items['item_name'].map(lambda x: ''.join(x.split(' ')))  # 删除空格
    duplicated_item_name = items[items['item_name'].duplicated()]
    duplicated_item_name 
    
    item_nameitem_iditem_category_id
    2558DEEPPURPLEComeHellOrHighWaterDVD255859
    2970Divinity:DragonCommander[PC,Цифроваяверсия]297031
    5063NIRVANAUnpluggedInNewYorkLP506358
    14539МЕНЯЮЩИЕРЕАЛЬНОСТЬ(регион)1453940
    19475СтругацкиеА.иБ.Улитканасклоне(mp3-CD)(Jewel)1947543
    19581ТАРЗАН(BD)1958137

    duplicated_item_name_rec = items[items['item_name'].isin(duplicated_item_name['item_name'])]  # 6个商品相同名字不同id的记录
    duplicated_item_name_rec
    
    item_nameitem_iditem_category_id
    2514DEEPPURPLEComeHellOrHighWaterDVD251459
    2558DEEPPURPLEComeHellOrHighWaterDVD255859
    2968Divinity:DragonCommander[PC,Цифроваяверсия]296831
    2970Divinity:DragonCommander[PC,Цифроваяверсия]297031
    5061NIRVANAUnpluggedInNewYorkLP506158
    5063NIRVANAUnpluggedInNewYorkLP506358
    14537МЕНЯЮЩИЕРЕАЛЬНОСТЬ(регион)1453740
    14539МЕНЯЮЩИЕРЕАЛЬНОСТЬ(регион)1453940
    19465СтругацкиеА.иБ.Улитканасклоне(mp3-CD)(Jewel)1946543
    19475СтругацкиеА.иБ.Улитканасклоне(mp3-CD)(Jewel)1947543
    19579ТАРЗАН(BD)1957937
    19581ТАРЗАН(BD)1958137


    【依旧是查看测试里面包含了哪一些重复项】

    test[test['item_id'].isin(duplicated_item_name_rec['item_id'])]['item_id'].unique()
    
    array([19581,  5063], dtype=int64)
    


    【测试集包含了2个同名不同id的商品。且都是较大的ID值。需要把训练集里小的ID值都映射为对应较大的ID值。】

    old_id = duplicated_item_name_rec['item_id'].values[::2]
    new_id = duplicated_item_name_rec['item_id'].values[1::2]
    old_new_map = dict(zip(old_id, new_id))
    old_new_map
    
    {2514: 2558, 2968: 2970, 5061: 5063, 14537: 14539, 19465: 19475, 19579: 19581}
    
    train.loc[train['item_id'].isin(old_id), 'item_id'] = train.loc[train['item_id'].isin(old_id), 'item_id'].map(old_new_map)
    train[train['item_id'].isin(old_id)]
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    train[train['item_id'].isin(duplicated_item_name_rec['item_id'].values)]['item_id'].unique()  # 旧id成功替换成新id
    
    array([ 2558, 14539, 19475, 19581,  5063,  2970], dtype=int64)
    

    3.1.4 商品类目数据集

    items.groupby('item_id').size()[items.groupby('item_id').size() > 1]  # 检查同一个商品是否分了不同类目
    
    Series([], dtype: int64)
    
    cat = pd.read_csv('item_categories.csv')
    cat
    
    item_category_nameitem_category_id
    0PC - Гарнитуры/Наушники0
    1Аксессуары - PS21
    2Аксессуары - PS32
    3Аксессуары - PS43
    4Аксессуары - PSP4
    .........
    79Служебные79
    80Служебные - Билеты80
    81Чистые носители (шпиль)81
    82Чистые носители (штучные)82
    83Элементы питания83

    84 rows × 2 columns

    cat[cat['item_category_name'].duplicated()]
    
    item_category_nameitem_category_id

    对类别名称进行简单分析后,发现大部分都是‘大类-小类’的组合形式
    Аксессуары :配件
    Аксессуары - PS2 :PS2游戏机配件
    Игровые консоли :游戏机

    【先拆分大类】

    cat['item_type'] = cat['item_category_name'].map(lambda x: 'Игры' if x.find('Игры ')>0 else x.split(' -')[0].strip('\"')) 
    cat.iloc[[32, 33, 34, -3, -2, -1]]  # 有几个比较特殊,需要另外调整一下
    
    item_category_nameitem_category_iditem_type
    32Карты оплаты (Кино, Музыка, Игры)32Карты оплаты (Кино, Музыка, Игры)
    33Карты оплаты - Live!33Карты оплаты
    34Карты оплаты - Live! (Цифра)34Карты оплаты
    81Чистые носители (шпиль)81Чистые носители (шпиль)
    82Чистые носители (штучные)82Чистые носители (штучные)
    83Элементы питания83Элементы питания
    cat.iloc[[32,-3, -2], -1] = ['Карты оплаты', 'Чистые носители', 'Чистые носители' ]
    cat.iloc[[32,-3, -2]]
    
    item_category_nameitem_category_iditem_type
    32Карты оплаты (Кино, Музыка, Игры)32Карты оплаты
    81Чистые носители (шпиль)81Чистые носители
    82Чистые носители (штучные)82Чистые носители
    item_type_map = dict([(v,k) for k, v in enumerate(cat['item_type'].unique())])
    cat['item_type_code'] = cat['item_type'].map(item_type_map)
    cat.head()
    
    item_category_nameitem_category_iditem_typeitem_type_code
    0PC - Гарнитуры/Наушники0PC0
    1Аксессуары - PS21Аксессуары1
    2Аксессуары - PS32Аксессуары1
    3Аксессуары - PS43Аксессуары1
    4Аксессуары - PSP4Аксессуары1

    【接着是拆分小类】
    cat['sub_type'] = cat['item_category_name'].map(lambda x: x.split('-',1)[-1]) 
    cat
    
    item_category_nameitem_category_iditem_typeitem_type_codesub_type
    0PC - Гарнитуры/Наушники0PC0Гарнитуры/Наушники
    1Аксессуары - PS21Аксессуары1PS2
    2Аксессуары - PS32Аксессуары1PS3
    3Аксессуары - PS43Аксессуары1PS4
    4Аксессуары - PSP4Аксессуары1PSP
    ..................
    79Служебные79Служебные15Служебные
    80Служебные - Билеты80Служебные15Билеты
    81Чистые носители (шпиль)81Чистые носители16Чистые носители (шпиль)
    82Чистые носители (штучные)82Чистые носители16Чистые носители (штучные)
    83Элементы питания83Элементы питания17Элементы питания

    84 rows × 5 columns

    cat['sub_type'].unique()
    
    array([' Гарнитуры/Наушники', ' PS2', ' PS3', ' PS4', ' PSP', ' PSVita',
           ' XBOX 360', ' XBOX ONE', 'Билеты (Цифра)', 'Доставка товара',
           ' Прочие', ' Аксессуары для игр', ' Цифра',
           ' Дополнительные издания', ' Коллекционные издания',
           ' Стандартные издания', 'Карты оплаты (Кино, Музыка, Игры)',
           ' Live!', ' Live! (Цифра)', ' PSN', ' Windows (Цифра)', ' Blu-Ray',
           ' Blu-Ray 3D', ' Blu-Ray 4K', ' DVD', ' Коллекционное',
           ' Артбуки, энциклопедии', ' Аудиокниги', ' Аудиокниги (Цифра)',
           ' Аудиокниги 1С', ' Бизнес литература', ' Комиксы, манга',
           ' Компьютерная литература', ' Методические материалы 1С',
           ' Открытки', ' Познавательная литература', ' Путеводители',
           ' Художественная литература', ' CD локального производства',
           ' CD фирменного производства', ' MP3', ' Винил',
           ' Музыкальное видео', ' Подарочные издания', ' Атрибутика',
           ' Гаджеты, роботы, спорт', ' Мягкие игрушки', ' Настольные игры',
           ' Настольные игры (компактные)', ' Открытки, наклейки',
           ' Развитие', ' Сертификаты, услуги', ' Сувениры',
           ' Сувениры (в навеску)', ' Сумки, Альбомы, Коврики д/мыши',
           ' Фигурки', ' 1С:Предприятие 8', ' MAC (Цифра)',
           ' Для дома и офиса', ' Для дома и офиса (Цифра)', ' Обучающие',
           ' Обучающие (Цифра)', 'Служебные', ' Билеты',
           'Чистые носители (шпиль)', 'Чистые носители (штучные)',
           'Элементы питания'], dtype=object)
    
    sub_type_map = dict([(v,k) for k, v in enumerate(cat['sub_type'].unique())])
    cat['sub_type_code'] = cat['sub_type'].map(sub_type_map)
    cat.head()
    
    item_category_nameitem_category_iditem_typeitem_type_codesub_typesub_type_code
    0PC - Гарнитуры/Наушники0PC0Гарнитуры/Наушники0
    1Аксессуары - PS21Аксессуары1PS21
    2Аксессуары - PS32Аксессуары1PS32
    3Аксессуары - PS43Аксессуары1PS43
    4Аксессуары - PSP4Аксессуары1PSP4

    【合并商品和类目数据集】
    items = items.merge(cat[['item_category_id', 'item_type_code', 'sub_type_code']], on='item_category_id', how='left')
    items.head()
    
    item_nameitem_iditem_category_iditem_type_codesub_type_code
    0!ВОВЛАСТИНАВАЖДЕНИЯ(ПЛАСТ.)D0401024
    1!ABBYYFineReader12ProfessionalEditionFull[PC,Ц...1761459
    2***ВЛУЧАХСЛАВЫ(UNV)D2401024
    3***ГОЛУБАЯВОЛНА(Univ)D3401024
    4***КОРОБКА(СТЕКЛО)D4401024
    import gc
    del cat
    gc.collect()
    
    2917
    



    3.2 训练集数据清洗

    3.2.1 过滤离群值

    利用散点图观察商品价格和单日销量的分布情况

    sns.jointplot('item_cnt_day', 'item_price', train, kind='scatter')
    
    <seaborn.axisgrid.JointGrid at 0xd6e1d68>
    

    在这里插入图片描述

    先过滤明显的离群值

    train_filtered = train[(train['item_cnt_day'] < 800) & (train['item_price'] < 70000)].copy()
    sns.jointplot('item_cnt_day', 'item_price', train_filtered, kind='scatter')
    
    <seaborn.axisgrid.JointGrid at 0xd7bd7b8>
    

    在这里插入图片描述

    查看价格和销量的异常情况

    outer = train[(train['item_cnt_day'] > 400) | (train['item_price'] > 40000)]
    outer
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    88513817.09.20138121136559200.0000001.0
    100663824.10.2013912723842000.0000001.0
    116315813.12.201311126066307980.0000001.0
    148813520.03.201414251319950999.0000001.0
    150116015.03.20141424209495.000000405.0
    157325223.04.2014152780571200.000000401.0
    157325322.04.2014152780571200.000000502.0
    170820728.06.20141725209495.000000501.0
    204851802.10.2014211292421500.000000512.0
    206766704.10.2014215519437899.000000401.0
    206766909.10.2014215519437899.000000508.0
    206767709.10.20142155194451249.000000412.0
    214390320.11.201422121417340900.0000001.0
    225729919.12.20142312209494.000000500.0
    232693015.01.20152412209494.0000001000.0
    232715929.01.20152412724149782.0000001.0
    260804014.04.2015271237311904.548077624.0
    262584719.05.20152812102091499.000000480.0
    262618119.05.2015281211373155.192950539.0
    285107329.09.2015325592491500.000000533.0
    285109130.09.2015325592491702.825746637.0
    286423530.09.2015321292481692.526158669.0
    286426029.09.2015321292481500.000000504.0
    288569223.10.201533421340342990.0000001.0
    289310020.10.201533381340341990.0000001.0
    290940114.10.20153312209494.000000500.0
    290981828.10.20153312113730.9087142169.0
    291015520.10.201533121340341990.0000001.0
    291015629.10.201533121340342990.0000001.0
    291326722.10.201533181340341990.0000001.0
    291776020.10.20153331340342990.0000001.0
    292757222.10.201533281340340991.0000001.0
    293138020.10.201533221340342990.0000001.0

    再检查是否需要修改过滤的阈值

    outer_set = train_filtered[train_filtered['item_id'].isin(outer['item_id'].unique())].groupby('item_id')
     
    fig, ax = plt.subplots(1,1,figsize=(10, 10))
    colors = sns.color_palette() + sns.color_palette('bright')  # 使用调色板。默认颜色只有10来种,会重复使用,不便于观察
    i = 1
    for name, group in outer_set:
        ax.plot(group['item_cnt_day'], group['item_price'], marker='o', linestyle='', ms=12, label=name, c=colors[i])
        i += 1
    ax.legend()
    
    plt.show()
    

    在这里插入图片描述

    上图可以看出:
    1. 青蓝的11365、 橙色的13199 、深红的7241 出现了价格异常高的情况 (大于45000)。(价格在25000 到 45000 是否要考虑)
    2. 浅粉的9248、灰色的9249、橘色的3731 出现了销量特别高的情况 (大于600)。(销量高于500的记录也大部分偏离较远,是否需要计入离群值的考量)

    train[train['item_id'].isin([13403,7238, 14173])]
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    100663824.10.2013912723842000.01.0
    214390320.11.201422121417340900.01.0
    288569223.10.201533421340342990.01.0
    288569325.10.201533421340328992.01.0
    288569429.10.201533421340337991.02.0
    289061726.10.201533311340335991.01.0
    289310020.10.201533381340341990.01.0
    291015520.10.201533121340341990.01.0
    291015629.10.201533121340342990.01.0
    291326722.10.201533181340341990.01.0
    291776020.10.20153331340342990.01.0
    292757222.10.201533281340340991.01.0
    293138020.10.201533221340342990.01.0
    293263726.10.201533251340337991.01.0

    7238号和14173号只出现过一次,13403号则是新品, 可以考虑过滤掉7238号和214173号

    train.loc[train['item_id']==13403].boxplot(['item_cnt_day', 'item_price'])
    
    <matplotlib.axes._subplots.AxesSubplot at 0xf53dc50>
    

    在这里插入图片描述

    销量高于500的记录中, 蓝色11373和灰色9249的记录看起来明显离群,应该过滤。
    剩下的大于400小于600之间的最大值是512。
    再查看400到520这中间的商品的销量情况。

    m_400 = train[(train['item_cnt_day'] > 400) & (train['item_cnt_day'] < 520)]['item_id'].unique()
    n = m_400.size
    fig, axes = plt.subplots(1,n,figsize=(n*4, 6))
    for i in range(n):
        train[train['item_id'] == m_400[i]].boxplot(['item_cnt_day'], ax=axes[i])
        axes[i].set_title('Item%d' % m_400[i])
    plt.show()
    

    在这里插入图片描述

    销量大于400的记录都偏离其总体水平较远。

    总结:
    过滤掉7238号和214173号,过滤掉单价高于45000的记录,过滤掉日销量高于400的记录 。
    本该查看所有商品销量的离群值。但是数量太多了,耗时且不实际。暂且只过滤与总体差异较为显著的。
    况且商品交易本身存在一定的不确定性,去掉全部异常,保留范围内的数据建立的理想化模型来模拟实际情况,结果可能适得其反。保留数据中可接受范围的噪声数据反而更符合实际情况。

    filtered = train[(train['item_cnt_day'] < 400) & (train['item_price'] < 45000)].copy()
    filtered.head()
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    002.01.201305922154999.001.0
    103.01.20130252552899.001.0
    205.01.20130252552899.00-1.0
    306.01.201302525541709.051.0
    415.01.201302525551099.001.0
    filtered.drop(index=filtered[filtered['item_id'].isin([7238, 14173])].index, inplace=True)
    
    del train, train_filtered
    gc.collect()
    
    60
    

    训练集出现销量为-1的情况,估计是出现退货了,属于正常情况。
    需要查看有没有小于0的id或者价格。

    (filtered[['date_block_num', 'shop_id','item_id', 'item_price']] < 0).any()
    
    date_block_num    False
    shop_id           False
    item_id           False
    item_price         True
    dtype: bool
    
    # 商品单价小于0的情况
    filtered[filtered['item_price'] <= 0]
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    48468315.05.20134322973-1.01.0
    filtered.groupby(['date_block_num','shop_id', 'item_id'])['item_price'].mean().loc[4, 32, 2973]
    
    1249.0
    
    filtered.loc[filtered['item_price'] <= 0, 'item_price'] = 1249.0  # 用了同一个月同一个商店该商品的均价
    filtered[filtered['item_price'] <= 0]  # 检查是否替换成功
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_day
    # 下面也给出替换的函数
    def clean_by_mean(df, keys, col):
        """
        用同一月份的均值替换小于等于0的值
        keys 分组键;col 需要替换的字段
        """
        group = df[df[col] <= 0]
        # group = df[df['item_price'] <= 0]
        mean_price = df.groupby(keys)[col].mean()
        # mean_price = df.groupby(['date_block_num', 'shop_id', 'item_id'])['item_price'].mean()
        for i, row in group.iterrows:
            record = group.loc[i]
            df.loc[i,col] = mean_price.loc[record[keys[0]], record[keys[1]], record[keys[2]]]
            # df.loc[i,'item_price'] = mean_price.loc[record['date_block_num'], record['shop_id'], record['item_id']]
        return df
    



    4. 数据规整 和 数据分析(EDA)

    # 添加日营业额
    filtered['turnover_day'] = filtered['item_price'] * filtered['item_cnt_day']
    filtered
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_dayturnover_day
    002.01.201305922154999.001.0999.00
    103.01.20130252552899.001.0899.00
    205.01.20130252552899.00-1.0-899.00
    306.01.201302525541709.051.01709.05
    415.01.201302525551099.001.01099.00
    ........................
    293584410.10.201533257409299.001.0299.00
    293584509.10.201533257460299.001.0299.00
    293584614.10.201533257459349.001.0349.00
    293584722.10.201533257440299.001.0299.00
    293584803.10.201533257460299.001.0299.00

    2935824 rows × 7 columns



    4.1 销量分析

    item_sales_monthly = filtered.pivot_table(columns='item_id',
                                              index='date_block_num', 
                                              values='item_cnt_day',
                                              fill_value=0,
                                              aggfunc=sum)
    item_sales_monthly.head()
    
    item_id0123456789...22160221612216222163221642216522166221672216822169
    date_block_num
    00000000000...11000000020
    10000000000...7000000020
    20000000000...6000000010
    30000000000...2000000000
    40000000000...6100000000

    5 rows × 21796 columns

    fig, axes = plt.subplots(1,2, figsize=(20, 8))
    
    item_sales_monthly.sum(1).plot(ax=axes[0], title='Total sales of each month', xticks=[i for i in range(0,34,2)])  # 每月总销量
    item_sales_monthly.sum(0).plot(ax=axes[1], title='Total sales of each item')  # 每个商品的总销量
    plt.subplots_adjust(wspace=0.2)
    

    在这里插入图片描述

    描述:
    总体销量出现下滑趋势,且每月销量大部分都同比下降。
    有一款商品的销量异常高。

    top_sales = item_sales_monthly.sum().sort_values(ascending=False)
    top_sales
    
    item_id
    20949    184736
    2808      17245
    3732      16642
    17717     15830
    5822      14515
              ...  
    8515          0
    11871        -1
    18062        -1
    13474        -1
    1590        -11
    Length: 21796, dtype: int64
    
    test[test['item_id'].isin(top_sales[top_sales<=0].index)]
    
    IDshop_iditem_id


    4.1.1 销量最高的商品

    top_sales.iloc[0] / item_sales_monthly.sum().sum() * 100  # 销量占比
    
    5.0801850069973
    
    item_sales_monthly[top_sales.index[0]].plot(kind='bar', figsize=(12,6))  # 每月销量
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1101a240>
    

    在这里插入图片描述

    item_turnover_monthly = filtered.pivot_table(index= 'date_block_num',
                                                   columns= 'item_id',
                                                   values='turnover_day',
                                                   fill_value=0,
                                                   aggfunc=sum)
    item_turnover_monthly.head()
    
    item_id0123456789...22160221612216222163221642216522166221672216822169
    date_block_num
    00000000000...147700.00.00.0000.01598.00
    10000000000...96200.00.00.0000.01598.00
    20000000000...89400.00.00.0000.0798.50
    30000000000...29800.00.00.0000.00.00
    40000000000...894580.00.00.0000.00.00

    5 rows × 21796 columns

    item_sales_monthly = item_sales_monthly.drop(columns=top_sales[top_sales<=0].index, axis=1)  # 去掉销量为0和负值的商品
    item_turnover_monthly = item_turnover_monthly.drop(columns=top_sales[top_sales<=0].index, axis=1)
    
    total_turnover = item_turnover_monthly.sum().sum()
    item_turnover_monthly[top_sales.index[0]].sum() / total_turnover * 100
    
    0.02703470087824863
    

    在总销量排名第一的商品,其总销量占到所有总销量的5.080%,但是其总营收却只占到了所有商品总营收的0.027%

    items[items['item_id']==20949 ]
    
    item_nameitem_iditem_category_iditem_type_codesub_type_code
    20949Фирменныйпакетмайка1СИнтересбелый(34*42)45мкм20949711354

    翻译过来是一种打包用的包装。该商品的销量依赖于其他需要包装的商品的销售情况。

    该产品出现月销量总体下降的情况,说明相应依赖的商品也是总体销量呈下降的趋势。


    4.1.2 分析总体销量呈现下降趋势可能存在的原因

    使该公司月销量出现总体下降的原因可能存在于两个方面:
    一是:商店商品本身热度或生命名周期,等商品内在相关因素,使得商品销量减少。
    二是:商品下架或者缺货等外在原因导致商品不再销售,从而影响商品销量。
    这两方面的因素不是孤立存在的,对于总体销量来说,这两个影响因素往往是同时存在的。

    (item_sales_monthly > 0).sum(1).plot(figsize=(12, 6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1e59a860>
    

    在这里插入图片描述

    2013年每个月在有销量的商品的数量(在售商品数量)都在7500个以上。
    2014年每月有销量的商品下降到了约7500-6000个。
    2015年则下降到约6500-500个。

    小结:在售商品数量呈现总体持续下降的趋势。

    item_sales_monthly.sum(1).div((item_sales_monthly > 0).sum(1)).plot(figsize=(12, 6))
    # 商品月总销量 / 当月在售商品数量 = 当月在售商品平均销量
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1896d940>
    

    在这里插入图片描述

    小结:2013年和2014年在售商品月平均销量基本都在13-16个,2015年的在售商品月平均销量则下降到12-14个。

    结论:
    商品丰富度下降是导致总体销量呈现下降趋势的主要因素之一。
    除此之外,2015年在售商品的月均销量相比2013年和2014年下降有所下降,使得2015年总体销量下降幅度高于2013年和2014年。



    4.2 营收分析

    fig, axes = plt.subplots(1,2, figsize=(20, 8))
    item_turnover_monthly.sum(1).plot(ax=axes[0], title='Total turnovers of each month', xticks=[i for i in range(0,34,2)])  # 每月总营收
    item_turnover_monthly.sum(0).plot(ax=axes[1], title='Total turnovers of each item')  # 每个商品的总营收
    plt.subplots_adjust(wspace=0.2)
    

    在这里插入图片描述

    描述:
    第23个月份的营业收入比第11个月同增长明显,而在销量方面则是同比下跌的。
    有一款商品的总营业收入异常的高。


    4.2.1 总营收最高的商品

    top_turnover = item_turnover_monthly.sum().sort_values(ascending=False)
    top_turnover
    
    item_id
    6675     2.193915e+08
    3732     4.361798e+07
    13443    3.433125e+07
    3734     3.106516e+07
    3733     2.229886e+07
                 ...     
    18098    2.100000e+01
    3856     1.700000e+01
    7756     1.500000e+01
    22010    1.400000e+01
    22098    7.000000e+00
    Length: 21788, dtype: float64
    
    item_turnover_monthly[top_turnover.index[0]].sum() / total_turnover * 100
    
    6.472732889978659
    
    item_sales_monthly[top_turnover.index[0]].sum() / item_sales_monthly.sum().sum() * 100
    
    0.2829433478063709
    

    在总营业收入排名第一的商品,其销量占总销量的0.28%,其营业收入占公司总营业收入的6.47% 。

    item_turnover_monthly[top_turnover.index[0]].plot(kind='bar', figsize=(12, 6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x18b3b8d0>
    

    在这里插入图片描述

    item_turnover_monthly[top_turnover.index[0]].div(item_turnover_monthly.sum(1)).plot(figsize=(12, 6),xticks=[i for i in range(0,34,2)])
    
    <matplotlib.axes._subplots.AxesSubplot at 0x18cce588>
    

    在这里插入图片描述

    items[items['item_id']==top_turnover.index[0]]
    
    item_nameitem_iditem_category_iditem_type_codesub_type_code
    6675SonyPlayStation4(500Gb)Black(CUH-1008A/1108A/B01)66751243

    这是一款索尼PS4游戏机的一个型号,在13年11月份开始销售,营业收入最的月份为2013年12月,占当月公司总营业收入比例约23%。在15年最后2个月已经没有销量了。



    4.3 分析导致14年底销量和营收同比增长趋势不一致的可能原因

    turnover_monthly = item_turnover_monthly.sum(1)
    sales_monthly = item_sales_monthly.sum(1)
    fig, axe1 = plt.subplots(1, 1, figsize=(16, 6))
    axe2 = axe1.twinx()
    axe1.plot(turnover_monthly.index, turnover_monthly.values, c='r')
    
    axe2.plot(sales_monthly.index, sales_monthly.values, c='b')
    axe2.grid(c='c', alpha=0.3)
    axe1.legend(['Monthly Turnover'],fontsize=13, bbox_to_anchor=(0.95, 1))
    axe2.legend(['Monthly Sales'],fontsize=13, bbox_to_anchor=(0.93, 0.9))
    axe1.set_ylabel('Monthly Turnover', c='r')
    axe2.set_ylabel('Monthly Sales', c='b')
    plt.show()
    

    在这里插入图片描述

    sales_growth = item_sales_monthly.loc[23].sum() - item_sales_monthly.loc[11].sum()
    sales_growth_rate = sales_growth / item_sales_monthly.loc[11].sum() * 100
    turnover_growth = item_turnover_monthly.loc[23].sum() - item_turnover_monthly.loc[11].sum()
    turnover_growth_rate = turnover_growth / item_turnover_monthly.loc[11].sum() * 100
    print(
        ' 销售同比增长量为: %.2f ,同比增长率为: %.2f%%;\n' % (sales_growth, sales_growth_rate),
        '营收同比增长量为: %.2f ,同比增长率为: %.2f%%。' % (turnover_growth, turnover_growth_rate)
         )
    
     销售同比增长量为: -15086.00 ,同比增长率为: -8.23%;
     营收同比增长量为: 24759419.35 ,同比增长率为: 11.95%。
    

    第23月(2014年12月)销量同比第11月(2013年12月)下降了约15000,但是第23月营业收入同比第11月反而上涨了2500000。
    同比增长比率为:销量同比下降8.23%,营业收入同比上涨11.95%。

    dec_set = item_turnover_monthly.loc[[11, 23]]
    dec_set
    
    item_id0123456789...22160221612216222163221642216522166221672216822169
    date_block_num
    110000000000...000.00.00.00480024613.20.00
    23000002802800...000.00.00.00165011960.00.00

    2 rows × 21788 columns


    观察这两个月份营业收入的分布情况:

    plt.figure(figsize=(8, 4))
    sns.boxenplot(x=dec_set)
    
    'c' argument looks like a single numeric RGB or RGBA sequence, which should be avoided as value-mapping will have precedence in case its length matches with 'x' & 'y'.  Please use a 2-D array with a single row if you really want to specify the same RGB or RGBA value for all points.
    
    
    
    
    
    <matplotlib.axes._subplots.AxesSubplot at 0x18b3bf28>
    

    在这里插入图片描述

    有三款商品的营业收入明显高于其他商品

    dec_top = dec_set.loc[:,dec_set.sum() > 5000000]
    dec_top  # 年底营收最高的商品
    
    item_id66751340513443
    date_block_num
    114.645973e+070.000000e+000.0
    234.014789e+067.906979e+0624203004.2

    在2013年或2014年年底营业收入远超其他商品的商品中,13405号和13443号商品只出现在2014年年底。

    可以从新增的这两款商品对营收影响这方面着手分析。

    dec_top.iloc[1, 1:].sum() / dec_set.iloc[1].sum() * 100  # 只在第23月出售的商品其营业额占第23个月所有商品营业额的百分比
    
    13.839127677594432
    
    item_sales_monthly.loc[23,dec_top.columns[1:]].sum() / item_sales_monthly.loc[23].sum() * 100
    # 13405和13443号商品在第23月销量之和与所有商品总销量的百分比
    
    0.8701078719800304
    

    13405号和13443号商品在2014年12月的营业收入占该月所有商品营业收入的约13.84%,而该月销量只占该月所有总销量的0.87%。

    (dec_set.iloc[1].sum() - dec_set.iloc[0].sum()) / dec_set.iloc[0].sum() * 100  # 同比增长率
    
    11.945851204367324
    
    (dec_set.iloc[1].sum() - dec_set.iloc[0].sum()) / dec_set.iloc[1].sum() * 100  # 增长量占总额的百分比
    
    10.67109774578344
    
    item_turnover_monthly[dec_top.columns[1:]].plot(figsize=(12, 6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x18a29860>
    

    在这里插入图片描述

    该公司14年12月份营业收入同比增长11.94%,同比增长量占总量10.67%。
    而13405号和13443号商品在2014年12月的营业收入占该月所有商品营业收入的约13.84%,超过增长量。

    结论:
    13405号和13443号商品是该公司14年12月份营业收入同比大幅度增长的主要因素之一。
    此外,在前面分析6675号商品中,该商品在2013年12月营收占比约为23%,在2014年的营收占比下降到了约为3%。说明2014年还有其他商品贡献了这约为20%的营业收入。



    4.4 帕累托贡献度分析

    item_turnover_prop_cumsum = item_turnover_monthly.sum().div(total_turnover).sort_values(ascending=False).cumsum()
    pct80 = item_turnover_prop_cumsum.searchsorted(0.8) + 1
    pct80_items = item_turnover_prop_cumsum.iloc[:pct80].index
    pct80_items
    
    Int64Index([ 6675,  3732, 13443,  3734,  3733, 16787,  3731, 13405, 17717,
                 5823,
                ...
                 7073, 14333, 15240, 15450,  5581,  3458,  2040,  4837,  1515,
                20658],
               dtype='int64', name='item_id', length=1678)
    
    sales_pct = item_sales_monthly[pct80_items].sum().sum()/ item_sales_monthly.sum().sum()
    
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.pie([sales_pct, 1 - sales_pct],
            labels=['占总营收80%的商品总销量 ', '占总营收20%的商品总销量 '],
           autopct="%.1f%%",
           textprops={'fontsize':13,'color':"black"}, 
            startangle=90)
    plt.title('帕累托贡献度分析:销量对营收的贡献', fontdict={'fontsize':15,'color':"black"})
    plt.show() 
    

    在这里插入图片描述

    小结: 54.2%的商品销量产生了80%的营收效益。45.8%的商品销量只产生了20%的效益。


    4.5 店铺对营业收入的贡献

    filtered.groupby('shop_id')['item_cnt_day'].sum().sort_values().plot(kind='bar', figsize=(12, 6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1e959710>
    

    在这里插入图片描述

    filtered.groupby('shop_id')['turnover_day'].sum().sort_values().plot(kind='bar', figsize=(12, 6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1e6c35c0>
    

    在这里插入图片描述

    • 对总销量贡献的前三个商店分别是31、25、54号。28号的销量与54号相差不大。
    • 对总营业收费贡献的前三个商店分别是31、25、28号。
    shops[shops['shop_id'].isin([31, 25, 54, 28])]
    
    shop_nameshop_idshop_cityshop_typeshop_city_codeshop_type_code
    25Москва ТРК "Атриум"25МоскваТРК142
    28Москва ТЦ "МЕГА Теплый Стан" II28МоскваТЦ141
    31Москва ТЦ "Семеновский"31МоскваТЦ141
    54Химки ТЦ "Мега"54ХимкиТЦ271

    小结:

    对总销量和总营业收入贡献前三名的商店都来自同一个城市。
    Москва 即:莫斯科,是俄罗斯联邦首都、莫斯科州首府,Химки也是来自莫斯科州,莫斯科州GDP占俄罗斯全国GDP的1/3。
    * 商店所在城市的经济规模在很大程度上影响了商店的商品销量。


    4.6 产品种类对营业收入的贡献

    filtered = filtered.merge(items.iloc[:,1:], on='item_id', how='left')
    filtered.head()
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_dayturnover_dayitem_category_iditem_type_codesub_type_code
    002.01.201305922154999.001.0999.00371021
    103.01.20130252552899.001.0899.00581241
    205.01.20130252552899.00-1.0-899.00581241
    306.01.201302525541709.051.01709.05581241
    415.01.201302525551099.001.01099.00561239
    filtered.groupby('item_category_id')['turnover_day'].sum().sort_values().plot(kind='bar',figsize=(16,6), rot=0)
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1e1d31d0>
    

    在这里插入图片描述

    filtered.groupby('item_type_code')['turnover_day'].sum().sort_values().plot(kind='bar',figsize=(12,6), rot=0)
    
    <matplotlib.axes._subplots.AxesSubplot at 0xf7c1438>
    

    在这里插入图片描述

    filtered.groupby('sub_type_code')['turnover_day'].sum().sort_values().plot(kind='bar',figsize=(12,6), rot=0)
    
    <matplotlib.axes._subplots.AxesSubplot at 0x6c28a20>
    

    在这里插入图片描述

    小结:

    • 商品类目中对总营业收入贡献最大的前三类分别是第19、20、12类。
    • 商品大类中第5类商品对总营业收入贡献最大,且与其他类差异明显。
    • 商品小类中对总营业收入贡献最大的前三类分别是第3、2、6类。
    filtered.groupby('item_category_id')['item_cnt_day'].sum().sort_values().plot(kind='bar',figsize=(16,6), rot=0)
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1e9510f0>
    

    在这里插入图片描述

    filtered.groupby('item_type_code')['item_cnt_day'].sum().sort_values().plot(kind='bar',figsize=(12,6), rot=0)
    
    <matplotlib.axes._subplots.AxesSubplot at 0x10fc67b8>
    

    在这里插入图片描述

    filtered.groupby('sub_type_code')['item_cnt_day'].sum().sort_values().plot(kind='bar',figsize=(12,6), rot=0)
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1089d9b0>
    

    在这里插入图片描述

    小结:

    • 商品类目中对总销量贡献最大的前三类分别是第40、30、55类。
    • 商品大类中对总销量贡献最大的前三类分别是第10、8、5类,
    • 商品小类中对总销量贡献最大的前三类分别是第24、15、38类。

    • 在商品大类销量与营业收入综合贡献中,影响最大的是第5和第8类。
    • 在商品类目和商品小类中销量和营收前三名中,没有出现重叠的情况。没有综合贡献突出的类。

    * 不同种类的商品存在较为明显的销量差异。

    4.7 城市和商店类型对营业收入的贡献

    filtered = filtered.merge(shops[['shop_id','shop_city_code','shop_type_code']], on='shop_id', how='left')
    filtered.head()
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_dayturnover_dayitem_category_iditem_type_codesub_type_codeshop_city_codeshop_type_code
    002.01.201305922154999.001.0999.00371021291
    103.01.20130252552899.001.0899.00581241142
    205.01.20130252552899.00-1.0-899.00581241142
    306.01.201302525541709.051.01709.05581241142
    415.01.201302525551099.001.01099.00561239142
    filtered.groupby('shop_city_code')['turnover_day'].sum().plot(kind='bar',figsize=(12,6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x10895860>
    

    在这里插入图片描述

    filtered.groupby('shop_type_code')['turnover_day'].sum().plot(kind='bar',figsize=(12,6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1af6aac8>
    

    在这里插入图片描述

    小结: 14区城市的商店对总营业收入贡献最大。1类商店对总营业收入贡献最大。

    filtered.groupby('shop_city_code')['item_cnt_day'].sum().plot(kind='bar',figsize=(12,6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1b2500b8>
    

    在这里插入图片描述

    filtered.groupby('shop_type_code')['item_cnt_day'].sum().plot(kind='bar',figsize=(12,6))
    
    <matplotlib.axes._subplots.AxesSubplot at 0x1b27cda0>
    

    在这里插入图片描述

    小结:

    • 在销量方面也是14区城市的商店和1类商店贡献最大。
    • 主要影响因素是总营收和总销量排名前三的商店都在14区城市。

    * 除了前面分析的商店所在城市会影响商品销量,商店的规模同样在较大程度上影响了商品销量。

    题外话:
    前面三句红色加粗的话,看起来从常识就可以推断出来了,我在查阅别人的方案时,看到的几乎是直接跳过对这些特征的分析的过程,只给出哪些特征是有用的,容易让人不明所以。
    以我个人的理解,通过观测这些特征的值的分布情况,可以直观地了解不同特征对结果影响程度的差异。
    而且在后面特征重要性画图时,这些特征的重要性和特征本身对结果影响程度两者之间的一致性,可以作为特征选择可靠性的评判参考。



    5. 预测未来销量

    5.1 处理关闭的商店和停售的商品

    shop_sales_monthly = filtered.pivot_table(index='date_block_num',
                                              columns='shop_id',
                                              values='item_cnt_day',
                                              fill_value=0,
                                              aggfunc=sum)
    shop_open_month_cnt = (shop_sales_monthly.iloc[-6:] >  0).sum()  # 有销量的记录
    shop_open_month_cnt.head()  # 每个店铺最后半年里有几个月有销量
    
    shop_id
    2    6
    3    6
    4    6
    5    6
    6    6
    dtype: int64
    
    shop_c_n = shop_sales_monthly[shop_open_month_cnt[shop_open_month_cnt < 6].index]
    shop_c_n.tail(12)
    # 最后半年经营月数少于6个月的店铺
    
    shop_id8913172023272930323336435154
    date_block_num
    2200011990046751926204408140265910906389
    23000183200789624022700010560313916527677
    240006890056601564184201006013409766043
    25000000383912737450792006604221
    260000003634123900505005454625
    270-100003518121600-100494732
    280000003786880000007580
    2900000033570000006590
    3000000024780000007480
    3100000000000009160
    32000000-10000006240
    3303186002611000000330000

    9, 20, 36 当作是新店,其他的当作已经关闭了

    open_shop = shop_sales_monthly[shop_open_month_cnt[shop_open_month_cnt == 6].index]
    open_shop.tail(7) # 最后半年都正常经营的商店
    
    shop_id23456710121415...48495052535556575859
    date_block_num
    27859740899105419981340594262010551364...10815428951152132234221237286017101054
    2884373189310121748121746629309331277...1110692107389412062117131524081378916
    2980467279395415391235441183010191332...99078990082010781909156624401554913
    307855358429911484132744915549541316...1102856112682812571658149123521689992
    31942666947129415751409442147110611360...13089661081932131819761604278017381214
    32822745732109217251287519404210941267...1101567906108612295697119422661319914
    33727613831105218021212428151210021243...111164894984710611972126323161446790

    7 rows × 41 columns

    item_selling_month_cnt = (item_sales_monthly.iloc[-6:] >  0).sum() 
    item_selling_month_cnt.head()  # 这些商品在最后半年有几个月有销量
    
    item_id
    0    0
    1    0
    2    0
    3    0
    4    0
    dtype: int64
    
    item_zero = item_sales_monthly[item_selling_month_cnt[item_selling_month_cnt == 0].index]
    # 这些商品在最后半年都没有销量
    item_zero.tail(12)
    
    item_id0123456789...22150221512215222156221572216022161221652216822169
    date_block_num
    220010000000...0000000000
    230000010100...0000000000
    240000000000...0000000000
    250000000000...0000000000
    260000000000...0000000000
    270000000000...0000000000
    280000000000...0000000000
    290000000000...0000000000
    300000000000...0000000000
    310000000000...0000000000
    320000000000...0000000000
    330000000000...0000000000

    12 rows × 12878 columns

    selling_item = item_sales_monthly[item_selling_month_cnt[item_selling_month_cnt > 0].index]
    selling_item.tail(12)  # 最后半年有销量的商品
    
    item_id30313233383940424549...22153221542215522158221592216222163221642216622167
    date_block_num
    22131129201001383...000000001649
    231618402111052102...100000001140
    24142542197012112...00000000733
    2514133226411124...100003110289846
    265124020100232...000001940921240
    274132013001155...0000078027438
    28552012300222...00001235023831
    294102611200113...0010322061033
    30462115500134...0800027012834
    316533014710044...160001429201129
    32391916200163...041007209521
    331182216001112...050101026151137

    12 rows × 8910 columns



    5.2 处理训练集

    只保留最后6个月正常经营的商店和有销量的商品

    cl_set = filtered[filtered['shop_id'].isin(open_shop.columns) & filtered['item_id'].isin(selling_item.columns)]
    cl_set
    
    datedate_block_numshop_iditem_iditem_priceitem_cnt_dayturnover_dayitem_category_iditem_type_codesub_type_codeshop_city_codeshop_type_code
    002.01.201305922154999.01.0999.0371021291
    103.01.20130252552899.01.0899.0581241142
    205.01.20130252552899.0-1.0-899.0581241142
    1003.01.20130252574399.02.0798.0551238142
    1105.01.20130252574399.01.0399.0551238142
    .......................................
    293581910.10.201533257409299.01.0299.0551238142
    293582009.10.201533257460299.01.0299.0551238142
    293582114.10.201533257459349.01.0349.0551238142
    293582222.10.201533257440299.01.0299.0571240142
    293582303.10.201533257460299.01.0299.0551238142

    1782392 rows × 12 columns

    统计月销量

    from itertools import product
    import time
    ts = time.time()
    martix = []
    for i in range(34):
        record = cl_set[cl_set['date_block_num'] == i]
        group = product([i],record.shop_id.unique(),record.item_id.unique())
        martix.append(np.array(list(group)))
                
    cols = ['date_block_num', 'shop_id', 'item_id']
    martix = pd.DataFrame(np.vstack(martix), columns=cols)
    
    martix
    
    date_block_numshop_iditem_id
    005922154
    10592552
    20592574
    30592607
    40592515
    ............
    494019933217635
    494020033217638
    494020133217640
    494020233217632
    494020333217440

    4940204 rows × 3 columns

    from itertools import product
    import time
    ts = time.time()
    martix = []
    for i in range(34):
        record = filtered[filtered['date_block_num'] == i]
        group = product([i],record.shop_id.unique(),record.item_id.unique())
        martix.append(np.array(list(group)))
                
    cols = ['date_block_num', 'shop_id', 'item_id']
    martix = pd.DataFrame(np.vstack(martix), columns=cols)
    
    martix
    
    date_block_numshop_iditem_id
    005922154
    10592552
    20592554
    30592555
    40592564
    ............
    1084073533217635
    1084073633217638
    1084073733217640
    1084073833217632
    1084073933217440

    10840740 rows × 3 columns

    del cl_set
    gc.collect()
    
    20
    
    group = filtered.groupby(['date_block_num', 'shop_id', 'item_id']).agg({'item_cnt_day': np.sum})
    group.columns = ['item_cnt_month']
    group.reset_index(inplace=True)
    group
    
    date_block_numshop_iditem_iditem_cnt_month
    002271.0
    102331.0
    2023171.0
    3024381.0
    4024712.0
    ...............
    16082423359220876.0
    16082433359220882.0
    16082443359220911.0
    16082453359221001.0
    16082463359221021.0

    1608247 rows × 4 columns

    martix = pd.merge(martix, group, on=['date_block_num', 'shop_id', 'item_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_month
    0059221541.0
    10592552NaN
    20592554NaN
    30592555NaN
    40592564NaN

    测试集是第34月的数据,所以给测试集合添加上'date_block_num'字段并设置为34。
    待预测的字段‘item_cnt_mun’就设置为0。

    test['date_block_num'] = 34
    test['item_cnt_month'] = 0
    martix = pd.concat([martix.fillna(0), test.drop(columns='ID')], sort=False, ignore_index=True, keys=['date_block_num','shop_id','item_id'])
    martix
    
    date_block_numshop_iditem_iditem_cnt_month
    0059221541.0
    105925520.0
    205925540.0
    305925550.0
    405925640.0
    ...............
    110549353445184540.0
    110549363445161880.0
    110549373445157570.0
    110549383445196480.0
    1105493934459690.0

    11054940 rows × 4 columns



    5.3 特征处理

    5.3.1 融合商店数据集和商品数据集的特征

    martix = martix.merge(shops[['shop_id', 'shop_type_code', 'shop_city_code']], on='shop_id', how='left')
    martix = martix.merge(items.drop(columns='item_name'), on='item_id', how='left')
    martix
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_code
    0059221541.0129371021
    105925520.0129581241
    205925540.0129581241
    305925550.0129561239
    405925640.0129591242
    ..............................
    110549353445184540.0121551238
    110549363445161880.0121641347
    110549373445157570.0121551238
    110549383445196480.0121401024
    1105493934459690.0121371021

    11054940 rows × 9 columns

    添加具体的年份和月份

    martix['year'] =  martix['date_block_num'].map(lambda x: x // 12 + 2013)
    martix['month'] = martix['date_block_num'].map(lambda x: x % 12)
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonth
    0059221541.012937102120130
    105925520.012958124120130
    205925540.012958124120130
    305925550.012956123920130
    405925640.012959124220130


    5.3.2 添加当月销量特征


    当月商店、商品销量均值
    # 商品 月销量均值
    group = martix.groupby(['date_block_num','item_id']).agg({'item_cnt_month':'mean'})
    group.columns = ['item_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num', 'item_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avg
    0059221541.0129371021201300.400000
    105925520.0129581241201300.000000
    205925540.0129581241201300.022222
    305925550.0129561239201300.044444
    405925640.0129591242201300.111111
    # 商店 月销量均值
    group = martix.groupby(['date_block_num','shop_id']).agg({'item_cnt_month':'mean'})
    group.columns = ['shop_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num', 'shop_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avg
    0059221541.0129371021201300.4000000.248613
    105925520.0129581241201300.0000000.248613
    205925540.0129581241201300.0222220.248613
    305925550.0129561239201300.0444440.248613
    405925640.0129591242201300.1111110.248613

    当月种类销量均值
    # 类别 月销量均值
    group = martix.groupby(['date_block_num','item_category_id']).agg({'item_cnt_month':'mean'})
    group.columns = ['cat_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num', 'item_category_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avgcat_cnt_month_avg
    0059221541.0129371021201300.4000000.2486130.199738
    105925520.0129581241201300.0000000.2486130.043386
    205925540.0129581241201300.0222220.2486130.043386
    305925550.0129561239201300.0444440.2486130.049630
    405925640.0129591242201300.1111110.2486130.093842
    # 商店-类别 月销量均值
    group = martix.groupby(['date_block_num','shop_id','item_category_id']).agg({'item_cnt_month':'mean'})
    group.columns = ['shop_cat_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num','shop_id','item_category_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avgcat_cnt_month_avgshop_cat_cnt_month_avg
    0059221541.0129371021201300.4000000.2486130.1997380.088496
    105925520.0129581241201300.0000000.2486130.0433860.000000
    205925540.0129581241201300.0222220.2486130.0433860.000000
    305925550.0129561239201300.0444440.2486130.0496300.008333
    405925640.0129591242201300.1111110.2486130.0938420.012048
    # 大类 月销量均值
    group = martix.groupby(['date_block_num', 'item_type_code']).agg({'item_cnt_month':'mean'})
    group.columns = ['itemtype_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num', 'item_type_code'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avgcat_cnt_month_avgshop_cat_cnt_month_avgitemtype_cnt_month_avg
    0059221541.0129371021201300.4000000.2486130.1997380.0884960.284744
    105925520.0129581241201300.0000000.2486130.0433860.0000000.166607
    205925540.0129581241201300.0222220.2486130.0433860.0000000.166607
    305925550.0129561239201300.0444440.2486130.0496300.0083330.166607
    405925640.0129591242201300.1111110.2486130.0938420.0120480.166607
    # 小类 月销量均值
    group = martix.groupby(['date_block_num', 'sub_type_code']).agg({'item_cnt_month':'mean'})
    group.columns = ['subtype_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num','sub_type_code'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avgcat_cnt_month_avgshop_cat_cnt_month_avgitemtype_cnt_month_avgsubtype_cnt_month_avg
    0059221541.0129371021201300.4000000.2486130.1997380.0884960.2847440.199738
    105925520.0129581241201300.0000000.2486130.0433860.0000000.1666070.043386
    205925540.0129581241201300.0222220.2486130.0433860.0000000.1666070.043386
    305925550.0129561239201300.0444440.2486130.0496300.0083330.1666070.049630
    405925640.0129591242201300.1111110.2486130.0938420.0120480.1666070.093842

    当月商店城市和商店类型的销量均值
    # 城市-商品 月销量均值
    group = martix.groupby(['date_block_num','shop_city_code','item_id']).agg({'item_cnt_month':'mean'})
    group.columns = ['city_item_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num','shop_city_code','item_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avgcat_cnt_month_avgshop_cat_cnt_month_avgitemtype_cnt_month_avgsubtype_cnt_month_avgcity_item_cnt_month_avg
    0059221541.0129371021201300.4000000.2486130.1997380.0884960.2847440.1997381.0
    105925520.0129581241201300.0000000.2486130.0433860.0000000.1666070.0433860.0
    205925540.0129581241201300.0222220.2486130.0433860.0000000.1666070.0433860.0
    305925550.0129561239201300.0444440.2486130.0496300.0083330.1666070.0496300.0
    405925640.0129591242201300.1111110.2486130.0938420.0120480.1666070.0938420.0
    # 商店类型-商品 月销量均值
    group = martix.groupby(['date_block_num','shop_type_code','item_id']).agg({'item_cnt_month':'mean'})
    group.columns = ['shoptype_item_cnt_month_avg']
    group.reset_index(inplace=True)
    martix = martix.merge(group, on=['date_block_num','shop_type_code','item_id'], how='left')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonthitem_cnt_month_avgshop_cnt_month_avgcat_cnt_month_avgshop_cat_cnt_month_avgitemtype_cnt_month_avgsubtype_cnt_month_avgcity_item_cnt_month_avgshoptype_item_cnt_month_avg
    0059221541.0129371021201300.4000000.2486130.1997380.0884960.2847440.1997381.00.28
    105925520.0129581241201300.0000000.2486130.0433860.0000000.1666070.0433860.00.00
    205925540.0129581241201300.0222220.2486130.0433860.0000000.1666070.0433860.00.00
    305925550.0129561239201300.0444440.2486130.0496300.0083330.1666070.0496300.00.04
    405925640.0129591242201300.1111110.2486130.0938420.0120480.1666070.0938420.00.12
    del group
    gc.collect()
    
    28
    



    5.3.3 添加销量特征的历史特征

    本次项目目标是用历史销量数据预测未来销量。
    所以在这里我们转换一下思路,用历史销量数据作为模型的特征,将本月销量结果作为标签建立模型进行回归分析。

    def lag_feature(df, lags, col):
        tmp = df[['date_block_num','shop_id','item_id',col]]
        for i in lags:
            shifted = tmp.copy()
            shifted.columns = ['date_block_num','shop_id','item_id', col+'_lag_'+str(i)]
            shifted['date_block_num'] += i
            df = pd.merge(df, shifted, on=['date_block_num','shop_id','item_id'], how='left')
        return df
    
    martix = lag_feature(martix, [1,2,3,6,12], 'item_cnt_month')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...shop_cat_cnt_month_avgitemtype_cnt_month_avgsubtype_cnt_month_avgcity_item_cnt_month_avgshoptype_item_cnt_month_avgitem_cnt_month_lag_1item_cnt_month_lag_2item_cnt_month_lag_3item_cnt_month_lag_6item_cnt_month_lag_12
    0059221541.01293710212013...0.0884960.2847440.1997381.00.28NaNNaNNaNNaNNaN
    105925520.01295812412013...0.0000000.1666070.0433860.00.00NaNNaNNaNNaNNaN
    205925540.01295812412013...0.0000000.1666070.0433860.00.00NaNNaNNaNNaNNaN
    305925550.01295612392013...0.0083330.1666070.0496300.00.04NaNNaNNaNNaNNaN
    405925640.01295912422013...0.0120480.1666070.0938420.00.12NaNNaNNaNNaNNaN

    5 rows × 24 columns

    martix = lag_feature(martix, [1,2,3,6,12], 'item_cnt_month_avg')
    martix = lag_feature(martix, [1,2,3,6,12], 'shop_cnt_month_avg')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...item_cnt_month_avg_lag_1item_cnt_month_avg_lag_2item_cnt_month_avg_lag_3item_cnt_month_avg_lag_6item_cnt_month_avg_lag_12shop_cnt_month_avg_lag_1shop_cnt_month_avg_lag_2shop_cnt_month_avg_lag_3shop_cnt_month_avg_lag_6shop_cnt_month_avg_lag_12
    0059221541.01293710212013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    105925520.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    205925540.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    305925550.01295612392013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    405925640.01295912422013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

    5 rows × 34 columns

    martix.drop(columns=[ 'item_cnt_month_avg', 'shop_cnt_month_avg'], inplace=True)  # 只保留特征的历史信息
    gc.collect()
    
    40
    
    martix = lag_feature(martix, [1,2,3,6,12], 'cat_cnt_month_avg')
    martix = lag_feature(martix, [1,2,3,6,12], 'shop_cat_cnt_month_avg')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...cat_cnt_month_avg_lag_1cat_cnt_month_avg_lag_2cat_cnt_month_avg_lag_3cat_cnt_month_avg_lag_6cat_cnt_month_avg_lag_12shop_cat_cnt_month_avg_lag_1shop_cat_cnt_month_avg_lag_2shop_cat_cnt_month_avg_lag_3shop_cat_cnt_month_avg_lag_6shop_cat_cnt_month_avg_lag_12
    0059221541.01293710212013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    105925520.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    205925540.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    305925550.01295612392013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    405925640.01295912422013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

    5 rows × 42 columns

    martix.drop(columns=['cat_cnt_month_avg', 'shop_cat_cnt_month_avg'], inplace=True)
    
    martix = lag_feature(martix, [1,2,3,6,12], 'itemtype_cnt_month_avg')
    martix = lag_feature(martix, [1,2,3,6,12], 'subtype_cnt_month_avg')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...itemtype_cnt_month_avg_lag_1itemtype_cnt_month_avg_lag_2itemtype_cnt_month_avg_lag_3itemtype_cnt_month_avg_lag_6itemtype_cnt_month_avg_lag_12subtype_cnt_month_avg_lag_1subtype_cnt_month_avg_lag_2subtype_cnt_month_avg_lag_3subtype_cnt_month_avg_lag_6subtype_cnt_month_avg_lag_12
    0059221541.01293710212013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    105925520.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    205925540.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    305925550.01295612392013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    405925640.01295912422013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

    5 rows × 50 columns

    martix.drop(columns=['itemtype_cnt_month_avg', 'subtype_cnt_month_avg'], inplace=True)
    
    martix = lag_feature(martix, [1,2,3,6,12], 'city_item_cnt_month_avg')
    martix = lag_feature(martix, [1,2,3,6,12], 'shoptype_item_cnt_month_avg')
    martix.head()
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...city_item_cnt_month_avg_lag_1city_item_cnt_month_avg_lag_2city_item_cnt_month_avg_lag_3city_item_cnt_month_avg_lag_6city_item_cnt_month_avg_lag_12shoptype_item_cnt_month_avg_lag_1shoptype_item_cnt_month_avg_lag_2shoptype_item_cnt_month_avg_lag_3shoptype_item_cnt_month_avg_lag_6shoptype_item_cnt_month_avg_lag_12
    0059221541.01293710212013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    105925520.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    205925540.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    305925550.01295612392013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    405925640.01295912422013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

    5 rows × 58 columns

    martix.drop(columns=[ 'city_item_cnt_month_avg','shoptype_item_cnt_month_avg'], inplace=True)
    martix
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...city_item_cnt_month_avg_lag_1city_item_cnt_month_avg_lag_2city_item_cnt_month_avg_lag_3city_item_cnt_month_avg_lag_6city_item_cnt_month_avg_lag_12shoptype_item_cnt_month_avg_lag_1shoptype_item_cnt_month_avg_lag_2shoptype_item_cnt_month_avg_lag_3shoptype_item_cnt_month_avg_lag_6shoptype_item_cnt_month_avg_lag_12
    0059221541.01293710212013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    105925520.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    205925540.01295812412013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    305925550.01295612392013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    405925640.01295912422013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
    ..................................................................
    110549353445184540.01215512382015...0.50.00.00.0NaN0.1111110.000.0526320.666667NaN
    110549363445161880.01216413472015...0.00.0NaNNaNNaN0.0000000.05NaNNaNNaN
    110549373445157570.01215512382015...0.00.50.00.00.00.1666670.100.1052630.1428570.250000
    110549383445196480.01214010242015...0.00.00.00.0NaN0.0555560.050.2631580.142857NaN
    1105493934459690.01213710212015...0.50.00.00.00.00.1111110.150.0000000.0952380.041667

    11054940 rows × 56 columns

    martix[martix.columns[:20]].isna().any()
    
    date_block_num              False
    shop_id                     False
    item_id                     False
    item_cnt_month              False
    shop_type_code              False
    shop_city_code              False
    item_category_id            False
    item_type_code              False
    sub_type_code               False
    year                        False
    month                       False
    item_cnt_month_lag_1         True
    item_cnt_month_lag_2         True
    item_cnt_month_lag_3         True
    item_cnt_month_lag_6         True
    item_cnt_month_lag_12        True
    item_cnt_month_avg_lag_1     True
    item_cnt_month_avg_lag_2     True
    item_cnt_month_avg_lag_3     True
    item_cnt_month_avg_lag_6     True
    dtype: bool
    


    使用之前月份的销量,会导致有很多记录缺失信息。需要将这部分缺失信息的记录去掉。

    前面延迟了12个月的销量信息,这里就直接把前12个月的记录删除。

    train_set = martix[martix['date_block_num'] > 11].fillna(0)
    train_set
    
    date_block_numshop_iditem_iditem_cnt_monthshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyear...city_item_cnt_month_avg_lag_1city_item_cnt_month_avg_lag_2city_item_cnt_month_avg_lag_3city_item_cnt_month_avg_lag_6city_item_cnt_month_avg_lag_12shoptype_item_cnt_month_avg_lag_1shoptype_item_cnt_month_avg_lag_2shoptype_item_cnt_month_avg_lag_3shoptype_item_cnt_month_avg_lag_6shoptype_item_cnt_month_avg_lag_12
    44877061254102974.01273710212014...3.00.00.00.00.01.0800000.080.0000000.0000000.000000
    44877071254102963.01273810222014...0.00.00.00.00.00.4800000.000.0000000.0000000.000000
    448770812541029814.01274010242014...21.0119.07.00.00.08.32000033.683.5200000.0000000.000000
    44877091254103003.01273710212014...1.031.00.00.00.01.0400008.241.1200000.0000000.000000
    44877101254102841.01275712402014...0.00.00.01.00.00.0800000.040.1600000.1153850.000000
    ..................................................................
    110549353445184540.01215512382015...0.50.00.00.00.00.1111110.000.0526320.6666670.000000
    110549363445161880.01216413472015...0.00.00.00.00.00.0000000.050.0000000.0000000.000000
    110549373445157570.01215512382015...0.00.50.00.00.00.1666670.100.1052630.1428570.250000
    110549383445196480.01214010242015...0.00.00.00.00.00.0555560.050.2631580.1428570.000000
    1105493934459690.01213710212015...0.50.00.00.00.00.1111110.150.0000000.0952380.041667

    6567234 rows × 56 columns



    5.3.4 对类别特征进行独热编码、类别编码

    lightgbm算法模块的train函数中,有个categorical_feature参数,模型在训练时默认会将Dataframe的category类型字段自动识别为类别特征,就不需要预先对类别特征进行one-hot编码。
    很多特征的值的动态范围都比较小,但数据类型默认都是folat64和int64的,非常的占用内存,在训练的时候可能会抛出导致内存不足的异常。这里可以相应地修改成占用内存较低的int32和float32等类型,降低模型训练时的内存压力。

    for col in train_set.columns:
        if col.find('code') >= 0:
            train_set[col] = train_set[col].astype(np.int8)
        elif train_set[col].dtype == 'float64':
            train_set[col] = train_set[col].astype(np.float32)
        elif train_set[col].dtype == 'int64':
            train_set[col] = train_set[col].astype(np.int16)
            
    train_set['item_type_code'] = train_set['item_type_code'].astype('category')
    train_set['sub_type_code'] = train_set['sub_type_code'].astype('category')
    train_set.info()
    
    <class 'pandas.core.frame.DataFrame'>
    Int64Index: 6567234 entries, 4487706 to 11054939
    Data columns (total 56 columns):
    date_block_num                        int16
    shop_id                               int16
    item_id                               int16
    item_cnt_month                        float32
    shop_type_code                        int8
    shop_city_code                        int8
    item_category_id                      int16
    item_type_code                        category
    sub_type_code                         category
    year                                  int16
    month                                 int16
    item_cnt_month_lag_1                  float32
    item_cnt_month_lag_2                  float32
    item_cnt_month_lag_3                  float32
    item_cnt_month_lag_6                  float32
    item_cnt_month_lag_12                 float32
    item_cnt_month_avg_lag_1              float32
    item_cnt_month_avg_lag_2              float32
    item_cnt_month_avg_lag_3              float32
    item_cnt_month_avg_lag_6              float32
    item_cnt_month_avg_lag_12             float32
    shop_cnt_month_avg_lag_1              float32
    shop_cnt_month_avg_lag_2              float32
    shop_cnt_month_avg_lag_3              float32
    shop_cnt_month_avg_lag_6              float32
    shop_cnt_month_avg_lag_12             float32
    cat_cnt_month_avg_lag_1               float32
    cat_cnt_month_avg_lag_2               float32
    cat_cnt_month_avg_lag_3               float32
    cat_cnt_month_avg_lag_6               float32
    cat_cnt_month_avg_lag_12              float32
    shop_cat_cnt_month_avg_lag_1          float32
    shop_cat_cnt_month_avg_lag_2          float32
    shop_cat_cnt_month_avg_lag_3          float32
    shop_cat_cnt_month_avg_lag_6          float32
    shop_cat_cnt_month_avg_lag_12         float32
    itemtype_cnt_month_avg_lag_1          float32
    itemtype_cnt_month_avg_lag_2          float32
    itemtype_cnt_month_avg_lag_3          float32
    itemtype_cnt_month_avg_lag_6          float32
    itemtype_cnt_month_avg_lag_12         float32
    subtype_cnt_month_avg_lag_1           float32
    subtype_cnt_month_avg_lag_2           float32
    subtype_cnt_month_avg_lag_3           float32
    subtype_cnt_month_avg_lag_6           float32
    subtype_cnt_month_avg_lag_12          float32
    city_item_cnt_month_avg_lag_1         float32
    city_item_cnt_month_avg_lag_2         float32
    city_item_cnt_month_avg_lag_3         float32
    city_item_cnt_month_avg_lag_6         float32
    city_item_cnt_month_avg_lag_12        float32
    shoptype_item_cnt_month_avg_lag_1     float32
    shoptype_item_cnt_month_avg_lag_2     float32
    shoptype_item_cnt_month_avg_lag_3     float32
    shoptype_item_cnt_month_avg_lag_6     float32
    shoptype_item_cnt_month_avg_lag_12    float32
    dtypes: category(2), float32(46), int16(6), int8(2)
    memory usage: 1.3 GB
    

    5.4 使用模型训练数据集

    这里选择使用lightgbm模型进行训练。

    import lightgbm as lgb
    
    
    X_train = train_set[train_set['date_block_num'] < 33].drop(columns=['item_cnt_month'])  # 训练集的样本特征
    Y_train = train_set[train_set['date_block_num'] < 33]['item_cnt_month']  # 训练集的样本标签
    
    X_validate = train_set[train_set['date_block_num'] == 33].drop(columns=['item_cnt_month'])  # 校对集
    Y_validate = train_set[train_set['date_block_num'] == 33]['item_cnt_month']
    
    X_test = train_set[train_set['date_block_num'] == 34].drop(columns=['item_cnt_month'])  # 测试集
    
    del train_set
    gc.collect()
    
    20
    
    # 把数据加载为模型适合的数据格式
    train_data = lgb.Dataset(data=X_train, label=Y_train)
    validate_data = lgb.Dataset(data=X_validate, label=Y_validate)
    
    # 设置模型训练参数
    import time
    ts = time.time()
    params = {"objective" : "regression", "metric" : "rmse", 'n_estimators':10000, 'early_stopping_rounds':50,
                  "num_leaves" : 200, "learning_rate" : 0.01, "bagging_fraction" : 0.9,
                  "feature_fraction" : 0.3, "bagging_seed" : 0}
    print('Start....', ts)
    lgb_model = lgb.train(params, train_data, valid_sets=[train_data, validate_data], verbose_eval=1000) 
    print('End...', time.time() - ts)
    
    Start.... 1591718735.6430335
    
    
    c:\users\honk\appdata\local\programs\python\python37\lib\site-packages\lightgbm\engine.py:148: UserWarning: Found `n_estimators` in params. Will use it instead of argument
      warnings.warn("Found `{}` in params. Will use it instead of argument".format(alias))
    c:\users\honk\appdata\local\programs\python\python37\lib\site-packages\lightgbm\engine.py:153: UserWarning: Found `early_stopping_rounds` in params. Will use it instead of argument
      warnings.warn("Found `{}` in params. Will use it instead of argument".format(alias))
    
    
    Training until validation scores don't improve for 50 rounds
    Early stopping, best iteration is:
    [152]	training's rmse: 1.95378	valid_1's rmse: 1.57771
    End... 330.06891345977783
    
    # 特征重要性画图
    lgb.plot_importance(lgb_model, max_num_features=40, figsize=(12, 8))
    plt.title("Featurertances")
    plt.show()
    

    在这里插入图片描述

    lgb_model.save_model('model_bestscore02.txt')  # 保存模型
    
    <lightgbm.basic.Booster at 0x1e86f1d0>
    
    # 根据项目要求,把数据“裁剪”到[0,20]的区间。
    Y_test = lgb_model.predict(X_test).clip(0, 20)
    Y_test
    
    array([0.57359759, 0.3126507 , 0.93170284, ..., 0.11140766, 0.10172111,
           0.1178424 ])
    
    X_test['item_cnt_month'] = Y_test
    X_test
    
    date_block_numshop_iditem_idshop_type_codeshop_city_codeitem_category_iditem_type_codesub_type_codeyearmonth...city_item_cnt_month_avg_lag_2city_item_cnt_month_avg_lag_3city_item_cnt_month_avg_lag_6city_item_cnt_month_avg_lag_12shoptype_item_cnt_month_avg_lag_1shoptype_item_cnt_month_avg_lag_2shoptype_item_cnt_month_avg_lag_3shoptype_item_cnt_month_avg_lag_6shoptype_item_cnt_month_avg_lag_12item_cnt_month
    108407403455037341952201510...1.03.01.01.00.4444442.004.2500002.5000001.2500000.573598
    10840741345532034551238201510...0.00.00.00.00.0000000.000.0000000.0000000.0000000.312651
    108407423455233341952201510...3.01.03.00.01.2222222.504.5000002.1250000.0000000.931703
    108407433455232342356201510...0.01.00.00.00.5555561.252.0000000.0000000.0000000.403808
    108407443455268342053201510...0.00.00.00.00.0000000.000.0000000.0000000.0000002.331300
    ..................................................................
    11054935344518454121551238201510...0.00.00.00.00.1111110.000.0526320.6666670.0000000.172498
    11054936344516188121641347201510...0.00.00.00.00.0000000.050.0000000.0000000.0000000.118829
    11054937344515757121551238201510...0.50.00.00.00.1666670.100.1052630.1428570.2500000.111408
    11054938344519648121401024201510...0.00.00.00.00.0555560.050.2631580.1428570.0000000.101721
    110549393445969121371021201510...0.00.00.00.00.1111110.150.0000000.0952380.0416670.117842

    214200 rows × 56 columns


    将预测结果合并到测试集。

    result = pd.merge(test[['ID', 'shop_id', 'item_id']],X_test[['shop_id','item_id','item_cnt_month']], on=['shop_id', 'item_id'], how='left')
    result
    
    IDshop_iditem_iditem_cnt_month
    00550370.573598
    11553200.312651
    22552330.931703
    33552320.403808
    44552682.331300
    ...............
    21419521419545184540.172498
    21419621419645161880.118829
    21419721419745157570.111408
    21419821419845196480.101721
    214199214199459690.117842

    214200 rows × 4 columns

    result.isna().any()
    
    ID                False
    shop_id           False
    item_id           False
    item_cnt_month    False
    dtype: bool
    

    前面分析关闭的商店中,有3个商在最近半年里只有最后一个月有销量,推断是新开的商店。
    还有一些商品是最近半年都没有销量,推断是已经下架的商品,预测值填为0 。

    result[result.shop_id.isin(shop_c_n.columns)]['shop_id'].unique()
    
    array([36], dtype=int64)
    
    result.loc[result.item_id.isin(item_zero), 'item_cnt_month'] = 0
    result.loc[result.item_id.isin(item_zero), 'item_cnt_month']
    
    298       0.0
    493       0.0
    817       0.0
    953       0.0
    1165      0.0
             ... 
    214165    0.0
    214167    0.0
    214176    0.0
    214179    0.0
    214180    0.0
    Name: item_cnt_month, Length: 7812, dtype: float64
    
    result[['ID','item_cnt_month']].to_csv('fromfinal01.csv',sep=',',index=False)
    

    5.5 预测效果评估

    得分:0.93740

    在这里插入图片描述
    当前排名:2560/7373


    6. 项目总结

    项目经验:
    本次项目花了几个星期的业余时间才完成的,花费了很多精力和时间,但总算是有所收获。
    从数据分析方法到特征工程和预测模型的构建,都花了很多时间去研究和梳理。通过这次项目学到了很多,包括问题切入的有效面、分析算法的代码实现,分析流程的设计等,让我从整体上更好的掌握数据分析的思维。


    项目不足:

    • 数据分析方法分两种:一种是统计分析方法,另一种是挖掘方法。在做预测型分析的时候,特征分析和模型构建往往都需要用到数据挖掘的方法和思维,在这个一方面的理论和数据支撑还不够简明有力,还需要加强学习。

    • 在特征处理中,还有一个商品价格的特征没有加入到预测模型里面。主要原因是对价格特征的处理还存在一些不足之处,价格特征比较特殊,和销量不一样,如果某商品当月没有销量的话,销量可以记为0,但是价格则不能记录为0 。我的做法是使用最近半年内上次有销量的价格,但是预测效果并不好,需要重新理解这内在的联系,重新构建价格特征。
    展开全文
  • Prophet模型预测商品销售量

    千次阅读 2019-01-28 11:15:55
    机器学习训练营(qq群号:696721295)—— 机器学习案例详解的直播互动...如果你擅长预测商品未来一段时间的销量,你就能合理地安排你的库存盘点和分类。 该案例要求根据提供的2013年至2017年商店-商品的销售数据,...

    机器学习训练营(入群联系qq:2279055353)—— 机器学习案例详解的直播互动平台
    下期直播案例预告:大数据预测商品的销售量波动趋势

    案例介绍

    像沃尔玛、家乐福这样的超市零售商,使用销售预报系统和工具补充商品。一套完善的预报系统有助于赢得其它的供应链渠道。如果你擅长预测商品未来一段时间的销量,你就能合理地安排你的库存盘点和分类。

    该案例要求根据提供的2013年至2017年商店-商品的销售数据,使用时间序列技术,预测10家不同的商店的50种不同的商品在未来3个月的销售量。

    • 数据来源:Kaggle 竞赛

    • 代码实现:R 语言

    数据文件

    训练集train.csv与检验集test.csv. 请到以下百度网盘下载:

    链接:https://pan.baidu.com/s/1j2FCvikgR2aPkfW2bDtLsA 密码:mmby

    加载R包

    首先,加载必需的R包。

    rm(list=ls())
    
    suppressMessages(library(data.table))
    suppressMessages(library(DT))
    suppressMessages(library(timeSeries))
    suppressMessages(library(tidyverse))
    suppressMessages(library(reshape))
    suppressMessages(library(stringr))
    suppressMessages(library(doBy))
    suppressMessages(library(formattable))
    suppressMessages(library(gridExtra))
    
    suppressMessages(library(ggplot2))
    suppressMessages(library(plotly))
    suppressMessages(library(corrplot))
    suppressMessages(library(wesanderson))
    suppressMessages(library(RColorBrewer))
    suppressMessages(library(gridExtra))
    suppressMessages(library(zoo))
    
    suppressMessages(library(forecast))
    suppressMessages(library(prophet)) 
    
    set.seed(2018)
    

    加载数据集

    train=fread("e:/kaggle_exercises/forecast/input/train.csv")
    sprintf("The train data set has %d rows and %d columns", nrow(train), ncol(train) )
    str(train)
    
    test  <- fread("e:/kaggle_exercises/forecast/input/test.csv")
    sprintf("The test data set has %d rows and %d columns", nrow(test), ncol(test) )
    str(test)
    
    print("the summary of train sales is:")
    summary(train$sales)
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    从变量date里提取“年、月”变量
    在这里插入图片描述

    # Extraction of Year and Month of Year :
    train$Year=year(train$date)        #returns the year from date i.e. 2013, 2014 etc.
    train$Month=as.yearmon(train$date) #this yearmon() function is coming from zoo package returns the month of an year i.e Jan 2013, Feb 2015 etc
    head(train)
    

    缺失值检查

    colSums(is.na(train))
    # Function 1 : For ploting missing value
    plot_missing <- function(data, title = NULL, ggtheme = theme_gray(), theme_config = list("legend.position" = c("bottom"))) {
      ## Declare variable first to pass R CMD check
      feature <- num_missing <- pct_missing <- group <- NULL
      ## Check if input is data.table
      is_data_table <- is.data.table(data)
      ## Detect input data class
      data_class <- class(data)
      ## Set data to data.table
      if (!is_data_table) data <- data.table(data)
      ## Extract missing value distribution
      missing_value <- data.table(
        "feature" = names(data),
        "num_missing" = sapply(data, function(x) {sum(is.na(x))})
      )
      missing_value[, feature := factor(feature, levels = feature[order(-rank(num_missing))])]
      missing_value[, pct_missing := num_missing / nrow(data)]
      missing_value[pct_missing < 0.05, group := "Good"]
      missing_value[pct_missing >= 0.05 & pct_missing < 0.4, group := "OK"]
      missing_value[pct_missing >= 0.4 & pct_missing < 0.8, group := "Bad"]
      missing_value[pct_missing >= 0.8, group := "Remove"][]
      ## Set data class back to original
      if (!is_data_table) class(missing_value) <- data_class
      ## Create ggplot object
      output <- ggplot(missing_value, aes_string(x = "feature", y = "num_missing", fill = "group")) +
        geom_bar(stat = "identity") +
        geom_text(aes(label = paste0(round(100 * pct_missing, 2), "%"))) +
        scale_fill_manual("Group", values = c("Good" = "#1a9641", "OK" = "#a6d96a", "Bad" = "#fdae61", "Remove" = "#d7191c"), breaks = c("Good", "OK", "Bad", "Remove")) +
        scale_y_continuous(labels = comma) +
        coord_flip() +
        xlab("Features") + ylab("Number of missing rows") +
        ggtitle(title) +
        ggtheme + theme_linedraw()+
        do.call(theme, theme_config)
      ## Print plot
      print(output)
      ## Set return object
      return(invisible(missing_value))
    }
    plot_missing(train)
    

    在这里插入图片描述
    可见,数据集里没有缺失值。

    特征可视化

    我们用图形的方式探索训练集里的特征的分布及变化情况。

    销售量直方图

    gbp1<-wes_palette("GrandBudapest2")[1]
    
    ggplot(train, aes(x=sales))+
      geom_histogram(fill="#a6d96a", alpha=.9)+
      labs(x=NULL, y=NULL, title = "Histogram of Sale Price")+
      # scale_x_continuous(breaks= seq(0,600000, by=100000))+
      theme_minimal() + theme(plot.title=element_text(vjust=3, size=15))
    

    在这里插入图片描述
    从直方图可以看出,销售量的分布是正偏的。

    日均销售量与改变率

    改变率rate定义为

    r a t e = s a l e s i + 1 − s a l e s i s a l e s i , &ThinSpace; i = 0 , 1 , … , t − 1 rate=\dfrac{sales_{i+1}-sales_i}{sales_i},\, i=0,1,\dots,t-1 rate=salesisalesi+1salesi,i=0,1,,t1

    MSP <- aggregate(sales ~date, train, mean)
    # MSP <-na.omit(ddply(data, 'date', summarise, mean(Sale_Prices, na.rm=T)))
    
    sl1 <-ggplot(MSP, aes(x=as.factor(date), y=sales))+
      geom_line(color=gbp1, aes(group=1), size=1.5)+
      geom_point(colour=gbp1, size = 3.5, alpha=0.5)+
      labs(title="The Growth of Sale Prices by date", x=NULL, y="Sale Price")+
      theme( plot.title=element_text(vjust=3, size=15) ) + theme_minimal()
    
    MSP$rate = c(0, 100*diff(MSP$sales)/MSP[-nrow(MSP),]$sales)
    
    sl2 <-ggplot(MSP, aes(x=as.factor(date), y=rate))+
      geom_line(color= "gray50", aes(group=1), size=1)+
      #geom_point(colour=gbp1, size = 3.5, alpha=0.5)+
      labs(title="Change rate of Sale Price", x="date", y="rate of change")+
      geom_hline(yintercept = 0, color = gbp1 )+
      theme(plot.title=element_text(size=15))+ theme_minimal()
    
    grid.arrange(sl1,sl2)
    

    在这里插入图片描述
    销售量的改变率关于日期是固定的,我们再看看改变率关于年和月的变化,先来看看关于月份的变化。

    MSP <- aggregate(sales ~Month, train, mean)
    # MSP <-na.omit(ddply(data, 'date', summarise, mean(Sale_Prices, na.rm=T)))
    
    sl1 <-ggplot(MSP, aes(x=as.factor(Month), y=sales))+
      geom_line(color=gbp1, aes(group=1), size=1.5)+
      geom_point(colour=gbp1, size = 3.5, alpha=0.5)+
      labs(title="The Growth of Sale Prices by Month of Year", x=NULL, y="Sale Price")+
      theme( plot.title=element_text(vjust=3, size=15) ) + theme_minimal()
    
    MSP$rate = c(0, 100*diff(MSP$sales)/MSP[-nrow(MSP),]$sales)
    
    sl2 <-ggplot(MSP, aes(x=as.factor(Month), y=rate))+
      geom_line(color= "gray50", aes(group=1), size=1)+
      #geom_point(colour=gbp1, size = 3.5, alpha=0.5)+
      labs(title="Change rate of Sale Price", x="Month", y="rate of change")+
      geom_hline(yintercept = 0, color = gbp1 )+
      theme(plot.title=element_text(size=15))+ theme_minimal()
    
    grid.arrange(sl1,sl2)
    

    在这里插入图片描述
    改变率关于月份呈周期性的波动变化,再来看看关于年份的变化情况。

    MSP <- aggregate(sales ~Year, train, mean)
    # MSP <-na.omit(ddply(data, 'date', summarise, mean(Sale_Prices, na.rm=T)))
    
    sl1 <-ggplot(MSP, aes(x=as.factor(Year), y=sales))+
      geom_line(color=gbp1, aes(group=1), size=1.5)+
      geom_point(colour=gbp1, size = 3.5, alpha=0.5)+
      labs(title="The Growth of Sale Prices by Year", x=NULL, y="Sale Price")+
      theme( plot.title=element_text(vjust=3, size=15) ) + theme_minimal()
    
    MSP$rate = c(0, 100*diff(MSP$sales)/MSP[-nrow(MSP),]$sales)
    
    sl2 <-ggplot(MSP, aes(x=as.factor(Year), y=rate))+
      geom_line(color= "gray50", aes(group=1), size=1)+
      #geom_point(colour=gbp1, size = 3.5, alpha=0.5)+
      labs(title="Change rate of Sale Price", x="Year", y="rate of change")+
      geom_hline(yintercept = 0, color = gbp1 )+
      theme(plot.title=element_text(size=15))+ theme_minimal()
    
    grid.arrange(sl1,sl2)
    

    在这里插入图片描述
    我们可以得到4点结论:

    • 销售量逐年增长;

    • 销售量的年改变率呈“高-低”式的波动;

    • 从2013年到2014年,改变率单调增加,而从2014到2015年又单调下降;

    • 最高的增长率出现在2014年;

    • 按这种波动规律,我们推测在2018年改变率是增加的。

    不同商店的销售量

    我们看看不同的商店的销售量情况,训练集有多少家商店呢?

    unique(train$store)
    

    在这里插入图片描述
    我们再画出这10家商店从2013年到2017年的销售量。

    Year_state<-aggregate(sales ~store+Year, train,mean)
    pal<-rep(brewer.pal(10, "BrBG"),5)
    
    ggplot(Year_state, aes(group = store ))+
      geom_line(aes(x=Year,y=sales,color=store), alpha=0.5, show.legend=F)+
      labs(title="The Growth of Sales Price by Store from 2013 - 2017", x=NULL
      )+
      theme(panel.background=element_rect(fill = "Black"),
            plot.title=element_text(vjust=3, size=15),
            panel.grid.major=element_line(color = pal))
    

    在这里插入图片描述
    3号商店有最高的年销售量变化,而7号商店的最低。

    不同商品的年销售量

    训练集train有多少种商品呢?

    unique(train$item)
    

    在这里插入图片描述
    我们看一看这50种商店年销售量的变化趋势。

    Year_state<-aggregate(sales ~item+Year, train,mean)
    pal<-rep(brewer.pal(10, "BrBG"),5)
    
    ggplot(Year_state, aes(group = item ))+
      geom_line(aes(x=Year,y=sales,color=item), alpha=0.5, show.legend=F)+
      labs(title="The Growth of Sales Price by Store from 2013 - 2017", x=NULL
      )+
      theme(panel.background=element_rect(fill = "Black"),
            plot.title=element_text(vjust=3, size=15),
            panel.grid.major=element_line(color = pal))
    

    在这里插入图片描述
    17号商品年销售量最高,26号商品的最低。

    Prophet model

    Prophet 介绍

    Prophet是在一个加性模型的基础上预报时间序列数据。这个加性模型拟合时间序列里的年、周、日、季节、假期效应等趋势。对于有强季节效应的时间序列,Prophet 模型特别有效。Prophet对缺失数据、季节漂移也是稳健的。一个Prophet模型定义为

    y ^ = t r e n d × ( 1 + m u l t i p l i c a t i v e &ThinSpace; t e r m s ) + a d d i t i v e &ThinSpace; t e r m s \hat{y}=trend\times(1+multiplicative\,terms)+additive\,terms y^=trend×(1+multiplicativeterms)+additiveterms

    Prophet 基础模型

    现在,我们产生一个Prophet基础模型:store= 1, Product_ID=1. 即,预报1号商店的1号商品销售量。为此,需要先把这两个变量的观测值进行对数变换。

    train_final_store1_item1=subset(train,train$store==1 & train$item==1)
    
    stats=data.frame(y=log1p(train_final_store1_item1$sales)
                     ,ds=train_final_store1_item1$date)
    stats=aggregate(stats$y,by=list(stats$ds),FUN=sum)
    head(stats)
    

    在这里插入图片描述

    colnames(stats)<- c("ds","y")
    model_prophet = prophet(stats)
    summary(model_prophet)
    
    future = make_future_dataframe(model_prophet, periods = 90)
    forecast = predict(model_prophet, future)
    

    下面,可视化我们的Prophet model的改变点。

    add_changepoints_to_plot <- function(m, threshold = 0.01, cp_color = "red",
                                   cp_linetype = "dashed", trend = TRUE, ...) {
      layers <- list()
      if (trend) {
        trend_layer <- ggplot2::geom_line(
          ggplot2::aes_string("ds", "trend"), color = cp_color, ...)
        layers <- append(layers, trend_layer)
      }
      signif_changepoints <- m$changepoints[abs(m$params$delta) >= threshold]
      cp_layer <- ggplot2::geom_vline(
        xintercept = as.integer(signif_changepoints), color = cp_color,
        linetype = cp_linetype, ...)
      layers <- append(layers, cp_layer)
      return(layers)
    }
    plot(model_prophet, forecast)+ add_changepoints_to_plot(model_prophet)
    

    在这里插入图片描述
    图中红线标记的是改变点,我们需要删除多个改变点,这是由于模型过度拟合造成的。

    我们再检查一下模型成分:

    prophet_plot_components(model_prophet, forecast)
    

    在这里插入图片描述
    我们发现,从周日到周一销售量明显下降。因此在销售数据里一定有假日效应。在7月份有一个销售量高峰,这意味着假日时间或者打折促销的影响。综合这些分析,我们认为有必要优化Prophet参数,尝试排除改变点,包括假日效应。

    展开全文
  • 内容在我的个人博客里面:...

    内容在我的个人博客里面:https://www.liuzhi.org.cn/archives/3065

    展开全文
  • Kaggle比赛——预测未来销售(一)

    万次阅读 2018-11-30 15:01:22
    预测未来销售——项目介绍1、数据来源2、数据集说明2、1 文件说明2、2 文件字段说明2、3 商店名称说明(Google翻译过来的)3、项目要求3、1 评估的要求3、2 提交文件格式 1、数据来源   预测未来销售该项目来源于...

    1、数据来源

      预测未来销售该项目来源于kaggle中的一场比赛的赛题,比赛使用的数据是由日常销售数据组成的时间序列数据集,该数据集由俄罗斯最大的软件公司之一 - 1C公司提供。

    2、数据集说明

      数据集下载地址

    2、1 文件说明

    文件名文件说明包含属性
    sales_train.csv训练集(2013年1月至2015年10月的每日历史数据,包括销售额)date、date_block_num、shop_id、item_id、item_price、item_cnt_day
    test.csv测试集(预测2015年11月这些商店和产品的销售额)ID、shop_id、item_id
    sample_submission.csv格式正确的示例提交文件ID、item_cnt_month
    items.csv有关商品/产品的补充信息item_name、item_id、item_category_id
    item_categories.csv有关项目类别的补充信息item_category_name、item_category_id
    shops.csv有关商店的补充信息shop_name、shop_id

    2、2 文件字段说明

    字段字段说明
    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项目类别的名称
    ID表示测试集中的(商店,项目)元组的ID

    2、3 商店名称说明(Google翻译过来的)

    item_category_nameitem_category_id
    PC - 耳机/耳机0
    配件 - PS21
    配件 - PS32
    配件 - PS43
    配件 - PSP4
    配件 - PSVita5
    配件 - XBOX 3606
    配件 - XBOX ONE7
    门票(号码)8
    货物交付9
    游戏机 - PS210
    游戏机 - PS311
    游戏机 - PS412
    游戏机 - PSP13
    游戏机 - PSVita14
    游戏机 - XBOX 36015
    游戏机 - XBOX ONE16
    游戏机 - 其他17
    游戏 - PS218
    游戏 - PS319
    游戏 - PS420
    游戏 - PSP21
    游戏 - PSVita22
    游戏 - XBOX 36023
    游戏 - XBOX ONE24
    游戏 - 游戏配件25
    Android游戏 - 数字26
    MAC游戏 - 数字27
    PC游戏 - 其他出版物28
    PC电脑游戏 - 收藏版29
    PC游戏 - 标准版30
    电脑游戏 - 数字31
    支付卡(电影、音乐、游戏)32
    支付卡 - 直播33
    支付卡 - 直播! (数字)34
    支付卡 - PSN35
    支付卡 - Windows(数字)36
    电影院 - 蓝光37
    电影院 - 蓝光3D38
    电影院 - 蓝光4K39
    电影院 - DVD40
    电影院 - 收藏家41
    书籍 - 艺术书、百科全书42
    书籍 - 有声读物43
    书籍 - 有声读物(图)44
    书籍 - 有声读物1C45
    书籍 - 商业文学46
    书籍 - 漫画47
    书籍 - 计算机文学48
    书籍 - 有条理的材料1C49
    书籍 - 明信片50
    书籍 - 认知文学51
    书籍 - 指南52
    书籍 - 小说53
    书籍 - 数字54
    音乐 - 本地CD55
    音乐 - 品牌CD制作56
    音乐 - MP357
    音乐 - 乙烯基58
    音乐 - 音乐视频59
    音乐 - 礼品版60
    礼品 - 属性61
    礼品 - 小工具、机器人、体育62
    礼品 - 软玩具63
    礼品 - 棋盘游戏64
    礼品 - 棋盘游戏(紧凑型)65
    礼品 - 卡片、贴纸66
    礼品 - 发展67
    礼品 - 证书、服务68
    礼品 - 纪念品69
    礼品 - 纪念品(链接)70
    礼品 - 袋,相册,鼠标垫71
    礼品 - 数字72
    程序 - 1C:企业873
    程序 - MAC(数字)74
    计划 - 家庭和办公室75
    节目 - 家庭和办公室(数字)76
    课程 - 教育77
    课程 - 教育(图)78
    服务79
    实用程序 - 门票80
    纯粹的载体(尖顶)81
    纯载体(片)82
    电池83

    3、项目要求

    3、1 评估的要求

      (1) 将均方根误差(RMSE)作为评估提交的度量指标;
      (2) 真实销售量被限制在[0,20]范围以内。

    3、2 提交文件格式

      该项目要求预测下个月(第34个月或2015年11月)各个商店的每个产品的销售额。即对于测试集中的每个ID(唯一映射到shop_id与item_id),预测出其下一个月的销售总数。提交的文件格式如下所示:

    IDitem_cnt_month
    00.5
    10.5
    20.5
    30.5

      PS:最新优化没有同步到博客当中,需要交流的可以邮箱交流:博主邮箱:greatpanc@163.com

    展开全文
  • 预测10家商店未来三个月50种商品销售量 一.前言 目前拥有10家店50种商品过去5年内的销售量,尝试通过建立ARIMAL,回归,GBDT模型来预测未来一年的销量 时间序列提供了预测未来价值的机会。 基于以前的价值观,可以...
  • Kaggle比赛——预测未来销售(三)

    千次阅读 2018-12-02 22:58:49
    预测未来销售——模型选择 未完待续…
  • Kaggle产品销售预测比赛优胜方案
  • 数据挖掘——预测未来销售处理sale_train_v2.csv和test.csv处理shop.csv处理item_categories.csv处理items.csv特征添加 该项目来自kaggle比赛, 处理sale_train_v2.csv和test.csv 1、读取训练数据: test = pd....
  • Kaggle比赛——预测未来销售(二)

    千次阅读 2018-12-01 11:26:43
    预测未来销售(二)——数据预处理与特征提取1、初始化环境2、数据读取过程2.1 销售数据2.1.1 销售数据读取2.1.2 统计不同ID(shop_id、item_id)下的月销量2.1.3 汇入item_categroy_id属性2.1.4 修正后的item_cat_...
  • 销售预测

    万次阅读 多人点赞 2019-01-11 16:56:51
    这就引出了我的主题,《机器学习对销售预测的研究》。 机器学习是常用的日常分析的方法,另一方面机器学习在海量数据中挖掘其中的规律效果非常好。 首先,说说,销售预测的现状和痛点。销售只是一个商业问题,要做...
  • 商品销量预测介绍 一、商品销量预测: 在充分考虑未来各种影响因素的基础上,根据历史销量以及市场上对产品需求的变化情况,对未来一定时期内产品的产品销量变化所进行的科学预计和推测。 二、商品销量预测三大特点...
  • 在数据科学学习之旅中,我经常处理日常工作中的时间序列数据集,并据此做出预测
  • # 本次比赛目标:通过时间序列模型,预测接下来一个月,俄罗斯某商超集团每件商品在各个商店的总销售额。 # 听说 Eviews 和 MATLAB 是经济金融界的利器,特别是在时间序列方面,二者优势得天独厚。 # 我估摸着,就...
  • LSTM时间序列预测销量

    2018-12-26 19:23:17
    本代码采用python语言写的一个LSTM时间序列来预测销量
  • 商品销量预测是什么?

    千次阅读 2019-01-14 11:28:18
    商品销量预测 在充分考虑未来各种影响因素的基础上,根据历史销量以及市场上对产品需求的变化情况情况,对未来一定时期内产品的销量变化所进行的科学预计和推测。 产品销量预测有三个特点 连贯性 第一,预测具有一定...
  • 单品类商品销售预测项目(基于GBDT)

    千次阅读 2020-02-07 21:50:24
    单品类商品销售预测项目(基于GBDT) 一.项目背景及挖掘目标: 商品销售预测几乎是每个运营部门的必备数据支持项目,无论是大型促销活动,还是单品营销.本案例是依据历史销售数据,对某单品的销售预测. 二.项目数据介绍: 1...
  • PS : 参加比赛的前提是有kaggle账号,我注册账号的时候不是一帆风顺的,遇到了很多人都遇到的验证码收不到的问题,解决方法据我所知就只有在上网的时候要“科学”,而且这个问题在上传预测数据的时候也会遇到...
  • 明确定义所要解决的问题——网店销售额的预测 在数据的收集与预处理环节,分五个环节完成数据的预处理工作,分别如下 (1)收集数据— 需要提供的网店的相关记录 (2)将收集到的数据可视化,显示出来看一看 (3)做...
  • 数据挖掘入门与实战 公众号: datadw .../forum/postdetail/59194c685d9...调查发现,在出行产品业务中,不同区域的产品需求量级不一样,不同时段需求会有高低起伏,相同区域相同时段各产品的需求因产品特性不同又...
  • 七、基于机器学习方法对销售预测的研究

    万次阅读 多人点赞 2018-06-19 16:59:39
    基于机器学习方法对销售预测的研究 在开始今天的分享之前,我首先跟大家简单的聊一下,刚刚过去的双十一,大家可能更关心的是双十一的折扣,什么商品打了什么折扣。但是对于天猫而言,他们可能更关心的是双十一当天...
  • 预测未来销售该项目来源于kaggle中的一场比赛的赛题,数据是由日常销售数据组成的时间序列数据集,该数据集由俄罗斯最大的软件公司之一 - 1C公司提供。提供了包括商店,商品,价格,日销量等连续34个月内的数据,...
  • price:单品价格,整数型变量,代码商品在不同阶段的实际销售价格。 discount_rate:折扣率,浮点型变量,值域[0,1],值越大代表折扣力度越大。 hour_resouces:在促销活动中展示的小时数,整数型变量,值越大...
  • 使用时间序列分解模型预测商品销量 1.商品销量预测:  连贯性:把过去,现在,未来的发展联系起来,通过过去和现在的数据推导出将来的变化  相关性:从宏观上讲,市场需求和国家经济状况,家庭收入水平,消费需求...
  • Excel数据分析案例二——预测销售

    千次阅读 2021-09-08 18:49:47
    Excel数据分析案例二——预测销售额①算术平均法②加权平均法③移动平均法④加权移动平均法⑤回归预测法⑥业务分析法总结 题目:现有某商场2020年1-9月实际销售额数据,需要预测10月销售额数据,以便制定10月目标,...
  • 比如,在这堆数据中,CoorChice 希望通过机器学习训练一个模型,能够用于预测未来一定条件下的某种类型的面包所能达到的交易是多少。 因此,我们将交易 Transaction 这一列数据作为数据集的标签数据。 接...
  • 在我的工作中,有一部分内容要涉及到年销售预测并制定相应的订铺补计划 当然,相信很多涉及到销售行业的都会有这方面的需要,根据预测值制定分解任务指标 简单的归纳下: 对数据进行处理回正:数据并不是拿到手就...
  • 销量预测简单模型

    千次阅读 2021-04-20 20:54:40
    销量预测常用简单模型总结,基于python和Spark.SQL
  • 软件测试面试题汇总

    万次阅读 多人点赞 2018-09-27 12:31:09
    转载自: ... 软件测试面试题汇总 测试技术面试题 ...........................................................................................................
  • 数学建模之预测模型总结

    万次阅读 多人点赞 2018-01-29 19:51:56
    基于数学建模的预测方法种类繁多,从经典的单耗法、弹性系数法、统计分析法,到目前的灰色预测法。当在使用相应的预测方法建立预测模型时,我们需要知道主要的一些预测方法的研究特点,优缺点和适用范围。下面就当下...
  • 亿元) 图表45:2021年中国挖掘机制造行业产品国内消费市场结构(单位:%) 图表46:2021年中国挖掘机市场区域分布情况(单位:台,%) 图表47:2021年中国挖掘机制造行业前二十地区销售量排名情况(单位:台) 图表...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,023
精华内容 3,609
关键字:

预测商品未来销售量