简介这份资源面向参加2024年高教社杯全国大学生数学建模竞赛C题的学生与指导教师聚焦农作物种植策略的可视化实现。包内共31个文件以20个png图表、6个Python脚本和5个xlsx数据表为主压缩包约2MB涵盖数据预处理、描述性统计、异常值分析、价格与占地面积可视化等模块图表类型包括热力图、箱线图、散点图、饼图与QQ图等。已有1010人学习下载说明其在赛题复现与可视化参考方面具有较高认可度。读者可借助现成脚本快速完成附件数据清洗与图形输出对照图表理解作物亩产量、销售单价、种植成本与地块面积之间的关系并参考异常值检测与相关性分析思路为自身建模论文的图表呈现与结果验证提供可复用的代码模板和排错方向。1. 从赛题到可交互图表这套种植策略可视化代码到底能省多少事每年数学建模竞赛的 C 题都是数据密集型题目2024 年高教社杯 C 题「农作物的种植策略」也不例外。题目给了一张包含地块类型、作物种类、种植季次、产量与成本参数的表格要求参赛队在耕地资源约束、轮作要求、市场需求波动等条件下给出 2024 到 2030 年的最优种植方案。很多队伍在建模和求解上花了两三天最后卡在「怎么把结果讲清楚」这一步——论文里全是表格和数字评委翻三页就疲了。这套可视化代码解决的就是这个环节。它把求解输出的种植方案通常是各年份、各地块、各作物的种植面积矩阵转成堆叠面积图、热力图、桑基图、雷达图等图表让「哪块地种了什么、几年间结构怎么变、方案在哪些指标上占优」一眼可见。适合两类人一类是正在准备竞赛、需要快速出图的队伍另一类是已经拿到求解结果、但不知道怎么把数据组织成可绘图格式的选手。代码本身不包含求解器它只负责「结果 → 图表」这一段所以你得先有自己的种植方案数据。2. 数据接口与目录结构先把输入格式对齐再谈画图2.1 输入数据的三种常见形态不同队伍的求解器输出格式差异很大常见的有三种一是长表格式每行是一条「年份-地块-作物-面积」记录二是宽表格式行是地块、列是年份或作物单元格是面积三是嵌套字典Python 求解脚本里直接以 dict 形式保存。可视化代码默认吃长表因为长表最容易做分组聚合也最接近 pandas 的 groupby 习惯。如果你手上是宽表需要先 melt 成长表。这一步看起来简单但坑在于列名里可能混了年份和作物名比如「2024_小麦」这种复合列名直接 melt 会得到一列无意义的 variable。常见做法是用正则把年份和作物名拆成两列再传给绘图函数。2.2 目录结构与依赖安装代码包一般包含以下几个部分data/放原始数据和求解结果src/放绘图脚本config/放作物颜色映射和地块标签output/放生成的图片。依赖主要是 pandas、matplotlib、seaborn、plotly 和 networkx桑基图用。安装命令如下pip install pandas matplotlib seaborn plotly networkx kaleidokaleido 是 plotly 导出静态图片的后端很多人在本地能显示图但导出 PNG 时报错就是缺这个包。如果你只在 Jupyter 里看图可以不装但论文排版通常需要高清 PNG 或 PDF建议装上。2.3 配置文件里的三个关键参数config/plot_config.yaml里有三个参数最常改year_range控制横轴年份范围默认 2024-2030crop_order控制堆叠图的图例顺序按作物类别排还是按面积排color_map是作物到颜色的映射如果不改小麦和玉米可能都是蓝色系堆叠图会糊成一片。我一般会把主粮作物用暖色、经济作物用冷色、蔬菜用绿色系这样评委一眼能区分结构。提示配置文件里的中文作物名要和数据表里的名称完全一致包括空格和括号。曾经有队伍数据里写「小麦(冬)」配置里写「小麦冬」全角半角不一致导致颜色映射失效图例全是灰色。3. 四类核心图表的绘制流程与参数调优3.1 堆叠面积图看种植结构年际变化堆叠面积图最适合展示「每年各类作物总种植面积占比」。核心逻辑是按年份和作物分组求和然后 pivot 成宽表再用 matplotlib 的 stackplot 或 pandas 的 area plot。import pandas as pd import matplotlib.pyplot as plt # 读取长表格式的种植方案 df pd.read_csv(data/planting_result.csv) # 按年份和作物聚合面积 pivot_df df.groupby([year, crop])[area].sum().unstack(fill_value0) # 按 config 中的作物顺序重排列 crop_order [小麦, 玉米, 水稻, 大豆, 蔬菜] pivot_df pivot_df[crop_order] fig, ax plt.subplots(figsize(10, 6)) ax.stackplot(pivot_df.index, pivot_df.T.values, labelspivot_df.columns, colors[#D4A017, #8B4513, #2E8B57, #4682B4, #90EE90], alpha0.85) ax.set_xlabel(年份) ax.set_ylabel(种植面积亩) ax.legend(locupper left, ncol3) plt.tight_layout() plt.savefig(output/stacked_area.png, dpi300)逻辑说明groupby后unstack把长表转成「年份 × 作物」的宽表fill_value0保证缺失组合填零而不是 NaN。stackplot的values需要转置因为它的输入是「序列的序列」每一行是一个作物的面积序列。alpha设 0.85 是为了让网格线透出来方便读数值。dpi 设 300 是论文投稿的常见要求低于 200 打印出来会糊。参数调优如果年份多、作物多图例会挤可以把ncol改成 2 或 3或者把图例放到图外。colors建议手动指定不要用默认色环因为默认色环在堆叠图里相邻颜色对比度不够。3.2 热力图地块-作物匹配关系一目了然热力图适合展示「某一年份各地块种了什么、面积多大」。行是地块列是作物单元格颜色深浅代表面积。这种图在答辩时特别好用评委能快速看出哪些地块被充分利用、哪些地块有空闲。import seaborn as sns # 筛选特定年份 year_df df[df[year] 2024] heatmap_data year_df.pivot_table(indexplot, columnscrop, valuesarea, aggfuncsum, fill_value0) plt.figure(figsize(12, 8)) sns.heatmap(heatmap_data, annotTrue, fmt.0f, cmapYlGnBu, linewidths0.5, cbar_kws{label: 面积亩}) plt.title(2024 年地块-作物种植面积热力图) plt.tight_layout() plt.savefig(output/heatmap_2024.png, dpi300)逻辑说明pivot_table比pivot更稳因为如果同一地块同一作物有多条记录比如不同季次aggfuncsum会自动合并。annotTrue把数值标在格子里fmt.0f控制不显示小数。cmapYlGnBu是浅黄到深蓝的渐变面积越大颜色越深符合直觉。注意如果地块数量超过 20 个行标签会重叠这时候要么把图拉长要么只画重点地块。我一般会按总面积排序取前 15 个地块画图剩下的在附录里用表格呈现。3.3 桑基图追踪作物从地块到市场的流向桑基图在种植策略题里是个加分项因为它能同时展示「地块 → 作物 → 销售渠道」的多级流向。plotly 的go.Sankey需要节点和链接两个数据结构节点是所有出现过的地块名、作物名、渠道名链接是它们之间的面积流量。import plotly.graph_objects as go # 构建节点列表 plots df[plot].unique().tolist() crops df[crop].unique().tolist() channels [自用, 市场销售, 库存] labels plots crops channels # 构建链接地块到作物 source, target, value [], [], [] for _, row in df.iterrows(): source.append(labels.index(row[plot])) target.append(labels.index(row[crop])) value.append(row[area]) fig go.Figure(go.Sankey( nodedict(labellabels, pad15, thickness20), linkdict(sourcesource, targettarget, valuevalue) )) fig.write_image(output/sankey.png, width1200, height800, scale2)逻辑说明labels.index()把名称转成索引这是 plotly Sankey 的硬性要求。scale2让导出图片分辨率翻倍适合论文。如果流向太密可以按作物类别聚合比如把「小麦」「玉米」合并成「粮食作物」减少节点数。3.4 雷达图多方案综合对比如果你有多个求解方案比如线性规划、遗传算法、启发式各出一个结果雷达图能把「总收益、土地利用率、轮作合规度、市场满足率、成本」五个维度放在一张图上对比。import numpy as np categories [总收益, 土地利用率, 轮作合规度, 市场满足率, 成本控制] schemes { 方案A: [0.85, 0.92, 0.78, 0.88, 0.70], 方案B: [0.90, 0.85, 0.82, 0.80, 0.75], 方案C: [0.78, 0.95, 0.90, 0.85, 0.68] } angles np.linspace(0, 2 * np.pi, len(categories), endpointFalse).tolist() angles angles[:1] # 闭合 fig, ax plt.subplots(figsize(8, 8), subplot_kwdict(polarTrue)) for name, values in schemes.items(): vals values values[:1] ax.plot(angles, vals, labelname, linewidth2) ax.fill(angles, vals, alpha0.15) ax.set_xticks(angles[:-1]) ax.set_xticklabels(categories) ax.legend(locupper right, bbox_to_anchor(1.3, 1.1)) plt.tight_layout() plt.savefig(output/radar.png, dpi300)逻辑说明雷达图的关键是闭合所以angles和vals都要在末尾追加第一个元素。fill的alpha设 0.15 是为了让多个方案叠加时还能看清各自的轮廓。bbox_to_anchor把图例挪到图外避免遮挡数据。4. 避坑与排查那些让图表翻车的细节4.1 中文显示成方块现象图表里所有中文变成方框英文和数字正常。原因matplotlib 默认字体不含中文字形。解决在绘图前设置plt.rcParams[font.sans-serif] [SimHei]和plt.rcParams[axes.unicode_minus] False。如果系统没有 SimHei换成「Microsoft YaHei」或「Noto Sans CJK」。Linux 服务器上通常没有这些字体需要手动安装或把字体文件放到代码目录并用font_manager加载。4.2 堆叠图面积总和对不上现象堆叠图某年的总面积和表格里算出来的不一致。原因数据里同一地块同一作物同一季次有多条记录groupby时没去重或没合并。解决先检查原始数据的唯一键确认「年份地块作物季次」是否唯一。如果不唯一要么在求解阶段就合并要么在绘图前用groupby加sum聚合。4.3 plotly 导出图片报错现象fig.write_image报「Kaleido 未安装」或「版本不兼容」。原因plotly 导出静态图依赖 kaleido且版本要匹配。解决pip install kaleido0.2.1如果还不行降级 plotly 到 5.x 版本。另一个常见原因是缺少 Chrome 内核kaleido 需要它来渲染服务器上要装chromium或google-chrome。4.4 热力图颜色区分度不够现象面积差异大的地块和面积小的地块颜色看起来差不多。原因默认的线性颜色映射对极端值不敏感。解决把cmap换成对数刻度或者用LogNorm归一化。另一个办法是手动设置vmin和vmax把最大值截断到 95 分位数避免一个超大值把其他颜色压扁。4.5 桑基图节点顺序混乱现象桑基图的节点排列没有逻辑地块和作物交叉出现。原因labels列表的顺序决定了节点位置。解决按「地块 → 作物 → 渠道」的顺序拼接labels并且把同类节点按面积或名称排序。如果还是乱可以用node的x和y参数手动指定位置但工作量较大一般排序就够了。5. 进阶技巧把静态图变成可交互的答辩工具5.1 用 plotly 做年份滑块论文里的图是静态的但答辩时如果能拖动滑块看不同年份的种植结构变化效果会好很多。plotly 的frames机制可以实现这个效果核心思路是把每年的数据做成一个 frame然后用updatemenus加滑块。import plotly.express as px fig px.bar(df, xcrop, yarea, colorcrop, animation_frameyear, range_y[0, df[area].max() * 1.1]) fig.update_layout(title各年份种植面积动态变化) fig.write_html(output/animated_bar.html)逻辑说明animation_frameyear自动生成滑块range_y固定纵轴范围避免每年刻度跳动。导出 HTML 后可以直接用浏览器打开答辩时全屏展示。如果数据量大可以只保留面积前 10 的作物减少渲染压力。5.2 图表组合与论文排版论文里通常需要把多张图拼成一张大图用plt.subplots或matplotlib.gridspec都行。我一般会把堆叠面积图放上面热力图和雷达图放下面桑基图单独一页。导出时统一用 300 dpi 的 PNG如果期刊要求矢量图就导 PDF。注意 PDF 里中文字体要嵌入否则编辑那边打开可能乱码。5.3 一个我踩过的坑有一次帮队伍调图堆叠面积图怎么画都是空白查了两个小时才发现数据里的year列是字符串类型groupby后索引排序变成「2024, 2025, 2026...」没问题但stackplot的 x 轴不接受字符串需要转成整数。从那以后我每次拿到数据第一件事就是df.info()看列类型年份、面积、数量这些列必须是数值型分类列必须是 category 或 object混了就先转再画。希望这套可视化代码能帮你把建模结果讲得更清楚少在出图这一步耗时间。本文还有配套的精品资源点击获取