目录
[二、SPI 计算核心函数解析](#二、SPI 计算核心函数解析)
[三、数据生成与 SPI 计算流程](#三、数据生成与 SPI 计算流程)
[(二)计算 SPI-1 并组织数据](#(二)计算 SPI-1 并组织数据)
[(二)干旱 / 湿润等级阈值线绘制](#(二)干旱 / 湿润等级阈值线绘制)

若觉得代码对您的研究 / 项目有帮助,欢迎点击打赏支持!需要完整代码的朋友,打赏后可在后台私信(复制文章标题发给我),我会尽快发您完整可运行代码,感谢支持!
本代码基于 Python 数据科学生态库(NumPy、Pandas、Matplotlib、SciPy),实现三大核心功能:
- 生成 1990-2022 年逐月模拟降水数据(服从 Gamma 分布);
- 计算标准化降水指数(SPI-1,即 1 个月尺度 SPI);
- 可视化 SPI-1 时间序列,标注不同干旱 / 湿润等级阈值,并保存高清晰度图片。
SPI(Standardized Precipitation Index)是衡量干旱程度的常用指标,通过将降水数据标准化为正态分布(均值 0、标准差 1),使不同地区、不同时段的降水异常程度具有可比性。
一、依赖库与环境配置
(一)导入依赖库
python
import numpy as np # 数值计算(数组操作、随机数生成)
import pandas as pd # 数据处理(时间序列、DataFrame管理)
import matplotlib.pyplot as plt # 绘图可视化
import scipy.stats as stats # 统计分析(分布拟合、分位数计算)
import os # 文件路径与目录管理
各库核心作用:
- **NumPy:**处理降水数据数组、替换无效值;
- **Pandas:**生成时间序列、组织 SPI 结果为表格格式;
- **Matplotlib:**绘制 SPI 趋势图、阈值线、图例等;
- **SciPy.stats:**拟合 Gamma 分布、计算累积概率(CDF)、转换正态分布分位数;
- **os:**创建保存目录、处理文件路径。
(二)中文字体配置
python
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示乱码(Windows系统)
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示异常
注: 不同系统需适配字体(如 macOS 用'Arial Unicode MS',Linux 用'WenQuanYi Zen Hei')。
二、SPI 计算核心函数解析
(一)函数定义与逻辑框架
python
def calculate_spi(data):
"""
简易SPI计算逻辑(核心三步):
1. 处理无效降水数据(0值替换)
2. 拟合Gamma分布(描述降水数据统计特征)
3. 转换为标准正态分布Z分数(即SPI值)
"""
# 步骤1:处理0值(降水为0时无法拟合Gamma分布,替换为极小值)
data = np.where(data <= 0, 0.01, data)
# 步骤2:拟合Gamma分布参数(shape=形状参数,loc=位置参数,scale=尺度参数)
shape, loc, scale = stats.gamma.fit(data, floc=0) # 固定位置参数为0(降水非负)
# 步骤3:计算累积概率(CDF)并转换为SPI
cdf = stats.gamma.cdf(data, shape, loc, scale) # 每个数据点的累积概率(0-1)
spi = stats.norm.ppf(cdf) # 概率转换为标准正态分布分位数(Z分数)
return spi
(二)关键算法说明
- Gamma 分布拟合: 降水数据(非零)通常服从 Gamma 分布,通过
stats.gamma.fit估计分布参数,是 SPI 计算的核心前提; - **累积概率(CDF):**表示某一降水量小于等于当前值的概率,反映降水在历史序列中的相对位置;
- 正态转换: 通过
stats.norm.ppf(分位数函数)将 [0,1] 区间的概率转换为标准正态分布的 Z 分数,即 SPI 值(SPI=0 为平均水平,正值为湿润,负值为干旱)。
三、数据生成与 SPI 计算流程
(一)生成时间序列与模拟降水数据
python
# 生成1990-2022年逐月时间索引(freq='ME'表示月末频率,修复Pandas兼容性警告)
dates = pd.date_range(start='1990-01-01', end='2022-12-31', freq='ME')
# 设置随机种子(保证结果可重复)
np.random.seed(42)
# 模拟月降水量(Gamma分布:shape=2.0,scale=50.0,均值≈shape×scale=100mm)
precip = np.random.gamma(shape=2.0, scale=50.0, size=len(dates))
- **时间序列:**共 33 年(1990-2022),每月 1 个数据点,总计 396 个数据;
- **降水模拟:**Gamma 分布的 shape 和 scale 参数决定降水分布特征,此处模拟均值约 100mm 的月降水(符合温带地区平均水平)。
(二)计算 SPI-1 并组织数据
python
# 计算1个月尺度SPI(SPI-1)
spi_values = calculate_spi(precip)
# 构建DataFrame(便于后续绘图与数据管理)
df = pd.DataFrame({'Date': dates, 'SPI1': spi_values})
DataFrame 结构: 两列(Date为时间,SPI1为对应月份的 SPI 值),行索引为数据序号。
四、可视化模块详细解析
(一)绘图基础设置
python
# 创建画布(figsize=宽×高,单位英寸;dpi=显示分辨率)
plt.figure(figsize=(16, 6), dpi=100)
# 绘制SPI-1时间序列折线
plt.plot(df['Date'], df['SPI1'], color='darkblue', linewidth=1.2, label='SPI-1')
- **画布尺寸:**16×6 英寸(宽屏适合时间序列展示);
- **折线样式:**深蓝色、线宽 1.2,标注图例为 "SPI-1"。
(二)干旱 / 湿润等级阈值线绘制
python
# 定义阈值参数:(SPI值, 颜色, 线型, 标签)
thresholds = [
(2.0, 'navy', '--', '极端湿润 (SPI ≥ 2.0)'),
(1.5, 'royalblue', '--', '严重湿润 (SPI ≥ 1.5)'),
(1.0, 'lightblue', '--', '中等湿润 (SPI ≥ 1.0)'),
(-1.0, 'orange', '--', '中等干旱 (SPI ≤ -1.0)'),
(-1.5, 'red', '--', '严重干旱 (SPI ≤ -1.5)'),
(-2.0, 'brown', '--', '极端干旱 (SPI ≤ -2.0)'),
]
# 循环绘制各阈值线(水平虚线)
for val, color, style, label in thresholds:
plt.axhline(y=val, color=color, linestyle=style, linewidth=1, label=label)
# 绘制SPI=0基准线(黑色实线,区分湿润与干旱)
plt.axhline(y=0, color='black', linewidth=0.8)
阈值标准说明(国际通用 SPI 等级):
| SPI 范围 | 等级 | 颜色 |
|---|---|---|
| ≥2.0 | 极端湿润 | 藏青色 |
| 1.5-2.0 | 严重湿润 | 宝蓝色 |
| 1.0-1.5 | 中等湿润 | 浅蓝色 |
| -1.0-1.0 | 正常 | - |
| -1.5--1.0 | 中等干旱 | 橙色 |
| -2.0--1.5 | 严重干旱 | 红色 |
| ≤-2.0 | 极端干旱 | 棕色 |
(三)坐标轴与图表样式优化
python
# 设置Y轴范围(聚焦主要异常区间,避免极端值拉伸图表)
plt.ylim(-2.8, 2.8)
# 设置X轴范围(覆盖完整时间序列)
plt.xlim(df['Date'].min(), df['Date'].max())
# 坐标轴标签与标题
plt.ylabel('SPI-1', fontsize=12)
plt.title('1990 - 2022年逐月标准化降水指数(SPI-1)', fontsize=14, pad=20)
# X轴刻度优化(每2年显示一次,旋转45°避免重叠)
xtick_dates = pd.date_range(start='1990-12-31', end='2022-12-31', freq='2YE')
plt.xticks(xtick_dates, [d.strftime('%Y-%m-%d') for d in xtick_dates], rotation=45)
# 网格线(细点线,透明度0.6,不干扰主线)
plt.grid(True, linestyle=':', alpha=0.6)
(四)图例与布局调整
python
# 图例设置(顶部居中,4列分布,带阴影边框)
plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
ncol=4, frameon=True, shadow=True, fontsize=9)
# 自动调整布局(避免标题、图例被截断)
plt.tight_layout()
**图例优化逻辑:**因阈值线较多(6 条),采用 4 列布局避免纵向占用过多空间,提升图表整洁度。
五、高清晰度图片保存功能
(一)目录创建与路径处理
python
# 定义保存目录(统一管理图片文件)
save_dir = 'SPI_figures'
# 若目录不存在则创建(避免保存失败)
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 构建保存路径(目录+文件名,支持png/jpg/svg格式)
save_path = os.path.join(save_dir, '1990-2022_SPI-1_600dpi.png')
(二)图片保存参数详解
python
plt.savefig(
save_path,
dpi=600, # 核心参数:600DPI(高清晰度,满足论文/报告使用)
bbox_inches='tight', # 自动裁剪多余空白,避免图例/标题被截断
facecolor='white', # 背景色设为白色(默认透明,适配多数文档场景)
edgecolor='none' # 去除图片边框
)
# 打印保存路径(方便用户查找文件)
print(f"图片已保存至:{os.path.abspath(save_path)}")
# 显示图片(保存后再显示,不影响图片质量)
plt.show()
- **DPI 设置:**600DPI 为学术图表常用分辨率,高于 300DPI 的 "印刷级" 标准;
- **背景色:**白色背景避免插入文档时出现透明区域与正文冲突;
- **
bbox_inches='tight':**解决 Matplotlib 默认布局下图例 / 标题超出画布的问题。
六、总结
优点:
- **流程完整:**从数据生成、SPI 计算到可视化、保存一站式实现;
- 兼容性强: 修复 Pandas 频率字符串警告(
M→ME、Y→YE),适配新版本库; - **可视化专业:**阈值线颜色与等级对应清晰,图例、刻度优化提升可读性;
- **实用性高:**支持高清晰度图片保存,满足学术与业务场景需求。
局限性与改进方向:
- **数据处理简化:**将 0 降水替换为 0.01 仅为简易处理,实际应分离 "无降水" 与 "有降水" 数据分别计算;
- **分布拟合:**未进行 Gamma 分布拟合优度检验(如 KS 检验),需验证数据是否符合 Gamma 分布;
- **实际应用:**模拟数据需替换为真实站点 / 格点降水数据,且需考虑数据缺失值填补。
七、运行结果
1990-2022 年逐月 SPI-1 计算与可视化结果
若觉得代码对您的研究 / 项目有帮助,欢迎点击打赏支持!需要完整代码的朋友,打赏后可在后台私信(复制文章标题发给我),我会尽快发您完整可运行代码,感谢支持!