使用Python绘制多区域水文多面图的实现需求
使用Python绘制多区域水文多面图的实现需求
我手里有好几个区域的水文监测数据,现在已经写了一段代码可以单独画出每个区域的降雨量(倒置柱状图)和河流流量(折线图)的组合水文图,但现在想把这些区域的图整合到同一个画布上,做成类似2x2布局的多面图,这样对比起来更直观。
原有的代码和示例数据如下:
import pandas as pd import matplotlib.pyplot as plt def hydrograph_plot(dates, rain, river_flow): # figure and subplots fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True, gridspec_kw={'height_ratios': [1, 2]}) # bar graph rainfall (inverse y axis) ax1.bar(dates, rain, color='blue', alpha=0.6, label='Chuva (mm)') ax1.set_ylabel('Rainfall (mm)', color='blue') ax1.set_ylim(max(rain), 0) # y axis inverted ax1.set_title('Hydrograph: Rainfall at ' + rain.name + ' and Flow at ' + river_flow.name) # Concatenate colname to the title ax1.legend(loc='upper left') ax1.grid(True, linestyle='--', alpha=0.5) # line graph for river flow ax2.plot(dates, river_flow, color='green', marker='o', label='Nível do Rio (m)') ax2.set_xlabel('Data') ax2.set_ylabel('StreamFlow (m)', color='green') ax2.legend(loc='upper left') ax2.grid(True, linestyle='--', alpha=0.5) # layout and show fig.autofmt_xdate() plt.tight_layout() plt.show() # Example data for multiple locations data = { 'Time': pd.date_range(start='2023-10-01', periods=24, freq='D'), # Time in days 'Rainfall_Loc1': [0, 2, 5, 10, 8, 4, 2, 0, 0, 1, 3, 6, 9, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0], # Rainfall for Location 1 'Streamflow_Loc1': [10, 12, 15, 50, 80, 70, 60, 50, 40, 35, 30, 25, 20, 18, 16, 14, 12, 11, 10, 10, 10, 10, 10, 10], # Streamflow for Location 1 'Rainfall_Loc2': [0, 1, 3, 7, 12, 10, 6, 3, 1, 0, 0, 0, 2, 4, 6, 8, 7, 5, 3, 1, 0, 0, 0, 0], # Rainfall for Location 2 'Streamflow_Loc2': [15, 18, 20, 60, 90, 85, 75, 65, 55, 50, 45, 40, 35, 30, 25, 20, 18, 16, 15, 15, 15, 15, 15, 15], # Streamflow for Location 2 'Rainfall_Loc3': [0, 0, 0, 0, 1, 2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0], # Rainfall for Location 3 'Streamflow_Loc3': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20], # Streamflow for Location 3 'Rainfall_Loc4': [0, 5, 8, 0, 1, 3, 14, 6, 5, 20, 1, 10, 0, 16, 2, 21, 10, 0, 0, 0, 0, 0, 0, 0], # Rainfall for Location 4 'Streamflow_Loc4': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20] # Streamflow for Location 4 } df = pd.DataFrame(data)
解决方案:修改代码实现多区域多面布局
要实现你想要的多子图布局,咱们只需要对原代码做两个关键调整,就能批量生成统一布局的多区域水文图:
import pandas as pd import matplotlib.pyplot as plt from matplotlib.gridspec import GridSpec # 修改后的绘图函数:接收外部子图对象,不再单独创建画布 def hydrograph_plot(dates, rain, river_flow, ax1, ax2): # 绘制降雨量柱状图(倒置Y轴) ax1.bar(dates, rain, color='blue', alpha=0.6, label='Chuva (mm)') ax1.set_ylabel('Rainfall (mm)', color='blue') # 处理无降雨的异常情况,避免Y轴范围错误 ax1.set_ylim(max(rain) if max(rain) > 0 else 10, 0) ax1.set_title(f'Hydrograph: {rain.name} & {river_flow.name}') ax1.legend(loc='upper left') ax1.grid(True, linestyle='--', alpha=0.5) # 绘制河流流量折线图 ax2.plot(dates, river_flow, color='green', marker='o', label='Nível do Rio (m)') ax2.set_xlabel('Data') ax2.set_ylabel('StreamFlow (m)', color='green') ax2.legend(loc='upper left') ax2.grid(True, linestyle='--', alpha=0.5) # 自动旋转X轴日期标签,避免重叠 plt.setp(ax2.get_xticklabels(), rotation=45, ha='right') # 加载数据 data = { 'Time': pd.date_range(start='2023-10-01', periods=24, freq='D'), 'Rainfall_Loc1': [0, 2, 5, 10, 8, 4, 2, 0, 0, 1, 3, 6, 9, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0], 'Streamflow_Loc1': [10, 12, 15, 50, 80, 70, 60, 50, 40, 35, 30, 25, 20, 18, 16, 14, 12, 11, 10, 10, 10, 10, 10, 10], 'Rainfall_Loc2': [0, 1, 3, 7, 12, 10, 6, 3, 1, 0, 0, 0, 2, 4, 6, 8, 7, 5, 3, 1, 0, 0, 0, 0], 'Streamflow_Loc2': [15, 18, 20, 60, 90, 85, 75, 65, 55, 50, 45, 40, 35, 30, 25, 20, 18, 16, 15, 15, 15, 15, 15, 15], 'Rainfall_Loc3': [0, 0, 0, 0, 1, 2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0], 'Streamflow_Loc3': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20], 'Rainfall_Loc4': [0, 5, 8, 0, 1, 3, 14, 6, 5, 20, 1, 10, 0, 16, 2, 21, 10, 0, 0, 0, 0, 0, 0, 0], 'Streamflow_Loc4': [20, 22, 25, 70, 100, 95, 85, 75, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 20, 20, 20, 20, 20, 20] } df = pd.DataFrame(data) dates = df['Time'] # 1. 规划画布布局:4个区域用2行2列的大网格,每个大格子包含上下两个子图(降雨+流量) fig = plt.figure(figsize=(16, 12)) gs = GridSpec(4, 2, figure=fig, height_ratios=[1,2,1,2]) # 4行2列:第1/3行是降雨子图,第2/4行是流量子图 # 2. 循环批量处理每个区域 locations = ['Loc1', 'Loc2', 'Loc3', 'Loc4'] for idx, loc in enumerate(locations): # 获取当前区域的降雨和流量数据 rain_col = f'Rainfall_{loc}' flow_col = f'Streamflow_{loc}' rain_data = df[rain_col] flow_data = df[flow_col] # 计算当前区域对应的子图位置 row = idx * 2 # 每个区域占2行(降雨在上,流量在下) ax_rain = fig.add_subplot(gs[row, idx%2]) ax_flow = fig.add_subplot(gs[row+1, idx%2], sharex=ax_rain) # 调用修改后的绘图函数绘制当前区域的图 hydrograph_plot(dates, rain_data, flow_data, ax_rain, ax_flow) # 调整整体布局,避免标签重叠 fig.tight_layout() plt.subplots_adjust(hspace=0.3) # 增加子图之间的垂直间距 plt.show()
关键修改说明
- 绘图函数重构:去掉原函数中创建画布的逻辑,改成接收外部传入的子图对象,这样就能把图画到预先规划的网格里,实现多面布局。
- 灵活网格布局:用
GridSpec实现精准的子图划分,保持每个区域的降雨/流量子图高度比例和原代码一致。 - 批量处理逻辑:通过循环自动匹配每个区域的列名,计算子图位置,批量生成所有区域的水文图,不用重复写代码。
- 异常兼容:处理了某区域无降雨的情况,避免Y轴范围出现错误。
备注:内容来源于stack exchange,提问作者Caluan
相关产品推荐
相关产品推荐

