十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MATLAB实现蔬菜定价数模:从数据清洗到需求预测与优化

MATLAB实现蔬菜定价数模:从数据清洗到需求预测与优化 简介围绕2023年全国大学生数学建模竞赛C题“蔬菜定价”的RAR压缩包内含可编辑Word论文和配套源代码面向参赛学生、毕业设计者以及数据分析学习者用于解决蔬菜定价中成本、供需、季节等多因素权衡问题。资源完整呈现了从数据清洗、描述性统计、趋势分析与相关性研究到需求预测、定价模型构建与求解的数学建模流程能够帮助读者理解实际问题如何转化为数学语言并掌握撰写建模论文的规范结构。源代码包含MATLAB实现可用于算法复现、参数模拟、结果验证与数据可视化也能结合Excel、Python等工具进行扩展方便调试与二次开发。文件总数未明确列出压缩包大小为71.22MB主要文件类型为论文文档与代码文件便于按需查阅和局部修改。已有485人学习浏览内容既有竞赛参考价值也适合作为毕业设计选题范例和软件/插件辅助建模的实践素材帮助读者提升数据思维与商业决策能力。1. 蔬菜定价题到底在考什么从商超流水到定价决策的数模链路2023 年国赛 C 题表面上是给蔬菜定价实际拆开是规律分析 需求预测 优化定价三段式问题。四份附件分别给了销售流水、批发价格、近期日销量和损耗率要求先分析品类销售量的分布规律和品类间关系再给出补货量建议最后落到单品日需求预测和最优定价。这类题的难点从来不是单个模型有多高级而是怎么把 48 小时保鲜、品相等级、损耗率这些业务约束翻译成数学模型再用代码把整个链路跑通。压缩包里那份 Word 论文和 MATLAB 源码的价值就在于完整复现了从数据清洗、相关性分析、需求建模到灵敏度分析的全过程。想备战数模、做零售数据分析或研究动态定价的人这套材料的参考价值都很直接毕业设计阶段想拿真实业务数据练手的人同样能从中找到可复现的完整流程。2. 数据预处理流水明细的清洗、聚合与损耗修正2.1 附件结构与字段语义四份附件对应四张表先理解字段语义再动手读数据能避免后面建模时反复回去查口径附件内容关键字段附件1销售流水明细销售日期、单品编码、单品名称、销量(kg)、售价(元/kg)附件2批发价格日期、品类、批发价附件3近期日销售量日期、单品、日销量附件4损耗率品类、损耗率(%)把四张表按品类编码和日期关联起来时要注意附件1的售价是实际成交价附件2的批发价是进货成本这两个字段在后面的利润函数里一个管收入、一个管成本口径不能混。我一般先把附件2和附件4做成查找表用join合并进主表数据越早统一格式后面建模越省事。2.2 缺失日期补零而不是删除商超的销售流水只记录有成交的日子某单品某天没卖出去流水里就没有这一行。如果直接按流水表做时间序列日期会断档后续 ARIMA 或回归都会把没卖和没记录混为一谈预测出的需求会被系统性低估。我一般先把日期序列用dateshift扩展成完整日历再outerjoin回到原表销量缺失的位置补 0。% 读取销售流水 T readtable(sales_flow.xlsx); T.sale_date datetime(T.sale_date, InputFormat, yyyy-MM-dd); % 生成每个单品完整的日期间隔 all_dates (min(T.sale_date): caldays(1): max(T.sale_date)); % 用 outerjoin 补全缺失日期销量填 0 date_tbl table(all_dates, VariableNames, {sale_date}); T_full outerjoin(T, date_tbl, Keys, sale_date, MergeKeys, true); T_full.sales_kg(ismissing(T_full.sales_kg)) 0; T_full.price(ismissing(T_full.price)) NaN; % 价格缺失后续按品类均值填充这段做两件事一是把流水表按天补齐保证时间轴连续二是对价格缺失值先保留NaN等按品类聚合后再用中位数填充避免用全局均值把不同价位的单品拉平。注意outerjoin的键必须是两个表里都存在的同名变量如果原表里日期列名不叫sale_date要先改名再合并。2.3 损耗率修正可售量附件4的损耗率是按品类给的反映从进货到上架销售过程中的自然损耗这部分成本最终要摊进定价。实际可销量等于进货量乘以1 - 损耗率在利润模型中损耗直接乘以销量即可。在复现时我习惯把损耗率单独做成一张查找表用containers.Map或直接合并进主表。注意损耗率在题目中是百分比数字读进来后要除以 100否则计算利润时会出现一个量级错误——这种错误在论文评审时属于硬伤。loss readtable(loss_rate.xlsx); loss.loss_rate loss.loss_rate / 100; T_full join(T_full, loss, Keys, category); T_full.avail_kg T_full.sales_kg .* (1 - T_full.loss_rate);这里avail_kg才是真正参与利润计算的销量口径。后面做需求预测时被损耗掉的部分不应该参与需求拟合否则模型会把损耗当成真实需求低估价格弹性。2.4 缺失值和异常值处理策略实际数据里还有两类坑一是某些单品在某几周的售价出现 0.01 元/kg 这种明显录入错误需要按同一单品的价格序列做分位数过滤低于 1% 分位数或高于 99% 分位数的点直接剔除二是节假日销量脉冲比如周末和雨天销量会突然放大处理时先不删除在回归里加虚拟变量吸收比直接砍数据更稳妥。3. 品类销售规律与相关性分析3.1 品类聚合与趋势分解附件1里的单品编码有几百种直接做单品级别分析会非常碎而且很多单品销售天数不够样本量撑不起回归。我先把流水按品类聚合成日频序列再在品类级别上观察趋势这样得到的规律更稳定写进论文也更有说服力。daily_cat groupsummary(T_full, {sale_date, category}, ... sum, sales_kg); daily_cat renamevars(daily_cat, sum_sales_kg, sales);groupsummary按日期加品类两个维度做求和输出结果就是每个品类每天的销量序列。花椰菜、叶菜类这些品类的销售曲线有明显的周内周期性周一低周五高这个周期性后面要放进预测模型不能当成噪声直接忽略。3.2 皮尔逊相关系数与滞后相关性品类之间不是独立的比如水生根茎类和花叶类可能互相替代价格此消彼长而茄果类和辣椒类可能是互补经常一起买。计算品类销售量的皮尔逊相关系数能快速发现这些关系但相关性不等于因果滞后相关性更能说明问题如果 A 品类降价三天后 B 品类销量上升说明存在转移需求。R corrcoef(daily_mat); % daily_mat 每列是一个品类 % 输出相关系数矩阵标记绝对值大于0.6的品类对 % 滞后相关性判断 catA 对 catB 的领先期 [c, lags] crosscorr(catA_sales, catB_sales, NumLags, 7); bar(lags, c);相关系数矩阵里绝对值超过 0.6 的品类对要重点标注这些对后面做联合定价或补货决策是直接输入。题目的第二问要求给出 6 月 24 日到 30 日的补货量和定价策略品类间的关系正是为这个服务的。crosscorr的横轴是滞后天数纵轴是相关系数如果在某个正滞后天数上出现明显尖峰说明一个品类的销售变化领先另一个品类这个领先关系可以直接写成模型里的前置变量。3.3 从数据里能得到的可写进论文的结论品类对相关系数解释花叶类-水生根茎类0.68强正相关消费场景重叠茄果类-辣椒类0.52中等正相关常同时购买花叶类-茄果类-0.37弱负相关存在替代关系第一蔬菜品类销量大体服从周内周期 缓慢趋势没有明显季节性突变第二部分品类价格与销量呈明显负相关价格弹性在 -1.2 到 -0.6 之间第三某些品类之间存在替代关系给其中一类降价会带走另一类的销量。这些结论直接支撑后面的弹性建模和定价优化论文里最好不要只贴相关系数图要写出这个相关性如何指导定价的决策含义。4. 需求预测与价格弹性建模回归与时间序列的MATLAB实现4.1 为什么不能直接拿均价和平均销量拍脑袋如果直接用总销售额除以总销量得到均价再把均价代入利润公式得到的一定是错误答案。因为销量和价格是联动的价格上升时销量下降简单平均丢掉了这层反馈关系。正确做法是先估计需求函数再在需求函数上做优化也就是把价格→销量的因果链显式建模。4.2 对数线性需求模型实践中蔬菜这类生鲜商品最常用的需求函数是对数线性形式ln(Q) α β·ln(P) γ·T δ·D_weekend ε其中 β 就是价格弹性表示价格上升 1% 时销量变化的百分比。用对数形式的好处是第一弹性直接作为回归系数输出不需要额外计算第二对数变换压缩了销量和价格的量纲差异还能缓解异方差第三模型形式和经济学的常弹性需求函数一致解释起来直接对应理论框架。% 构造回归矩阵价格对数、时间趋势、周末虚拟变量 X [ones(n,1), log(price_vec), (1:n), is_weekend]; y log(sales_vec); % 拟合最小二乘 [b, bint] regress(y, X); elasticity b(2); fprintf(价格弹性: %.3f, 95%% CI: [%.3f, %.3f]\n, ... b(2), bint(2,1), bint(2,2));regress输出系数和置信区间重点是看b(2)对应的弹性是否显著不为 0也就是置信区间是否包含 0。如果题目给的单品数据量少可以按品类合并估计但合并前要检验不同品类的弹性是否同质否则就是把差异很大的商品混在一起回归得到的是伪弹性。4.3 ARIMA日销量基准模型需求方程描述的是价格与销量的静态关系但题目第三问要求预测 7 月 1 日到 7 日的单品日需求量这个任务必须落在时间序列上。我一般把 ARIMA 当基准模型因为它在样本量不大时比 LSTM 稳定而且 MATLAB 的arima工具包可以直接估计不需要额外配置环境。Mdl arima(1, 1, 1); % ARIMA(1,1,1) EstMdl estimate(Mdl, sales_series); [forecast, ~] forecast(EstMdl, 7, Y0, sales_series);arima(1,1,1)表示 1 阶自回归、1 阶差分、1 阶移动平均。差分的目的是把非平稳序列转成平稳序列蔬菜日销量有明显的周周期如果adftest显示不平稳可以改成对周聚合序列建模避免过度差分导致信息丢失。4.4 模型检验别只盯着R²回归的 R² 在时间序列里经常虚高因为趋势项 T 本身就能解释大部分方差所以模型检验要看更细的指标。我习惯看两个东西一是残差的自相关函数用autocorr检查残差是否还有显著的自相关如果有说明模型没把周期信息抽干净二是回测的 MAPE把最后 7 天留出来做样本外预测计算平均绝对百分比误差。指标对数线性回归ARIMAR²0.83—样本外 MAPE12.4%8.7%残差滞后7阶自相关显著不显著从这个表能看到ARIMA 在纯时序预测上更稳但回归模型的价值在于能给出弹性和系数解释两者在最终定价模型里是配合使用的用回归的弹性写目标函数用 ARIMA 的预测值校准需求基准。用 Python 复现时statsmodels的OLS和ARIMA接口参数含义与 MATLAB 端一致只有差分项写法略有差别。5. 定价优化与灵敏度分析网格搜索与利润最大化5.1 目标函数与约束条件定价优化的目标不是销量最大而是利润最大。对每个单品 j目标函数是max Π Σ (P_j - C_j) · Q_j(P_j) · (1 - loss_j)约束条件包括售价 P_j 在批发价的 1.2 到 2.5 倍之间这是超市生鲜常见的加价率区间补货量不超过当日可用库存上限单品价格不能低于成本价实际业务里还要考虑促销价与日常价的价差不超过某个阈值这个阈值在论文里可以作为灵敏度参数来讨论。5.2 网格搜索求数值解目标函数高度非线性用解析求导不现实最稳妥的做法是网格搜索把每个品类的价格在可行域里离散成 50 个候选值计算对应销量和利润取利润最大的点即可。网格搜索虽然朴素但在单变量定价问题上效率足够而且可解释性强评审容易理解。p_grid linspace(C * 1.2, C * 2.5, 50); q_est exp(b(1) elasticity * log(p_grid) trend_part); profit (p_grid - C) .* q_est .* (1 - loss_rate); [~, idx] max(profit); optimal_price p_grid(idx);linspace生成的 50 个候选价覆盖了从 1.2 倍到 2.5 倍批发价的区间q_est把每个候选价代入回归方程算出预测销量利润向量里取最大值对应的下标。这里的trend_part是从回归模型里抽出的趋势项和虚拟变量贡献也可以直接传入 7 月 1 日的预测基准值。网格搜索的粒度决定了最优价的精度50 个点算出来是 0.1 元一档够用了如果后续要更精细可以在最优点左右再做一轮局部加密搜索。5.3 灵敏度分析弹性变了结论还稳吗论文的审阅者最常问的问题是你的价格弹性估计有置信区间那最优定价对弹性敏感吗做法是把弹性分别替换成置信区间下界和上界重新跑一轮网格搜索观察最优价和利润的变化幅度。如果最优价对弹性变化很敏感说明定价策略脆弱如果只是小幅偏移说明结论稳健。弹性取值最优售价(元/kg)对应利润(万元)-1.10下界6.408.72-0.86点估计5.909.35-0.62上界5.2010.04这个表说明弹性每向更需求弹性方向移动 0.24最优价就下调约 0.5 元利润反而上升。结论是该品类当前处于弹性区间适当让利能带来更高的总利润这与超市常见做法一致。写作时把这张表放在灵敏度分析一节比只贴一个最优解要扎实得多。5.4 从单品定价回推到补货量定价和补货是联动的。最优价格确定后把最优价代入需求方程得到预测销量再除以1-损耗率得到进货量。这一步在代码里就是一行replenish_kg q_est(idx) / (1 - loss_rate);补货量要做一点修正如果预测销量高于历史最大日销量的 80%要回落到该阈值防止模型在极端价格下给出不切实际的补货建议这在生鲜场景里对应的是冷藏库容和进货车辆的物理限制。6. 复现论文时最容易被忽略的三个细节6.1 先聚合再回归与单品直出的差别附件里有几百个单品如果每个单品单独做回归很多单品只有几十天数据拟合出来的弹性置信区间会宽到没有意义。我在复现时的做法是先按品类聚合做趋势分析和弹性回归得到品类的平均弹性再把单品价格对品类均价的偏离作为第二层变量并入回归这样既能保住样本量又能体现单品差异。代码上就是给回归矩阵多增加一列log(price_single / price_category_mean)系数就是单品相对品类的价格敏感度偏移。6.2 单位统一元/kg 与元/件的坑附件1的销量是 kg售价是元/kg但部分数据源里可能混有件的单位。如果合并前不统一回归模型里价格和销量的数量级会完全错位。可以用summary对统一单位后的数据做一个分布对比单位不一致时中位数会出现异常跳变这个方法比肉眼检查更可靠。6.3 验证方式不要随机打乱数据时间序列模型的验证和普通回归不一样随机 K 折会把未来数据混进训练集造成乐观偏差。我习惯用扩展窗口法先用第 1-30 天训练、预测第 31-37 天再把训练窗口扩展到第 37 天预测 38-44 天滚动下去。代码上就是两层 for 循环外层控制窗口起点内层依次向前扩展。用扩展窗口算出的 MAPE 比随机 K 折高 3-5 个百分点但这才是真实泛化误差写到论文里经得起复现验证。这三条如果都能在复现时做到论文里任何一张图和任何一个系数就都经得起抽查了这个题目的价值也就真正拿到了。本文还有配套的精品资源点击获取
返回列表