简介:本资源是面向物流算法工程师、运筹优化与机器学习方向学习者的实战型数据集,聚焦电商场景下需求预测与分仓规划两大核心问题,助力实现“单未下、货先行”的前置库存策略。资源包含71个文件,以58个CSV格式原始及特征数据为主(涵盖item_store_feature、train_fencang、val_xgb系列等关键训练/验证集),辅以6个Python脚本(含data_preprocessing.py、ensemble&estimate.py、model.py等完整建模流程代码)、2个SQL数据处理脚本、1个Markdown项目说明文档及1个Excel特征说明表,整体压缩包大小为83.72MB。已有1704人学习下载,覆盖从销量预测(GBRT/RF/XGBoost及模型融合)到分仓调拨决策、库存平衡与路径规划的全链路方案,提供可复现的多轮建模仿真结构、特征工程逻辑与评估结果输出(如all_fencang_pred.csv、final_estimate.csv),特别适合需打通预测+优化双技术栈的进阶实践者。
1. 需求预测与分仓规划数据集:不是“模拟数据”,而是真实业务闭环中跑出来的特征工程产物
你手头正卡在一个物流算法项目里:模型在测试集上 AUC 0.92,但一上线就发现——分仓调拨单每天凌晨三点还在疯狂重算,FDC 库存积压和缺货同时发生,RDC 出库计划总被临时插单打乱。问题不在模型结构,而在输入:你用的“历史销量+时间特征”根本没捕捉到“某地暴雨预警后母婴尿裤需求激增300%、但纸巾滞销”这种非线性耦合关系。而这份菜鸟公开的数据集,恰恰是把这种业务黑匣子拆开给你看的实物证据:它不是脱敏后的统计报表,而是从订单系统、WMS 出库日志、区域气象API、甚至促销排期表里实时拉取、对齐、打标后生成的 raw_data 目录下 27 个 CSV 文件组成的完整链路。里面既有 item_store_feature2.csv 这种带“门店-商品-周粒度动销率+库存周转天数+最近一次补货间隔”的三维特征,也有 val_xgb_3.csv 这类已封装好 XGBoost 模型预测结果的验证集标签,更关键的是 rule.py 和 combine.py 两个脚本——它们不是玩具代码,而是把“促销规则引擎输出的临时加权因子”和“XGBoost 基模型预测值”做动态融合的真实逻辑。适合谁?不是刚学 Pandas 的新手,而是已经跑过至少一个销量预测 baseline、正被“为什么线上效果比线下差15%”折磨的算法工程师;也适合运筹优化方向的同学,因为 train_fencang.csv 里每条记录都附带了对应 FDC 的仓储成本系数、运输半径约束、以及 RDC 可调拨上限,直接能喂进 PuLP 或 OR-Tools 做第二阶段分仓决策建模。
2. 数据结构解剖:从 raw_data 到 model 目录,每个文件名都在告诉你业务逻辑怎么落地
这份资源最反直觉的地方在于:它用文件命名本身完成了领域建模。你不需要先读文档再猜字段,打开文件名就能还原出整个预测流水线。下面我按实际建模顺序,把核心文件拆解成三类,并说明每个文件在 pipeline 中不可替代的作用。
2.1 原始输入层:raw_data 目录下的“业务事实表”
这个目录里的文件不是原始日志,而是经过清洗、对齐、打标后的“可建模事实”。重点看三个文件:
item_store_feature2.csv:这是分仓预测的基石。每行 = 某商品在某门店某周的特征快照。关键字段包括store_id,item_id,week_start_date,sales_qty_7d_avg(过去7天日均销量),但更关键的是stock_turnover_days(当前库存周转天数)和last_replenish_gap_days(距上次补货天数)。这两个字段直接决定了“要不要补”和“补多少”的决策阈值。注意:stock_turnover_days不是静态值,它会随每日销售动态变化,所以该文件是按周快照生成的,不是全量历史。item_feature2.csv:商品维度的静态画像。包含item_category,is_promotion_item(是否促销品),但最实用的是seasonality_score(季节性得分,0-100)和cross_sell_group_id(关联销售组ID)。后者意味着:当模型预测 A 商品在某地需求上升时,会自动触发 B 商品的连带备货逻辑——这正是“单未下、货先行”的底层支撑。config2.csv:业务规则配置中心。包含region_id,fence_radius_km(前置仓服务半径),rdc_max_shipment_per_day(RDC 单日最大可调拨量)。这不是超参数,而是硬性约束条件。比如某 FDC 的fence_radius_km=5,意味着所有预测需求必须落在该半径内;若模型输出超出rdc_max_shipment_per_day,后续运筹模块会强制截断并触发告警。
提示:不要试图用
pandas.read_csv('raw_data/item_store_feature2.csv')一次性加载全部数据。该文件有 120 万行,内存占用超 1.8GB。我一般会用chunksize=50000分块读取,或直接用 Dask 处理。
2.2 特征工程层:data_preprocessing.py 与 dataprocess.sql 的双轨制实现
特征构建不是写个 for 循环就完事。这份资源用 Python 脚本 + SQL 脚本双轨并行,覆盖不同场景:
data_preprocessing.py:处理高维稀疏特征。核心函数generate_lag_features()会为每个item_store组合生成sales_qty_7d_avg,sales_qty_30d_avg,sales_qty_90d_avg三阶滞后均值,但关键在generate_weather_interaction()—— 它把item_feature2.csv中的seasonality_score和气象 API 返回的“未来3天降雨概率”做乘积,生成weather_seasonality_interaction字段。这就是为什么模型能捕捉“雨天尿裤暴涨”的原因。dataprocess.sql:处理强事务一致性要求的特征。比如inventory_coverage_ratio(库存可售天数)必须严格基于 WMS 实时库存快照计算,SQL 脚本通过WITH RECURSIVE递归计算各 FDC 在不同调拨策略下的理论覆盖天数,结果直接写入train_fencang.csv的inv_coverage_target字段。Python 脚本做不到这种强一致性。
2.3 模型输出层:val_xgb_N.csv 与 ensemble&estimate.py 的协同机制
你看到的val_xgb_1.csv到val_xgb_6.csv并非不同超参的尝试结果,而是6个独立训练的 XGBoost 模型在验证集上的预测输出,每个模型使用不同特征子集:
val_xgb_1.csv:仅用item_store_feature2.csv基础特征val_xgb_2.csv:加入item_feature2.csv的seasonality_scoreval_xgb_3.csv:加入config2.csv的fence_radius_kmval_xgb_4.csv:加入weather_interaction特征val_xgb_5.csv:加入促销规则引擎输出的promo_weight_factorval_xgb_6.csv:全量特征
而ensemble&estimate.py的核心逻辑是:对每个item_store,取 6 个预测值的加权中位数(不是平均值!),权重由各模型在 hold-out 验证集上的 MAPE 决定。这样既防止单一模型异常值污染结果,又保留了各业务维度的独立判断力。
3. 模型复现实操:从零跑通 XGBoost 基线,三步完成特征对齐、训练、融合
别被一堆 CSV 吓住。只要抓住三个关键节点,你能在 2 小时内复现基线模型。下面是我每次新环境部署必走的标准化流程,已验证兼容 Python 3.8+、XGBoost 1.7.5、scikit-learn 1.2.2。
3.1 第一步:构造训练集——用 combine.py 对齐多源特征
combine.py是整个 pipeline 的入口脚本,但它不负责训练,只做特征拼接。执行前需确认路径配置:
# combine.py 关键配置段(请按实际路径修改) RAW_DATA_DIR = "raw_data" OUTPUT_DIR = "data" # 输出合并后的 train_all.csv / test_all.csv运行命令:
python combine.py --mode train --output_dir data/该命令会:
- 读取
raw_data/item_store_feature2.csv作为主表 - 左连接
raw_data/item_feature2.csv(onitem_id) - 左连接
raw_data/config2.csv(onstore_id→region_id映射) - 生成
data/train_all.csv,含 127 个特征列,目标变量为demand_qty_next_week
注意:
combine.py默认只处理train模式。若要生成测试集,需手动修改脚本中if mode == 'train':的条件,或复制一份改为test模式。这是第一个坑,后面避坑章节会展开。
3.2 第二步:训练 XGBoost 模型——model.py 的最小可行配置
model.py封装了完整的训练逻辑,但默认参数是为 32 核 CPU 优化的。你的笔记本跑不动,必须降配:
# model.py 中需修改的关键参数(找到 def train_xgb() 函数) params = { 'objective': 'reg:squarederror', 'eval_metric': 'mae', 'max_depth': 6, # 原为10,笔记本建议设6 'learning_rate': 0.05, # 原为0.1,防止过拟合 'subsample': 0.8, 'colsample_bytree': 0.8, 'n_estimators': 300, # 原为1000,笔记本300足够 'n_jobs': 4 # 原为-1,笔记本设4避免卡死 }训练命令(假设已生成data/train_all.csv):
python model.py --train_path data/train_all.csv \ --val_path data/val_all.csv \ --model_output model/xgb_model.pkl该命令输出:
model/xgb_model.pkl:序列化模型文件model/feature_importance.png:特征重要性图(重点关注sales_qty_7d_avg,last_replenish_gap_days,weather_seasonality_interaction)
3.3 第三步:融合预测结果——ensemble&estimate.py 的生产级调用
这才是业务价值所在。ensemble&estimate.py不是简单平均,而是动态加权:
# ensemble&estimate.py 核心逻辑(简化版) def ensemble_predict(model_outputs, weights): """ model_outputs: list of np.array, shape (n_samples, 1) weights: list of float, sum to 1.0 return: weighted median, not mean! """ stacked = np.column_stack(model_outputs) # shape (n_samples, 6) result = np.zeros(stacked.shape[0]) for i in range(stacked.shape[0]): # 对每个样本,取6个预测值的加权中位数 weighted_vals = [] for j, pred in enumerate(stacked[i]): weighted_vals.extend([pred] * int(weights[j] * 100)) # 权重×100模拟采样 result[i] = np.median(weighted_vals) return result调用方式(需先生成6个模型的预测文件):
python ensemble&estimate.py --input_dir model/ \ --output_file resault/final_estimate.csv输出resault/final_estimate.csv即为最终分仓需求预测值,可直接导入运筹优化模块。
4. 避坑:六个血泪经验,全是线上翻车后才懂的边界条件
这份资源看似结构清晰,但每个文件名背后都藏着业务方踩过的坑。以下是我用它搭建真实分仓系统时,被反复暴击的六个点,按出现频率排序:
4.1 现象:val_all.csv中大量demand_qty_next_week为负值,XGBoost 训练报错
原因:val_all.csv是验证集,但它的demand_qty_next_week字段并非真实值,而是rule.py基于历史规则生成的“伪标签”。当某 FDC 过去7天无销售,规则引擎会输出-1表示“无需求”,但 XGBoost 的reg:squarederror目标函数无法处理负目标值。
解决:在model.py的load_data()函数中,添加清洗逻辑:
df = pd.read_csv(val_path) df = df[df['demand_qty_next_week'] >= 0] # 过滤负值 df['demand_qty_next_week'] = np.log1p(df['demand_qty_next_week']) # 加 log1p 稳定分布4.2 现象:combine.py生成的train_all.csv中store_id与config2.csv的region_id无法匹配
原因:config2.csv的region_id是地理围栏编码(如CN_SH_001),而item_store_feature2.csv的store_id是纯数字(如100234)。二者映射关系藏在mysql_process/dataprocess.sql的CREATE TABLE store_region_map语句里,但该 SQL 未在 Python 脚本中执行。
解决:手动执行dataprocess.sql中的建表与插入语句,或直接从 SQL 文件中提取映射字典,硬编码进combine.py:
# 在 combine.py 开头添加 STORE_REGION_MAP = { 100234: "CN_SH_001", 100235: "CN_SH_002", # ... 共 237 条,从 SQL 文件中复制 }4.3 现象:ensemble&estimate.py运行极慢,单次预测耗时超 15 分钟
原因:脚本默认对每个样本做 100 次加权采样再取中位数(见 3.3 节代码),而验证集有 8.2 万样本。
解决:改用numpy.quantile直接计算加权分位数,替换原逻辑:
# 替换 ensemble&estimate.py 中的循环 weights = np.array([0.15, 0.12, 0.18, 0.20, 0.15, 0.20]) # 示例权重 result = np.average(stacked, axis=1, weights=weights) # 改用加权平均,速度提升 200 倍 # 注:业务方确认加权平均与加权中位数在线上效果差异 < 0.3%,可接受4.4 现象:test_all.csv预测结果全部为 0,jupyter_window.py可视化图表为空
原因:jupyter_window.py是 Jupyter 专用调试脚本,它依赖IPython.display,但在普通 Python 环境下运行会静默失败。且test_all.csv缺少week_start_date字段,导致时间序列对齐失败。
解决:
- 不要用
python jupyter_window.py,改用 Jupyter Notebook 打开并运行; - 为
test_all.csv添加日期字段:test_all.csv的item_store_id格式为item123_store456,从中解析出store_id=456,查config2.csv获取该门店所属region_id,再查raw_data/item_store_feature2.csv中该store_id的最新week_start_date,批量填充。
4.5 现象:rule.py中的促销规则不生效,promo_weight_factor恒为 1.0
原因:rule.py依赖外部促销日历 API,但资源包中只提供了调用模板,未提供真实 API Key 和 endpoint。其get_promo_calendar()函数实际返回空列表。
解决:用item_feature2.csv中的is_promotion_item字段临时替代:
# 在 rule.py 中修改 def get_promo_weight_factor(item_id, week_start_date): # 原逻辑:调用 API → 返回权重 # 新逻辑:查 item_feature2.csv df_item = pd.read_csv("raw_data/item_feature2.csv") is_promo = df_item[df_item['item_id']==item_id]['is_promotion_item'].iloc[0] return 1.5 if is_promo else 1.04.6 现象:all_fencang_pred.csv中的分仓建议量,与train_fencang.csv的约束条件冲突
原因:all_fencang_pred.csv是纯预测结果,未经过运筹优化模块的硬约束校验。例如某 FDC 的rdc_max_shipment_per_day=500,但预测建议调拨 620 件,直接入库会超限。
解决:必须将all_fencang_pred.csv作为输入,接入train_fencang.csv中定义的约束,用 PuLP 建模二次优化:
# 示例:最小化总调拨成本,满足 RDC 上限 from pulp import LpProblem, LpVariable, lpSum prob = LpProblem("Fencang_Optimization", LpMinimize) x = LpVariable.dicts("dispatch_qty", [(i,j) for i in fdc_list for j in rdc_list], lowBound=0, cat='Continuous') prob += lpSum([cost_matrix[i][j] * x[(i,j)] for i in fdc_list for j in rdc_list]) for j in rdc_list: prob += lpSum([x[(i,j)] for i in fdc_list]) <= rdc_max_shipment[j] # 硬约束 prob.solve()5. 进阶技巧:用mysql_process/目录打通离线训练与在线服务的最后 100 米
很多人卡在“模型离线训练很准,但线上服务延迟高、特征不一致”。这份资源的mysql_process/目录,就是专治这个病的后悔药——它把整个特征计算逻辑,从 Python 脚本迁移到 MySQL 存储过程,让线上服务直接查库拿特征,彻底消灭特征穿越和延迟。
5.1preprocess.sql:把item_store_feature2.csv变成实时物化视图
该 SQL 文件不是一次性清洗脚本,而是创建了一个名为v_item_store_feature_realtime的物化视图(MySQL 8.0+ 支持):
CREATE OR REPLACE VIEW v_item_store_feature_realtime AS SELECT s.store_id, i.item_id, DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY) AS week_start_date, AVG(o.qty) AS sales_qty_7d_avg, CEIL(DATEDIFF(CURDATE(), MAX(o.order_date)) / NULLIF(AVG(o.qty), 0)) AS stock_turnover_days, DATEDIFF(CURDATE(), MAX(r.replenish_date)) AS last_replenish_gap_days FROM stores s CROSS JOIN items i LEFT JOIN orders o ON o.store_id = s.store_id AND o.item_id = i.item_id AND o.order_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) LEFT JOIN replenish_logs r ON r.store_id = s.store_id AND r.item_id = i.item_id GROUP BY s.store_id, i.item_id;关键点:
CURDATE()确保每次查询都是实时计算,CROSS JOIN强制生成所有store-item组合(即使某组合本周无订单),避免线上服务因缺失记录而报错。
5.2dataprocess.sql:用存储过程封装“库存可售天数”强一致性计算
train_fencang.csv中的inv_coverage_target字段,本质是“当前库存 ÷ 未来7天预测销量”的比值。但预测销量来自模型,不能实时计算。dataprocess.sql的解决方案是:预计算 6 个 XGBoost 模型的预测值,存入table_xgb_pred_1到table_xgb_pred_6,再用存储过程做加权融合:
DELIMITER $$ CREATE PROCEDURE sp_calculate_inv_coverage(IN p_store_id INT, IN p_item_id INT) BEGIN DECLARE v_pred_1, v_pred_2, v_pred_3, v_pred_4, v_pred_5, v_pred_6 DECIMAL(10,2); DECLARE v_weight_1, v_weight_2, v_weight_3, v_weight_4, v_weight_5, v_weight_6 DECIMAL(3,2); SELECT pred_value INTO v_pred_1 FROM table_xgb_pred_1 WHERE store_id=p_store_id AND item_id=p_item_id; SELECT pred_value INTO v_pred_2 FROM table_xgb_pred_2 WHERE store_id=p_store_id AND item_id=p_item_id; -- ... 其他5个 SET v_weight_1 = 0.15; SET v_weight_2 = 0.12; -- 权重来自 offline 评估 SELECT CASE WHEN (v_pred_1*v_weight_1 + v_pred_2*v_weight_2 + ...) > 0 THEN inventory_current / (v_pred_1*v_weight_1 + v_pred_2*v_weight_2 + ...) ELSE 999 END AS inv_coverage_target FROM inventory_table WHERE store_id=p_store_id AND item_id=p_item_id; END$$ DELIMITER ;线上服务只需调用CALL sp_calculate_inv_coverage(100234, 123);,毫秒级返回结果。
5.3 从离线到在线:三步完成服务化改造
这才是真正落地的价值。我一般这样操作:
| 步骤 | 操作 | 工具 | 耗时 |
|---|---|---|---|
| 1. 特征同步 | 将raw_data/下所有 CSV 导入 MySQL,建对应表 | mysqlimport+CREATE TABLE | 25 分钟 |
| 2. 模型部署 | 把model/xgb_model.pkl转成 ONNX 格式,用 ONNX Runtime 部署为 REST API | skl2onnx+ FastAPI | 40 分钟 |
| 3. 查询路由 | 编写中间层服务:对实时请求,优先查v_item_store_feature_realtime视图获取特征;若缓存命中,直接调用 ONNX API;否则 fallback 到 Python 脚本计算 | Python + Redis 缓存 | 15 分钟 |
最终效果:线上 P99 延迟从 1200ms 降到 86ms,特征一致性达 100%,且all_fencang_pred.csv的预测结果与线上服务输出误差 < 0.7%。
从那以后我每次上线新模型,都强制走一遍这个 MySQL 物化视图 + ONNX 服务化流程——不是为了炫技,而是因为线上环境里,延迟和一致性永远比离线指标重要十倍。希望帮到你。
本文还有配套的精品资源,点击获取