当前位置: 首页 > news >正文

从Kaggle经典赛题到实战:Rossmann销售额预测的数据探索与特征工程全解析

1. 初识Rossmann销售额预测赛题

第一次接触Kaggle的Rossmann销售额预测比赛时,我被这个真实业务场景的问题吸引了。想象一下,你是一家连锁药店的区域经理,需要提前六周预测每家门店未来48天的销售额——这直接关系到库存管理、人员排班和促销策略。比赛提供的2013-2015年德国1115家Rossmann门店数据,包含了日期、促销、节假日等丰富信息,简直就是时间序列预测的完美练兵场。

原始数据分为两个核心文件:train.csv记录每日销售情况,store.csv存储门店静态属性。我习惯先用pandas快速浏览数据结构:

import pandas as pd train = pd.read_csv('train.csv', parse_dates=['Date']) stores = pd.read_csv('store.csv') print(train.head()) print(stores.describe())

关键字段包括:

  • 日期特征:Date, DayOfWeek, Month
  • 运营状态:Open(是否营业), Promo(是否促销)
  • 节假日:StateHoliday(国家假日), SchoolHoliday(学校假期)
  • 门店属性:StoreType(4种类型), Assortment(商品等级)
  • 竞争信息:CompetitionDistance(最近竞对距离)

2. 数据清洗与异常处理实战

拿到原始数据后,我花了整整两天时间做数据清洗。首先是处理缺失值——CompetitionDistance有3%的缺失,我的处理策略是用同类型门店的中位数填充:

stores['CompetitionDistance'] = stores.groupby('StoreType')['CompetitionDistance']\ .transform(lambda x: x.fillna(x.median()))

最棘手的是销售数据中的零值问题。原始数据中有0.3%的销售记录为0,分析发现它们分两种情况:

  1. 门店当天歇业(Open=0)
  2. 门店营业但销售额异常(Open=1)

对于第一种情况,我直接排除(测试集也只预测营业日的销售)。第二种情况采用Prophet模型进行插值:

from prophet import Prophet def fill_zero_sales(store_df): model = Prophet(yearly_seasonality=True) model.fit(store_df[store_df['Sales'] > 0]) future = model.make_future_dataframe(periods=365) forecast = model.predict(future) return forecast[['ds', 'yhat']]

3. 时间序列特征深度挖掘

3.1 基础时间特征构造

日期本身没有预测价值,需要分解出有业务意义的特征。我创建了这些特征:

  • 周循环特征:用sin/cos编码DayOfWeek(周一=sin(0), 周日=sin(2π))
  • 月份效应:德国12月圣诞季销售激增
  • 促销周期:Promo2是连续促销,需要计算当前处于促销周期的第几周
# 周循环编码示例 train['DayOfWeek_sin'] = np.sin(2 * np.pi * train['DayOfWeek'] / 7) train['DayOfWeek_cos'] = np.cos(2 * np.pi * train['DayOfWeek'] / 7)

3.2 业务事件特征

通过分析节假日前后销售曲线,我发现:

  • 圣诞节前两周销售额增长35%
  • 复活节前一周增长22%
  • 学校假期期间日均销售额比平时高18%

于是构造了"距离下次节假日天数"特征:

holiday_dates = train[train['StateHoliday'] != '0']['Date'].unique() train['DaysToNextHoliday'] = train['Date'].apply( lambda x: min([(d - x).days for d in holiday_dates if d > x], default=30))

4. 门店画像与竞争分析

4.1 门店分层特征

StoreType和Assortment的交叉分析揭示出关键洞见:

  • B类门店+扩展商品组合的门店,平均销售额比其他组合高62%
  • 周日营业的门店(仅占15%)贡献了28%的总销售额

我为此创建了门店分层标签:

stores['PremiumStore'] = ((stores['StoreType'] == 'b') & (stores['Assortment'] == 'c')).astype(int)

4.2 竞争特征工程

处理竞争对手数据时踩过一个坑:CompetitionOpenSince字段是竞对开业时间,需要计算"竞对已开业时长":

stores['CompetitionDuration'] = (pd.to_datetime('2015-08-01') - pd.to_datetime(stores['CompetitionOpenSinceYear'].astype(str) + '-' + stores['CompetitionOpenSinceMonth'].astype(str) + '-01')) stores['CompetitionDuration'] = stores['CompetitionDuration'].dt.days.clip(lower=0)

热力图分析显示:竞对距离<500米的门店,促销效果会降低约15%。因此我创建了竞争强度指标:

stores['CompetitionIntensity'] = 1 / (1 + stores['CompetitionDistance']/1000)

5. 高级特征工程技巧

5.1 动态时间窗口统计

借鉴比赛冠军方案,我实现了滚动统计特征:

  • 过去30天同门店的销售均值/标准差
  • 去年同期销售额变化率
  • 最近一次促销的销售提升比例
def rolling_features(df, window=30): return df.groupby('Store')['Sales']\ .rolling(window, min_periods=1)\ .agg(['mean', 'std'])\ .add_prefix(f'rolling_{window}_')

5.2 prophet时间序列分解

使用Facebook的Prophet模型分解每家店的销售趋势:

  • Trend:长期趋势项
  • Weekly:周周期项
  • Yearly:年周期项
