1. 环境监测数据与趋势报告概述
环境监测是生态环境保护工作的“眼睛”,而趋势报告则是把这双眼睛看到的变化讲清楚、说明白的载体。在动手写任何一行 Matplotlib 代码之前,先要弄清楚三件事:我们手里有什么数据、报告要交给谁看、Matplotlib 在其中扮演什么角色。这三件事决定了后续所有图表的设计方向。
一、环境监测数据的类型与来源
环境监测数据按介质划分,常见的有空气质量、地表水、噪声、土壤与污染源排放等几大类。以空气质量为例,常规监测因子包括 PM2.5、PM10、SO₂、NO₂、CO、O₃ 六项,数据通常以小时均值形式采集,再汇总为日均值、月均值。地表水则关注 pH、溶解氧、高锰酸盐指数、氨氮、总磷等指标,按断面和监测频次组织。噪声数据多为小时等效声级 Leq,污染源数据则常带排放口编号和工况信息。
从数据来源看,主要有三条渠道:一是国控、省控自动监测站的在线数据,特点是高频、连续、带时间戳;二是手工采样加实验室分析数据,频次低但精度高,常用于比对和质控;三是企业自行监测与执法监测数据,带有明显的点位和工况属性。这些数据在进入绘图环节前,通常已经整理成“时间—点位—指标—数值”的表格结构,可能还附带超标标记、有效标志位等字段。理解这一点很关键:Matplotlib 画图时面对的往往不是原始报文,而是经过清洗的二维表,因此数据的时间粒度、缺失值处理方式会直接影响趋势线的形态。
二、趋势报告的目标与读者
趋势报告不是数据的堆砌,而是围绕一个明确问题组织证据。常见目标有三类:判断某指标在时间上是否改善或恶化、比较不同点位或区域的差异、识别异常波动并给出可能原因。目标不同,图的选择就不同——看长期走向用折线,看分布差异用箱线图或分组柱状图,看超标频次用堆叠柱或热力图。
读者决定了图的“信息密度”。面向管理决策者的报告,图要一眼看懂结论,坐标轴标签简洁、重点用颜色或标注突出;面向技术人员的报告,可以保留更多细节,如置信区间、超标阈值线、数据点标记。同一份数据,给领导看的版本可能只保留月均值折线和一条标准限值参考线,给业务科室的版本则会叠加日均值散点和缺失区间。写作时要先在心里给读者画像,再决定图的复杂度。
三、Matplotlib 在报告制作中的定位
Matplotlib 是 Python 生态中最基础、可控性最强的绘图库。它的价值不在于“好看”,而在于“可复现”和“可批量”。环境监测报告往往涉及多个点位、多个指标、多个时间段的重复出图,手工在 Excel 里调格式既慢又容易出错;用 Matplotlib 把绘图逻辑写成函数,换一份数据就能重新生成整套图,风格还能保持一致。
它的核心对象是 Figure(画布)和 Axes(坐标系),几乎所有定制都围绕这两个对象展开:用 fig, ax = plt.subplots() 建立画布与坐标系,用 ax.plot()、ax.bar() 绘制图形,用 ax.set_xlabel()、ax.axhline() 添加标签和参考线,最后 fig.savefig() 输出图片。相比一些封装度更高的库,Matplotlib 需要写的代码多一些,但每一个像素都可控,这对需要标注标准限值、超标区间、监测断面的专业报告来说非常重要。
四、整体工作流程
一份趋势报告的完整链路可以概括为:明确问题与读者 → 获取并清洗数据 → 选择图表类型 → 用 Matplotlib 绘制与定制 → 检查可读性与结论一致性 → 导出并嵌入报告。下图展示了这条链路中各环节的输入输出关系,以及 Matplotlib 所处的位置。
这条链路里有两个容易被忽视的环节。其一是“清洗”,监测数据常有缺测、仪器故障导致的异常高值或零值,若不处理直接画折线,趋势会被尖刺扭曲;常见做法是按标志位剔除无效值,对短时缺失做插值或留空,并在图上用断线或浅色区间标明缺失时段。其二是“检查”,图生成后要回到报告目标上核对:这条趋势线是否支持正文结论?参考线位置是否正确?单位是否标注?这一步花不了几分钟,却能避免把一张“好看但误导”的图交出去。
五、一个最小示例的定位
为了把上述流程落到具体操作上,可以设想一份示例数据:某站点 2023 年 1 月至 12 月的 PM2.5 月均值,共 12 条记录,另有一条 35 μg/m³ 的二级标准参考线。绘图时用月份作横轴、浓度作纵轴画折线,叠加参考线,并在超过参考线的月份用橙色标记。这段代码不长,但它已经包含了本课程后续反复使用的几个基本动作:建画布、画线、加参考线、做条件标注、存图。示例数据仅用于演示,实际报告应替换为经质控的监测数据。
import matplotlib.pyplot as plt
months = list(range(1, 13))
pm25 = [42, 38, 35, 30, 26, 22, 19, 21, 25, 31, 39, 45] # 示例月均值
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(months, pm25, marker="o", color="#1e88e5", label="PM2.5 月均值")
ax.axhline(35, color="#e53935", linestyle="--", label="二级标准 35 μg/m³")
for m, v in zip(months, pm25):
if v > 35:
ax.annotate(f"{v}", (m, v), textcoords="offset points",
xytext=(0, 8), ha="center", color="#ef6c00")
ax.set_xlabel("月份")
ax.set_ylabel("PM2.5 浓度(μg/m³)")
ax.set_title("某站点 PM2.5 月均趋势(示例数据)")
ax.legend()
fig.tight_layout()
fig.savefig("pm25_trend.png", dpi=150)