Python多子图绘制代码(共享colorbar、微调子图等)
本文只包含绘图代码,不包含数据和数据处理。
1. 共享colorbar
2. 调整子图间距
3. 高分辨率保存
4. 微调字体大小
5. 合适的掩膜
6. colorbar刻度自定
代码语言:python代码运行次数:0运行复制import xarray as xr
import numpy as np
import cmaps
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import cartopy.io.shapereader as shapereader
import shapefile
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import matplotlib.gridspec as gridspec
import matplotlib
from matplotlib.ticker import MultipleLocator
# 中文字体设置
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# === 数据准备 ===
def load_data(month):
"""加载各月数据"""
path = fr"G:\CN0.5\CN05_2001-2020_{month}mon_extreme_precip_indices_hunan.nc"
ds = xr.open_dataset(path)
return {
'R95': ds['R95'],
'SEPA': ds['SEPA'],
'SEPD': ds['SEPD']
'SEPI': ds['SEPI'],
'RSEP': ds['RSEP'],
'Nce': ds['Nce'],
'lon': ds['lon'],
'lat': ds['lat']
}
# 加载各月数据
data_6 = load_data(6)
data_7 = load_data(7)
data_8 = load_data(8)
# 坐标网格
lon2d, lat2d = np.meshgrid(data_6['lon'], data_6['lat'])
# === 绘图设置 ===
fig = plt.figure(figsize=(18, 10))
gs = gridspec.GridSpec(3, 6, figure=fig,
hspace=0.15,
wspace=0.07,
bottom=0.18, # 底部留出空间给colorbar
left=0.1,
top=0.95)
# 色标等级配置(每列共享)
level_config = [
{'name': 'R95', 'levels': np.arange(18, 53, 1), 'unit': 'mm', 'extend': 'both', 'colorbar_level': [20, 26, 32, 38, 44, 50]},
{'name': 'SEPA', 'levels': np.arange(44, 90, 1), 'unit': 'mm', 'extend': 'both', 'colorbar_level': [45, 53, 61, 69, 77, 85]},
{'name': 'SEPD', 'levels': np.arange(0.8, 1.65, 0.01), 'unit': 'd', 'extend': 'both', 'colorbar_level': [0.8, 1.0, 1.2, 1.4, 1.6]},
{'name': 'SEPI', 'levels': np.arange(27, 75, 1), 'unit': 'mm/d', 'extend': 'both', 'colorbar_level': [30, 40, 50, 60, 70]},
{'name': 'RSEP', 'levels': np.arange(0.28, 0.46, 0.01), 'unit': '%', 'extend': 'both', 'colorbar_level': [0.29, 0.34, 0.39, 0.44]},
{'name': 'Nce', 'levels': np.arange(0.05, 0.35, 0.01), 'unit': 'times', 'extend': 'both', 'colorbar_level': [0.06, 0.13, 0.20, 0.27, 0.34]}
]
# 标题配置
titles = [
'(a)R95(mm)', '(b)SEPA(mm)', '(c)SEPD(d)', '(d)SEPI(mm/d)', '(e)RSEP(%)', '(f)Nce(times)',
'(g)R95(mm)', '(h)SEPA(mm)', '(i)SEPD(d)', '(j)SEPI(mm/d)', '(k)RSEP(%)', '(l)Nce(times)',
'(m)R95(mm)', '(n)SEPA(mm)', '(o)SEPD(d)', '(p)SEPI(mm/d)', '(q)RSEP(%)', '(r)Nce(times)'
]
# === 绘图主循环 ===
shp_path = 'C:/你的shapefile/行政边界.shp' # 掩膜用shp
sf = shapefile.Reader(shp_path)
shapes = sf.shapes()
for idx in range(18):
row = idx // 6 # 0-2 对应6-8月
col = idx % 6 # 0-5 对应各指标
# 选择数据
data_source = [data_6, data_7, data_8][row]
var_name = list(data_source.keys())[col]
data = data_source[var_name]
# 创建子图
ax = fig.add_subplot(gs[row, col], projection=ccrs.PlateCarree())
# === 绘制填色图 ===
level_info = level_config[col]
cs = ax.contourf(lon2d, lat2d, data, transform=ccrs.PlateCarree(), levels=level_info['levels'], cmap='Spectral_r', extend='both')
# === 添加省界掩膜 ===
codes = []
pts = shapes[0].points
prt = list(shapes[0].parts) + [len(pts)]
for j in range(len(prt) - 1):
codes += [Path.MOVETO]
codes += [Path.LINETO] * (prt[j + 1] - prt[j] - 2)
codes += [Path.CLOSEPOLY]
clip = Path(pts, codes)
clip = PathPatch(clip, transform=ax.transData)
cs.set_clip_path(clip)
# === 添加省内边界 ===
shp_shengnei = 'E:/你的shapefile.shp'
ax.add_geometries(shapereader.Reader(shp_shengnei).geometries(),
crs=ccrs.PlateCarree(),
facecolor='none',
edgecolor='k',
linewidth=0.5)
# === 坐标轴设置 ===
ax.set_extent([108.5, 114.5, 24.5, 30.5], crs=ccrs.PlateCarree())
# 左侧添加月份标签
if col == 0:
month_labels = ['六月', '七月', '八月']
ax.text(-0.32, 0.5, month_labels[row],
transform=ax.transAxes,
va='center',
ha='center',
fontsize=16,
rotation=90)
# 底部坐标轴(经度刻度)
if row == 2:
ax.set_xticks(np.arange(109, 115, 1))
lon_formatter = LongitudeFormatter(number_format='.0f') # 显示整数经度
ax.xaxis.set_major_formatter(lon_formatter)
ax.tick_params(axis='x', which='major', labelsize=14)
for label in ax.get_xticklabels():
label.set_rotation(30)
else:
ax.set_xticks([])
# 左侧坐标轴(纬度刻度)
if col == 0:
ax.set_yticks(np.arange(25, 31, 1))
lat_formatter = LatitudeFormatter(number_format='.0f') # 显示整数纬度
ax.yaxis.set_major_formatter(lat_formatter)
ax.tick_params(axis='y', which='major', labelsize=14)
else:
ax.set_yticks([])
# === 添加标题 ===
ax.text(0.02, 1.03, titles[idx], transform=ax.transAxes, fontsize=15,
bbox=dict(facecolor='none', edgecolor='none', pad=0.1))
# === 添加colorbar ===
if row == 2: # 最后一行添加colorbar
pos = ax.get_position()
cax = fig.add_axes([pos.x0, 0.10, # 调整垂直位置
pos.width, 0.015]) # 调整高度
cb = plt.colorbar(cs, cax=cax,
orientation='horizontal',
extend=level_info['extend'])
cb.set_ticks(level_info['colorbar_level']) # 设置 colorbar 的刻度
cb.set_ticklabels([str(i) for i in level_info['colorbar_level']]) # 设置刻度标签
cb.set_label(f"{level_info['name']} ({level_info['unit']})", fontsize=13)
cb.ax.tick_params(labelsize=13)
# 去除colorbar标题(因为子图已有标签)
cb.set_label('')
save_path = r'G:/fig/700dpi.png'
# 保存图像(调整关键参数)
fig.savefig(
save_path,
dpi=700, # 设置分辨率
bbox_inches='tight', # 去除多余白边
facecolor='white', # 背景色(默认透明,可选)
format='png' # 格式(也支持jpg/pdf等)
)
plt.show()
虽然技术上不难,但这张图的细节我也微调了好久,在这里存一下吧~
发布评论