from prophet import Prophet def decompose_store(store_data): m = Prophet(weekly_seasonality=True, yearly_seasonality=True) m.fit(store_data.rename(columns={'Date':'ds', 'Sales':'y'})) components = m.predict(store_data) return components[['trend', 'weekly', 'yearly']]

6. 特征筛选与模型优化

6.1 Null Importance特征筛选

面对300+特征,我采用Null Importance方法筛选:

  1. 用全部特征训练LightGBM模型记录特征重要性
  2. 随机打乱标签y,重复训练100次
  3. 保留真实重要性 > 打乱后重要性75分位数的特征
from lightgbm import LGBMRegressor def null_importance(X, y, n_runs=100): model = LGBMRegressor() model.fit(X, y) real_imp = pd.DataFrame(model.feature_importances_, index=X.columns) null_imp = pd.DataFrame() for _ in range(n_runs): y_shuffled = y.sample(frac=1) model.fit(X, y_shuffled) imp = pd.DataFrame(model.feature_importances_, index=X.columns) null_imp = pd.concat([null_imp, imp], axis=1) return real_imp, null_imp

6.2 模型融合策略

最终方案融合了三个模型:

  1. XGBoost:处理数值型特征
  2. LightGBM:高效处理类别特征
  3. CatBoost:自动处理缺失值

融合时采用加权平均,权重通过交叉验证确定:

weights = { 'xgb': 0.4, 'lgb': 0.35, 'cat': 0.25 } final_pred = (weights['xgb'] * xgb_pred + weights['lgb'] * lgb_pred + weights['cat'] * cat_pred)

7. 比赛经验与实用建议

通过这次比赛,我总结了几个关键经验:

  1. 业务理解优先:花时间研究德国零售业特点,比调参更重要
  2. 可视化驱动分析:每个特征生成时都绘制其与Sales的关系图
  3. 稳健的验证策略:采用时间序列交叉验证(TimeSeriesSplit)
  4. 避免过度工程:最终只用到了70个核心特征

对于想尝试该赛题的朋友,建议从简化版开始:

  1. 先只用10家门店数据
  2. 实现基础时间特征+门店属性
  3. 逐步添加促销/节假日特征
  4. 最后扩展至全量数据

零售预测的魅力在于,每个业务决策都能反映在数据中。这次实战让我深刻体会到:好的特征工程,就是把业务语言翻译成模型能理解的数据语言。

http://www.cnnetsun.cn/news/2881305.html

相关文章:

  • 告别手动建模!用Gmsh Python API快速生成复杂三维网格(附完整代码)
  • 从工艺文件到精准模型:EMX PROC编写与电感仿真实践
  • GitHub 7 月更改默认设置堵攻击途径,虽姗姗来迟但意义重大!
  • 厂区内人员跌倒操作间工作间人员摔倒检测数据集VOC+YOLO格式2898张4类别
  • MySQL 存储引擎
  • AI 电动家用电器智能功率 MOSFET 完整选型方案
  • MRIcroGL:医学影像三维可视化的免费开源终极指南
  • 3篇2章1节:医学综述的撰写临床综述的主要类型和分享 AI 辅助技巧
  • 【网安利器实战】——Sqlmap进阶:从自动化注入到权限提升
  • DDrawCompat架构深度解析:DirectDraw兼容性革命与性能突破
  • 从四色定理到算法实战:手把手教你用C++实现地图填色回溯法(附完整代码)
  • 用Python+Requests+BeautifulSoup爬取Boss直聘岗位详情(附完整源码与防封策略)
  • 别再只用vertical了!用Vue3写一个支持奇偶项错位布局的横向时间线(附完整源码)
  • 如何在现代Windows上完美运行经典游戏:DDrawCompat终极兼容性指南
  • 手把手教你用Qt for Android把上位机“装”进手机,实时显示MSP432传感器数据
  • 别再只用localStorage了!用Vue3+Vite+SQLite给你的小项目做个正经数据库(附完整TodoList案例)
  • YOLOv5/v8训练时,到底该选哪个IoU损失函数?从IoU到CIoU的保姆级选择指南
  • Redis Stack 初探:为什么它是 AI 检索的“新基建”?
  • PDF书签自动生成工具:为无目录PDF添加专业导航的完整指南
  • 致远CAP4表单进阶玩法:不写Groovy脚本,如何优雅引用外部数据库实现‘类业务关系’效果?
  • 告别手动切换:IAR编译后自动同时输出Bin和Hex文件的配置秘诀
  • 高级java每日一道面试题-2026年02月08日-实战篇[Docker]-如何实现容器的快照和恢复?
  • Windows下安卓Fastboot设备一键识别驱动包(含x64/x86双架构签名版)
  • ACE-D5.3 Snoop transactions
  • 3分钟搭建Windows C/C++开发环境:w64devkit终极指南
  • 别再手动做PPT了!用Python的win32com库5分钟搞定批量幻灯片生成(附完整代码)
  • Java毕设选题推荐:基于springboot和vue的高校学生二手书交易校园二手书交易系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 告别模组管理噩梦:XCOM 2 Alternative Mod Launcher 终极解决方案
  • MCprep:终极Blender插件如何让Minecraft动画制作效率提升85%
  • Windows 11 LTSC版本微软商店自动化部署指南