Python实现小超市供销存优化:数据清洗到补货策略全解析

Python实现小超市供销存优化:数据清洗到补货策略全解析 简介这是2017年BDCI“小超市供销存管理优化”赛题的Python源代码资源面向希望掌握机器学习在库存与销售预测场景落地方法的开发者、数据竞赛选手与相关专业学生。资源包为zip压缩格式共20个文件包含11个csv数据文件、5个Python脚本model1.py、model2.py、model3.py及output.py等、2个docx项目文档、1个pptx汇报材料和1个txt运行顺序说明整体大小仅1.22MB轻量但内容完整。已有308人学习下载。从赛题描述看资源覆盖了特征提取、pandas数据清洗、scikit-learn模型训练与评估、可视化模型解释、预测补货优化等完整流程目录将数据与程序分离配套运行顺序说明便于对照项目报告一步步复盘。无论是备战数据挖掘竞赛还是学习真实业务中的预测建模都能从中获得从原始数据到最终结论的完整示范是一套兼具教学与参考价值的实战资源。 2017年BDCI赛题里有一个特别接地气的题目——小超市供销存管理优化。第一次看到这个题我第一反应是这哪是比赛这不就是把我楼下小卖部老板每天挠头的问题搬上考场了吗一家小超市几十种商品每天都有销售流水供应商给的供货价格和到货周期还不一样怎么订货、订多少、多久订一次直接决定月底是赚钱还是压了一堆卖不动的货。这道题赛方给了大量历史销售数据和供应商信息要求选手设计一套补货、库存管理方案让超市在满足顾客需求的前提下尽可能提高利润。我这次写的是一个基于Python的完整复现版本从数据清洗、库存模拟到策略求值全部在一个工程里完成纯Python源码不依赖额外的商用求解器。文章适合正在学Python数据分析、对供应链优化感兴趣的人参考也适合竞赛党直接拿来当baseline跑通流程。这里先总结一下这套源码的整体能力它能读入销售记录和供应商表自动聚合出每日销量模拟不同补货策略下的库存变化最终输出一份按商品、按日期的订货计划并给出对应的利润、缺货天数、期末库存等指标。下面我把整个项目的思路、建模过程和代码实现完整拆开讲。1. 赛题复盘先搞清楚“供销存”到底要算什么1.1 赛题背景与目标重述小超市的供销存问题核心是三件事供应、销售和库存。“供应”指从哪家供应商、以什么价格、多长时间到一批货“销售”指每天每个商品卖出去多少“库存”则是这两者之间的缓冲既能留住顾客又不能压太多资金。赛题给的正是这三块原始数据但原始数据不会告诉你什么叫“优化”这个定义需要参赛者自己完成。我复现时把目标定义成“最大化经营净利润”这是最贴近门店老板感受的指标。净利润由四部分组成销售收入减去进货成本再减去缺货损失和库存持有成本。缺货损失不是真实支出但它代表顾客想买却没货、流失掉的那部分利润必须扣库存持有成本则包含资金占用和可能的商品损耗同样要扣。这个口径一旦定下来后面所有算法都围绕它转否则算出来的“最优”方案根本没有可比性。1.2 核心约束与优化方向拆解决定优化方案之前不能忽略约束。我总结了几类在赛题和真实门店中都会出现的限制库存不能为负缺货时只能接受损失每个供应商都有自己的供货能力与到货周期不是想订多少就能立刻到单次补货通常有最小起订量或者为了分摊配送成本要限制补货次数商品之间有明显的销售差异畅销品和滞销品不能共用一套补货规则。这些约束让问题从“多订点就行”变成了带限制的离散优化问题。再加上时间维度完整决策空间就是“每个时间点要不要下单、下多少”。这属于典型的有限时域库存优化问题也是很多供应链管理系统底层的核心逻辑。赛题数据规模不算大但决策空间组合很多直接穷举不现实需要设计合理的近似策略。1.3 为什么选用Python而不是其他工具选择Python不是因为“网上教程多”而是这个赛题的数据量和运算量正好在Python的舒适区里。几十种商品、几百天销售流水这个规模用纯循环都能跑完更不用说用pandas做聚合后速度非常快。Python的另一个优势是生态完整从数据读取、分析、模拟到可视化一站式解决写出来的代码可读性也高方便赛后复盘和调参数。如果换到更大的场景比如上千万条订单流水那我会考虑用C或者分布式框架。但对门店级的供销存优化Python就是最合适的工具能让你把80%的精力放在策略设计而不是工程细节上。2. 数据预处理与指标口径决定成败的第一道关2.1 销售流水清洗与日销量聚合赛题给的销售流水通常是一行一条购买记录包含商品编号、销售日期、销售数量、销售金额等信息。但原始数据直接用不了常见问题有三个同一天同一商品有多条记录日期字段格式不统一部分日期可能完全没有销售记录。我的处理方式是先用pandas把“商品编号日期”作为分组键对销售数量做sum聚合得到一个标准的长表。然后对每个商品生成完整日期序列从销售开始日到结束日逐天补齐没有销售的日子销量记为0。这一步非常关键因为后续库存模拟是按天滚动的如果某天缺失记录被当成“不存在”而不是“没卖出去”补货决策就会错位。2.2 供应商信息整理与到货周期映射供应商数据相对简单通常包含供应商编号、商品编号、供货单价、供货能力、到货周期等信息。这里最需要小心的是“到货周期”。它指的是从下单到商品实际入库的天数比如到货周期为2天那么第1天下的单第3天才能进入可用库存。我在代码里用字典把“商品编号-供应商”映射成到货周期和供货价再根据商品从哪家供应商进货来决定模拟时的补货提前期。一个小坑是同一商品可能对应多家供应商报价和周期都不一样。复现时我默认选择到货周期短的那家因为对门店来说缺货损失往往比单价差异更敏感这个取舍在后面的参数敏感性测试里也能看出来。2.3 成本参数设定与口径统一建模前需要把利润计算要用到的参数全部定义清楚。下面是我在复现中使用的一组示例参数实际跑代码时可以按业务情况调整参数含义示例取值备注成本价商品进货单价3.20元来自供应商报价零售价商品销售单价5.00元来自销售流水持货成本每件商品每天持有成本0.10元按货值百分比估算缺货损失缺货一件对应毛利损失1.80元按零售价减成本价最小起订量单次最少采购量10件可来自供应商规则到货周期下单到入库天数2天不同供应商不同参数口径统一非常重要。比如“持货成本”如果按货值的0.5%计算低价商品和高价商品每天占用成本完全不同不能用一个固定数字糊弄过去。我在代码里使用“持货成本成本价×持货费率”的方式计算这样更贴近实际情况。3. 建模思路从“经验判断”到“可计算的决策规则”3.1 三类成本如何平衡供销存优化的本质是平衡三类成本订货成本、持有成本、缺货成本。订货成本指的是每次下单产生的固定费用包括人力、配送等。如果订货次数太多固定费用会拖累利润如果订货次数太少单次订货量会很大库存持有成本上升如果订得太少太频繁缺货风险降低但成本高如果完全不控制又会频繁缺货流失顾客。这三者的平衡点就是最优策略所在。一个直观的类比是家里的冰箱采购每天都去菜市场买最新鲜的菜消耗时间成本一次买一周的菜有的菜放到坏掉就是持有成本周末想做饭却发现缺菜那就是缺货成本。超市补货和这个逻辑完全一样只是需要把“感觉”变成“公式”。3.2 库存滚动模拟与利润计算我采用按天滚动模拟的方式评估任何一套补货策略。基本逻辑是每天先接收“按到货周期应该到达的订单量”然后计算当天库存把当天销量和库存比较库存够就正常销售库存不够就记一次缺货并损失对应毛利最后扣除当天所有库存的持有成本。模拟结束后把所有天的收入和成本累加得到总利润。这个过程的代码逻辑可以表示成下面这样def simulate_inventory(sales, order_plan, init_stock20, lead_time2, price5.0, cost3.2, holding_fee0.1, shortage_loss1.8): stock init_stock shortage_days 0 total_profit 0.0 total_sales 0 for day in range(len(sales)): # 当天到货 stock order_plan.get(day - lead_time, 0) demand sales[day] # 实际销售量由库存上限决定 sold min(stock, demand) shortage demand - sold if shortage 0: shortage_days 1 total_profit - shortage * shortage_loss stock - sold total_sales sold total_profit sold * (price - cost) - stock * holding_fee return total_profit, shortage_days, total_sales, stock这段函数是整个项目的地基。后续不管用启发式算法、网格搜索还是机器学习最终都要落到这个评估函数上用利润数字检验策略好坏。3.3 用启发式搜索逼近最优解确定了评估函数之后下一步是找一套好的订货计划。我没有直接用商用求解器因为赛题规模不算大用启发式搜索反而更容易理解和调试。我选择的是“定期补货安全库存”策略再对两个关键参数做网格搜索补货周期每隔多少天检查一次库存并下单目标库存系数按未来多少天的预测销量来设定目标库存。补货计划的生成逻辑是每到补货日统计当前库存水平预测未来一个到货周期加补货周期内的需求用预测值减去当前库存再向上取整到最小订货批量的整数倍。如果计算出的订货量大于零就生成一条补货记录。import math def build_order_plan(sales, check_interval7, horizon9, min_order10, pack_size10, init_stock20, lead_time2): plan {} stock init_stock for day in range(0, len(sales), check_interval): # 预测未来 horizon 天需求 future_demand sum(sales[day: day horizon]) target max(future_demand, min_order) order_qty max(0, math.ceil((target - stock) / pack_size) * pack_size) if order_qty 0: plan[day] order_qty stock order_qty for d in range(day, min(day check_interval, len(sales))): stock max(stock - sales[d], 0) return plan注意这段代码是简化版实际运行时需要在每一天都更新库存特别是到货周期内不能重复下单。但思路很清晰策略由两个数字决定然后用网格搜索找到利润最高的参数组合。用这种办法几个小时内就能把参数空间扫一遍在赛题数据规模下完全可行。4. 代码架构与核心功能实现4.1 模块划分与工程结构整个项目源码保持了模块化方便把“数据处理”“策略搜索”“结果分析”三个环节分开。我的工程目录大致是这样的模块文件职责数据层data_loader.py读入销售数据与供应商数据预处理preprocess.py日期补齐、销量聚合、参数配置模拟引擎simulator.py库存滚动模拟与利润计算策略搜索optimizer.py网格搜索/枚举补货参数结果输出report.py输出订货计划表与指标统计这样拆分的好处是改参数不会污染数据处理逻辑换策略也只是新增一个函数不必重写模拟引擎。如果你想把项目扩展成自己的仓库按这个结构维护最省心。4.2 核心代码片段解读数据加载部分用pandas最顺手读入后统一转成标准列名。比如销售数据统一成“item_id”“date”“qty”供应商数据统一成“item_id”“supplier_id”“unit_cost”“lead_time”。之后所有逻辑都只认这些列名减少后续出错的概率。模拟引擎的代码在上一节已经给出是全项目最核心的函数。我在实际使用中给它加了一个“debug模式”可以打印每一天的库存变化和缺货记录非常好用。比如某个商品总在每周三缺货打开debug打印几行就能看到规律比盯着最终利润数字猜原因高效得多。4.3 结果输出与可视化优化结束之后代码会把最优补货计划保存成CSV包括“商品编号、订货日期、订货量、预计到货日期、订货时库存”。同时按商品维度汇总出利润、缺货天数、期末库存、订货次数等核心指标。这个输出格式也是答辩和写报告时最常需要的形式。可视化方面我用matplotlib画了每个商品的库存-销量时序图把补货时点和缺货时点都标出来。图上能直观看到库存是否总是压得很高、缺货是否集中在某些促销日附近。不要小看这几张图它经常能发现建模时忽略的异常规律比如某个商品销量在月初特别高普通数据分析根本看不出来。5. 常见问题与排查技巧实录5.1 数据对齐出错的坑这个项目里我踩过最大的坑是日期对齐。销售数据本身不是连续每一天都有记录有些商品在某个时间段内可能就是0销售。如果直接拿原始记录做模拟库存会被误判为“一直没动”导致补货策略完全失效。解决办法是先按商品生成完整日期索引再左连接销量缺失值用0填充。另一个相关问题是补货到货日期的边界。比如到货周期是2天第1天订货第3天到货。如果代码里用的是“当天订货当天就到”最优参数会明显偏向减少订货量因为缺货风险被低估了。这个边界在代码里极其容易忽略但影响非常大。5.2 缺货损失参数太灵敏缺货损失的设定对最优策略影响非常大。如果缺货损失设得很高算法会倾向于多订货、高库存如果设得很低算法会频繁缺货。实际操作中我的做法是先按“零售价-成本价”算出毛利作为缺货损失的基准再扫一个上下浮动50%的范围观察最优参数是否剧烈变化。如果变化不大说明策略稳定如果变化很大说明模型对这个参数比较敏感需要业务方给出明确口径。我在复现赛题时发现很多队伍的模型本质差别不大最终分数差距往往来自成本参数的口径选择。所以建议你在跑代码前先把参数定义和业务对齐不要默认一个数字就开跑。5.3 网格搜索太慢怎么办如果商品种类多、补货周期范围大网格搜索的组合数量会迅速膨胀。我的优化办法分两步先用大步长粗扫找到利润较高的区域再在小范围内细扫找到更精确的参数组合。这个方法虽然简单但能把搜索时间从几小时缩短到几分钟。如果商品数量特别多还可以把“所有商品共用一套参数”改成“每个商品单独搜索参数”再按利润贡献排序只对贡献最大的前几个商品做细粒度调参。这样既避免过度拟合又能聚焦真正影响利润的SKU。最后再分享一个实用技巧我在跑代码时把所有商品的最优参数保存成一个JSON文件每次调参都保留历史版本。这样反复实验后能快速对比不同版本之间的利润差异也方便在报告里展示“参数调优过程”比只给一个最终结果有说服力得多。这套代码里我已经把JSON输出的逻辑写好了直接用就行。本文还有配套的精品资源点击获取