尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

数学建模C题实战:Python数据可视化与农作物种植策略优化

数学建模C题实战:Python数据可视化与农作物种植策略优化 简介2024年高教社杯全国大学生数学建模竞赛C题“农作物的种植策略”配套可视化代码面向参赛学生和数据分析学习者。它聚焦于将模型结果转化为直观图表涵盖数据预处理、描述性统计、异常值分析与多维度可视化使用Python的matplotlib、seaborn等库实现。压缩包共31个文件包括6个py脚本、20个png图表和5个xlsx数据表整体仅2MB结构清晰脚本完成数据清洗与绘图Excel工作表保存原始及整理后的数据PNG则呈现柱状图、热力图、散点图等分析结果。已有1005人学习下载适合希望快速复现赛题分析流程、参考可视化代码风格或进行二次开发的用户。通过这份资源读者可以掌握从数据预处理到可视化呈现的完整思路直接运行代码生成图表并对照Excel数据理解作物种植策略中的关键影响因素。 2024 年高教社杯的 C 题放出来之后我身边不少参赛的朋友都在吐槽数据量大、约束条件细、地块类型绕最后论文写完了图却不知道怎么画“可视化代码”这事看起来简单真到自己手写要用的时候就发现到处是坑。我今天就把自己把 C 题整套数据从清洗到建模、再落到可视化代码的过程完整拆开。这篇内容主要围绕农作物的种植策略展开重点解决两个问题怎么把题目的数据约束转化为可计算的方案以及怎么用 Python 把地块、作物、产量、收益这些关系画得让评委一眼看懂。不管你是刚接触数学建模的小白还是已经卡在画图阶段的老手这篇都适合你花十几分钟过一遍。1. 题目再拆解C 题到底在问什么1.1 背景与数据范围C 题给的是华北某山村 2023 年的实际种植数据地块分成平旱地、梯田、山坡地和水浇地四类每一类又细分出具体地块编号。作物种类覆盖小麦、玉米、谷子、高粱、黄豆、黑豆、红豆、绿豆、爬豆、水稻、土豆、红薯、大蒜、白菜、白萝卜、胡萝卜、西红柿、茄子、柿子椒、大葱、黄瓜、四季豆、食用菌、灵芝、羊肚菌等二十多种。别看题目背景只写了个“种植策略”它本质上是一道多地块、多作物、多约束的非线性规划问题。你要在满足重茬限制、豆类固氮、食用菌后茬处理、每种作物每年只种一季等一系列条件下为 2024 到 2030 年制定最优种植方案。我一开始拿到数据时最头疼的其实是这种状态问题二给了庞大的网格搜索空间问题三还要做不确定性分析。很多人写到后面直接放弃可视化结果论文里全是表格评委看得费劲得分自然受影响。1.2 问题一最基础也最容易被轻视问题一要求“假定未来两年气候稳定、市场稳定、亩产量和销售价格保持不变”这句话直接决定模型复杂度。你不需要预测不需要回归只需要做约束下的最优规划。但注意它的交叠设定——同一块地不能连续种植同一种作物各种植季之间也不能重茬豆类作物要参与轮作食用菌种过之后需要休耕。这意味着你的决策变量不只是“种什么”还包括“前茬是什么”是一个带历史状态约束的指标规划不是简单的单期最大化。可视化在这个阶段最重要的不是炫技而是把每块地在两年内的作物安排呈现清楚。我推荐做堆叠柱状图加地块分面横轴是地块编号纵轴是种植面积颜色代表不同作物再叠加季度轮换信息一张图就能说完整个方案。1.3 问题二与问题三从静态到动态问题二要在问题一基础上放宽假设允许未来产量、成本、价格波动并且某些作物可以或必须连续种植此时整个决策变成多阶段规划。这里的核心难点不再只是约束判断而是“未来参数怎么定”。最稳妥的做法是用 2023 年的数据作为基准结合题目附带的 2024-2030 预期增长率和波动区间做情景生成再对每种情景跑规划最后加权各情景的最优方案。这个过程会产生大量中间数据如果不靠可视化做中间检查很容易出现“模型算出负产量、地块面积超限”这种低级错误。问题三则是引入不确定性的敏感性分析我通常的做法是把销售价格和亩产量按±5%、±10%、±15% 做扰动观察作物选择是否发生跳变。这一部分最出效果的可视化是折线带置信区间图把不同扰动水平下的收益范围和种植面积变化画出来直接能说明策略的稳健性。2. 数据处理链路与模型策略2.1 2023 年基准数据怎么清洗题目给的 Excel 里最核心的字段是地块名、地块类型、作物名、种植面积、亩产量斤/亩、销售单价元/斤、种植成本元/亩。这些字段本身很干净但里面潜伏着三个问题。第一题目里小麦、玉米等粮食作物的“亩产量”通常是一季的数据但如果某块地一年能种两季比如水浇地冬小麦夏玉米轮作你需要按季拆开否则年化产量会翻倍出错。第二蔬菜类的成本区间波动很大不同地块间可能给出不同的参考值直接用平均值会损失地块差异信息。第三食用菌和灵芝这类特殊作物数据可能只有个别地块有做全局约束时容易漏掉“只能种在特定大棚地块”这样的隐含条件。我的清洗思路是先把所有表按地块编号纵向拼接再按“季”横展开形成一张每一行是“地块×年×季”的长表。后续所有建模、绘图都基于这张长表不要再用原始宽表直接算。2.2 从数据到规划参数计算与约束编码在正式开始优化前我习惯先把每亩收益算出来[ \text{profit_per_mu} \text{yield_per_mu} \times \text{price} - \text{cost_per_mu} ]C 题里题目给了部分作物的销售价格但没给全比如部分蔬菜和食用菌可能需要你根据市场价格自行补充。我自己在复现时对缺失价格取了同类作物的平均价并在附录里做了说明这样模型至少可复现。约束编码要注意的是“重茬”判断。最简单的方式是构造一个 0-1 变量矩阵行代表地块列代表作物值 1 表示该季种该作物然后用前一季的矩阵做点乘强制同位置不能同时为 1。如果地块数量多直接用 scipy.optimize.linprog 或 pulp 都能解如果地块多到几千块建议用 openpyxl 把约束条件导出成 LP 文件再用 CBC 或 GLPK 求解避免在 Python 里硬算导致内存爆炸。2.3 网格搜索与结果导出结构问题二我采用的是 grid search 加单期规划组合先对每个地块、每种作物、每个年份跑预测收益把预测结果填入二维矩阵再逐期求解。这样每次求解规模小、速度快而且便于把中间结果实时存成 CSV用 matplotlib 画分阶段趋势。结果导出的统一结构我推荐这样定义{ year: 2024, plot_id: A1, plot_type: 平旱地, season: 第一季, crop: 小麦, area: 20.5, profit_per_mu: 1234.5, total_profit: 25307.25 }每画一张图都从这样的字典列表里抽字段灵活性最高。后续不论做堆叠图、热力图还是桑基图都不需要再回头改数据结构。3. 可视化方案设计从“能看”到“讲得清”3.1 可视化目标不是好看是辅助论证我在评阅过一些建模论文后发现评委看图最在意的不是配色而是能否快速提取信息。所以可视化的第一目标永远是辅助论证。比如你论证“水浇地适合小麦玉米轮作”就应该画一张水浇地的季相分布图让评委一眼看到轮作关系而不是只画柱状图堆叠成品。我在这道题里把图表分成四层数据概览层、模型输入层、优化方案层和敏感性分析层。图表类型对应问题展示内容堆叠柱状图问题一每个地块各作物面积占比分面热力地图问题一/二地块类型 × 作物 × 年份的种植分布分组柱状图问题二不同情景下的总收益对比折线图带置信区间问题三价格/产量扰动下的收益稳健性桑基图问题二地块类型到作物再到收益的资金流向这套图表的逻辑是一张图回答一个问题不堆叠信息。很多团队喜欢一张图塞五六种元素最后图例占了半个画布反而说不清问题。3.2 配色与字体细节数学建模论文是黑白的但答辩 PPT 可以是彩色的。我建议在论文中用白底黑线、灰度配色在答辩中用高区分度的色板。一个容易踩的坑是直接用 matplotlib 默认色板在论文打印时颜色区分度极低。推荐用 colorbrewer 的 Set3 或 Tableau 色板代码里这样指定colors [ #8dd3c7, #ffffb3, #bebada, #fb8072, #80b1d3, #fdb462, #b3de69, #fccde5, #d9d9d9, #bc80bd ]字体方面中文论文必须配中文字体macOS 用Arial Unicode MSWindows 用SimHeiLinux 服务器最好上传字体文件让 matplotlib 动态加载不要赌系统自带。3.3 模块化设计一份代码出全套图在实际写的时候我建议把可视化代码按照“数据读取 — 字段解析 — 绘图函数 — 主循环调用”四层组织不要把所有图写在同一个循环里。因为 2024-2030 有 7 年问题一问题二问题三加起来要上百张图一旦要调整配色逐图改能改到崩溃。我的基本组织结构是# plot_utils.py def plot_stack_bar(df, output_path): ... def plot_heatmap_by_plot_type(df, output_path): ... def plot_gantt_by_crop(df, output_path): ...然后主脚本统一调用。这样做的好处是如果后期题目数据勘误只需重新生成数据 CSV绘图函数完全不用改动。4. 核心可视化代码实现4.1 地块类型与种植面积分布图这张图解决的是“题目给了哪些地块、各类地块面积多大”的概述问题。数据读取后先按地块类型聚合面积。import pandas as pd import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams[font.sans-serif] [SimHei] matplotlib.rcParams[axes.unicode_minus] False df pd.read_excel(data/2023_data.xlsx, sheet_name地块信息) type_area df.groupby(地块类型)[面积].sum().sort_values() fig, ax plt.subplots(figsize(10, 6)) bars ax.barh(type_area.index, type_area.values, color#80b1d3) ax.set_xlabel(种植面积亩) ax.set_title(2023 年各类地块种植面积概览) for bar, val in zip(bars, type_area.values): ax.text(bar.get_width() 0.5, bar.get_y() bar.get_height() / 2, f{val:.1f}, vacenter) plt.tight_layout() plt.savefig(output/plot_area_by_type.png, dpi300) plt.show()为什么先画这张图因为 C 题的地块面积差异很大如果不先做概览后面画堆叠图时很容易被最大地块的数值拉乱坐标轴导致小地块信息完全被压扁。提前确认面积分布再有针对性地决定要不要做分面。4.2 作物收益率散点图筛选主栽作物的利器这张图解决的是“哪些作物值得优先种植”的问题。横轴是亩产量纵轴是销售单价点的大小代表种植面积颜色代表作物类别。看到这张图你基本能判断模型最后优化出的主力作物应该是哪些。crop_data pd.read_excel(data/2023_data.xlsx, sheet_name作物数据) fig, ax plt.subplots(figsize(12, 8)) scatter ax.scatter( crop_data[亩产量], crop_data[销售单价], scrop_data[种植面积] * 2, alpha0.6, cpd.factorize(crop_data[作物类别])[0], cmapSet3 ) ax.set_xlabel(亩产量斤/亩) ax.set_ylabel(销售单价元/斤) ax.set_title(作物亩产量与销售单价分布点大小种植面积) for idx, row in crop_data.iterrows(): ax.annotate(row[作物名称], (row[亩产量], row[销售单价]), fontsize9, xytext(5, 5), textcoordsoffset points) plt.tight_layout() plt.savefig(output/plot_crop_scatter.png, dpi300) plt.show()这张图对问题的帮助非常直观如果某种作物亩产量高、单价也高、种植面积也不小那在模型里它一定是最受欢迎的决策选择。反过来说如果某个作物三项指标都很低模型大概率在最优解里不会选它。这是你检验求解结果合理性的一张“直觉图”。4.3 2024-2030 最优方案堆叠柱状图这是论文里最核心的一张结果图。横轴是年份纵轴是总种植面积不同颜色堆叠代表不同作物。为了让每种作物都能被看到我建议每年只取种植面积占比前 10 的作物其余归为“其他”否则 20 多种作物堆在一张图里图例就能占满半页。opt_results pd.read_csv(output/optimal_solution.csv) top_crops ( opt_results.groupby(作物)[面积] .sum() .sort_values(ascendingFalse) .head(10) .index ) opt_results[作物分组] opt_results[作物].where( opt_results[作物].isin(top_crops), 其他 ) pivot_df opt_results.pivot_table( index年份, columns作物分组, values面积, aggfuncsum, fill_value0 ) pivot_df pivot_df[top_crops.tolist() [其他]] fig, ax plt.subplots(figsize(14, 7)) pivot_df.plot(kindbar, stackedTrue, axax, colormapSet3, edgecolorwhite) ax.set_xlabel(年份) ax.set_ylabel(种植面积亩) ax.set_title(2024-2030 年最优种植方案面积构成) ax.legend(ncol4, fontsize9) plt.xticks(rotation0) plt.tight_layout() plt.savefig(output/plot_optimal_stack.png, dpi300) plt.show()我实际画的时候发现如果不加edgecolorwhite相邻年份相同作物的色块会连成一片边界不清晰打印出来尤其明显。另外堆叠柱状图的图例顺序要和柱子内部顺序一致否则读者对不上这个问题用pivot_df[top_crops.tolist() [其他]]这句代码就能解决。4.4 地块类型与作物关系热力图热力图适合展示“哪类地块适合种哪些作物”的二维关系。我用的是 seaborn 的heatmap数据先做地块类型与作物的交叉表。import seaborn as sns cross_df opt_results.pivot_table( index地块类型, columns作物, values面积, aggfuncsum, fill_value0 ) # 只保留累计面积前 15 的作物避免图太宽 top_crops_area cross_df.sum(axis0).sort_values(ascendingFalse).head(15) cross_df cross_df[top_crops_area.index] fig, ax plt.subplots(figsize(14, 6)) sns.heatmap(cross_df, annotTrue, fmt.0f, cmapYlGnBu, linewidths0.5, axax) ax.set_title(2024-2030 年地块类型 × 作物累计种植面积热力图) plt.tight_layout() plt.savefig(output/plot_heatmap.png, dpi300) plt.show()这张图的优势是能一眼看出模型是否出现“山坡地种水稻”这种违反常识的方案。我在复现时曾经因为约束漏写了水稻只能种水浇地结果模型把水稻分到山坡地单看数值表格完全没发现一画热力图立刻暴露问题——这就是可视化作为模型校验工具的价值。4.5 收益与面积的关系图气泡图气泡图可以把“面积、收益、作物类型”三个维度的信息同时呈现在一张图里。横轴是累计种植面积纵轴是累计收益气泡大小代表平均亩收益颜色代表作物类别。summary opt_results.groupby(作物).agg( 面积(面积, sum), 总收益(收益, sum), 平均亩收益(亩收益, mean) ).reset_index() fig, ax plt.subplots(figsize(12, 8)) bubble ax.scatter( summary[面积], summary[总收益], ssummary[平均亩收益] * 0.8, alpha0.6, cpd.factorize(summary[作物])[0], cmaptab20 ) ax.set_xlabel(累计种植面积亩) ax.set_ylabel(累计总收益元) ax.set_title(作物累计面积与收益气泡图) for idx, row in summary.iterrows(): ax.annotate(row[作物], (row[面积], row[总收益]), fontsize9, xytext(4, 4), textcoordsoffset points) plt.tight_layout() plt.savefig(output/plot_bubble.png, dpi300) plt.show()这张图放在问题二的结果分析里特别加分。它能把“少数作物贡献大部分收益”的帕累托特征讲清楚评委会觉得你不仅算出了方案还理解了方案背后的经济学结构。5. 常见问题与排查技巧实录这一部分都是我自己在复现和代跑代码时真实踩过的坑比很多官方文档写得直白建议直接收藏。5.1 中文乱码与负号显示问题matplotlib 默认字体不含中文字形直接画图会出现一个个方框。在 Windows 下用SimHei基本能解决但要注意负号也会因为字体替换变成方框必须加matplotlib.rcParams[axes.unicode_minus] False。在 macOS 下SimHei不存在推荐用Arial Unicode MS或者PingFang SC。如果在服务器上跑什么都不带最省事的方案是直接用plt.rcParams[font.family] sans-serif然后下载一个Noto Sans CJK SC的 otf 字体文件放到 matplotlib 字体目录里并删除缓存。import matplotlib.font_manager as fm fm.fontManager.addfont(/path/to/NotoSansCJKsc-Regular.otf) plt.rcParams[font.family] Noto Sans CJK SC5.2 图例重叠与条目不齐图例重叠在堆叠柱状图里几乎是必现的特别是作物种类超过 12 种后。我的经验是优先用ax.legend(ncol4, bbox_to_anchor(0.5, -0.15), locupper center)把图例放到图外下方而不是放在绘图区内。如果放图外还重叠就减少堆叠的作物种类用“其他”来兜底。论文图最好不要出现超过 12 类的图例。5.3 数据透视表 NaN 值处理pivot_table默认对缺失的“地块 × 作物”组合会填 NaN直接画热力图时会出现白色方块看着像是数据缺失。加fill_value0把它们变成 0表示“没种”而不是“没数据”语义上更准确图表也更干净。5.4 导出高分辨率图论文插图通常要求 300dpi 以上plt.savefig(..., dpi300)是底线。同时注意保存格式期刊或论文模板一般要求 EPS 或 PDFmatplotlib 支持直接存成 PDF但有些中文字体在 PDF 里会提示“无法嵌入”。这种情况我会用bbox_inchestight再加figure.autolayoutTrue基本能避免大部分裁边和字体问题。plt.savefig(output/plot.pdf, dpi300, bbox_inchestight)5.5 常见错误速查表报错信息原因解决方案FindFont: font family not found中文字体未注册用font_manager.addfont指定字体文件ValueError: operands could not be broadcast数据表行列不对齐检查pivot_table的 index 和 columns 参数LinAlgError: Singular matrix线性规划约束矩阵奇异检查是否有重复约束或全零行KeyError: 面积列名不匹配打印df.columns.tolist()核对图形空白但无报错数据是 NaN 或被过滤检查聚合后 DataFrame 是否为空6. 一点个人体会C 题这套数据量放在数学建模竞赛里不算大但维度嵌套得很深地块类型、作物种类、年份、季节、约束条件叠加起来如果不在每一个阶段画图校验模型出错几乎是必然的。可视化代码不是论文写好之后才补的装饰品而是建模过程中帮你发现约束错误、确认数据口径、检验结果合理性的核心工具。我自己的习惯是每算完一步就导出 CSV每导出一个 CSV 就画一张图每画一张图就问自己一个问题——“这个结果符合常理吗如果不符合是模型错了还是数据错了”这套流程走下来论文的图表基本是顺手就齐了根本不需要最后赶工。如果你今年也在准备国赛或者其他数据类竞赛不妨试试按我上面的思路把数据表和可视化一起往前推。前期多花二十分钟画中间图后期可能帮你省下的是整整一天排查逻辑错误的时间。本文还有配套的精品资源点击获取
返回列表