Python matplotlib多序列绘图线型区分选择方法(水文场景)
降水扰动情景水文对比图线型优化方案
当前图件4类水文要素共20组序列挤在双轴坐标系内,仅靠同色系下不同marker区分,视觉编码维度单一,线条、符号堆叠后辨识度极低,可按以下方向优化:
一、视觉编码分层(核心提升区分度)
- 给不同要素大类绑定固定的、不重复的视觉属性,不要混同:
- 降水(P):蓝色系、实线、圆形类marker,线宽1.2
- 蒸散发(ET):红/橙红色系、虚线、方形类marker,线宽1.2
- 径流(Q):绿色系、点划线、菱形类marker,线宽1.2
- 雪水当量(SWE):青/蓝紫色系、点线、五边形类marker,线宽1.5(SWE在左轴,稍粗的线宽可以和右轴要素直接拉开差异)
- 基准情景(100%降水)统一用纯黑色,线宽2,marker比其他情景大2号,作为参考线突出显示,不用和其他扰动情景共用同色系
- 同要素下的不同降水扰动梯度,用同色系的透明度/饱和度区分:离基准100%降水越远,透明度越高(比如70%、110%情景透明度设0.6,80%、90%设0.8),不用全用0.5的统一透明度,读者可以直接通过颜色深浅判断扰动幅度
- 不要全用实线!现在所有序列都是实线,是辨识度低的核心原因之一。把4类要素的线型(
-实线/--虚线/-.点划线/:点线)拆开,哪怕颜色接近,线型差异也能快速区分,尤其打印成黑白版本时,线型的辨识度远高于颜色和marker - marker不要全数据点铺满!现在每个月的数据点都打marker,线密的时候符号直接糊成块,改成
markevery=2隔点显示,或者只在1、4、7、10月季度节点打marker,保留标识作用的同时图面干净很多
二、图面信息减负
- 20条线硬塞在一个双轴图里信息密度严重过载,优先考虑拆成2行2列的子图,每个子图单独放一类水文要素的5组扰动序列,5条线不需要复杂样式也不会混淆,还能给每个子图单独配坐标轴标注,读数值比双轴方便很多
- 如果必须保留单图双轴形式,把网格线透明度降到0.1以下,直接关掉次要网格和次刻度——现在主次网格全开,和细线叠在一起进一步加剧杂乱感
- 图例不要拆成左右两个飘在图外,合并成一个放在图右侧,开启
labelcolor='linecolor'让图例文字颜色和对应线条颜色一致,找序列的时候不用来回对照色块
三、代码优化参考
原脚本逐行写20个plot语句重复度极高,还容易出笔误(比如原代码里SWE的字段名前多打了空格,容易读不到数据),可以用循环批量绘图,统一管控样式:
import matplotlib.pyplot as plt # 预定义要素样式:要素名 -> (颜色, 线型, marker, 挂载坐标轴) style_config = { 'P': {'color': '#1f77b4', 'ls': '-', 'marker': 'o', 'ax': 'ax2'}, 'ET': {'color': '#d62728', 'ls': '--', 'marker': 's', 'ax': 'ax2'}, 'Q': {'color': '#2ca02c', 'ls': '-.', 'marker': 'D', 'ax': 'ax2'}, 'SWE': {'color': '#17becf', 'ls': ':', 'marker': 'p', 'ax': 'ax1', 'lw': 1.5} } # 预定义扰动情景:情景名, 对应数据集, 透明度 scenario_config = [ ('70%', bbprec070_vicoutput_monthly, 0.6), ('80%', bbprec080_vicoutput_monthly, 0.8), ('90%', bbprec090_vicoutput_monthly, 0.8), ('100%', bbb_vicoutput_monthly, 1.0), ('110%', bbprec110_vicoutput_monthly, 0.6) ] marksize = 6 fig, ax1 = plt.subplots(figsize=(10, 6), dpi=100) ax2 = ax1.twinx() ax_map = {'ax1': ax1, 'ax2': ax2} lines = [] labels = [] for var in style_config: sty = style_config[var] target_ax = ax_map[sty['ax']] for scen_name, data, alpha in scenario_config: # 基准情景单独用黑色加粗突出 if scen_name == '100%': line_color = 'black' line_width = 2 marker_size = 10 else: line_color = sty['color'] line_width = sty.get('lw', 1.2) marker_size = marksize # 计算变量值,修正原代码字段名前多余空格的问题 if var == 'Q': y_val = data['OUT_BASEFLOW']['mean'] + data['OUT_RUNOFF']['mean'] else: y_val = data[f'OUT_{var}']['mean'] line, = target_ax.plot(months, y_val, color=line_color, linestyle=sty['ls'], marker=sty['marker'], alpha=alpha, lw=line_width, markersize=marker_size, markevery=2) lines.append(line) labels.append(f'{var}({scen_name})') # 标注设置 ax1.set_ylabel('Hydrologic States (mm)', fontsize=10) ax2.set_ylabel('Hydrologic Fluxes (mm/day)', fontsize=10) plt.title("Precipitation Perturbation", fontsize=12, pad=15) # 仅保留低透明度主网格,关闭次网格减少干扰 ax1.grid(which='major', color='#666666', linestyle='-', alpha=0.1) ax1.minorticks_off() # 合并单图例,文字颜色和线条一致 ax1.legend(lines, labels, loc='center left', bbox_to_anchor=(1.12, 0.5), fontsize=9, labelcolor='linecolor') plt.tight_layout() plt.show()
原脚本输出的效果如下,核心问题为线型单一、marker过密、视觉属性无分层:
内容的提问来源于stack exchange,提问作者fyec
相关产品推荐
相关产品推荐

