Python实现中国气温时空变化趋势分析:从数据处理到可视化

📅 2026/7/30 15:24:00 👁️ 阅读次数 📝 编程学习
Python实现中国气温时空变化趋势分析:从数据处理到可视化

1. 项目概述:从一张图到一套方法论的跨越

最近在整理一个关于中国近六十年气温变化趋势的项目,这其实源于一个很实际的需求。当时手头有一堆从国家气象信息中心下载的站点数据,时间跨度从1960年到2020年,覆盖全国。老板想让我快速出一张图,直观展示一下这六十年里,中国不同地方的气温到底是怎么变的,是整体都在变暖,还是有地方变暖快,有地方变暖慢,甚至有没有地方在变冷?这个看似简单的“画张图”任务,一旦深入进去,就变成了一个涉及数据处理、统计方法、空间分析和机理探讨的完整分析流程。它绝不仅仅是计算一个全国平均的升温速率那么简单,而是要揭示变化趋势在空间上的“不均匀性”——也就是时空差异,并尝试去理解背后可能的原因。这对于理解区域气候响应、评估生态环境影响乃至制定适应性策略,都有很实在的参考价值。无论你是刚开始接触气候数据分析的学生,还是需要处理类似时空序列的科研或行业人员,这套从数据到结论的思路和实操细节,或许都能给你一些直接的参考。

2. 核心思路与方案选型:为什么是“线性趋势”?

面对长达61年的气温时间序列,首要问题是:如何量化其变化趋势?气象气候学中常用的趋势分析方法有线性回归、滑动平均、Mann-Kendall检验等。这里选择一元线性回归来计算线性趋势,是最直接、最透明也最被广泛接受的方法。

2.1 为什么选择线性趋势分析?

它的核心是拟合一条直线(y = a + b*x)穿过每年的气温值点。其中,斜率b就是我们要的线性趋势,单位通常是°C/10年,表示每十年气温的平均变化量。选择它主要基于几点考量:

  1. 直观可比:结果是一个简单的数值,能清晰比较不同站点或区域变暖速率的快慢。比如,b=0.25°C/10年意味着每十年升温0.25度,非常直观。
  2. 稳健性强:对于呈现单调上升或下降趋势的数据(如全球变暖背景下的气温),线性拟合能很好地捕捉其长期方向性变化,受短期年际波动(如厄尔尼诺事件)的影响相对较小。
  3. 计算与解释简便:算法成熟,几乎所有数据分析工具(Python的numpy.polyfit、R的lm、甚至Excel)都能轻松实现,结果也易于在学术论文或报告中呈现和解释。

2.2 方案技术栈选型

整个项目流程可以拆解为:数据获取 -> 质控与预处理 -> 网格化 -> 逐格点趋势计算 -> 空间分析与可视化 -> 影响因素探讨。对应的工具选择如下:

  • 数据处理与计算Python+Pandas/Xarray。Python生态在科学计算和地理数据处理方面有绝对优势。Pandas处理站点表格数据(CSV格式)非常高效,而Xarray专门为处理带标签的多维数组(如时间x经度x纬度的网格数据)设计,是处理气候网格数据的“神器”。
  • 趋势计算:使用scipy.stats中的linregress函数或numpy.polyfit。它们不仅能返回斜率(趋势),还能返回截距、R²(拟合优度)、p值(显著性检验)等全套统计信息。
  • 空间分析与可视化Cartopy+Matplotlib/Geopandas。Cartopy是专业的地图制图库,能轻松处理各种地理投影(本项目使用等经纬度投影或兰伯特投影即可),绘制国界、省界、海岸线。Matplotlib进行基础绘图,若需操作行政区划矢量数据,可结合Geopandas。
  • 统计检验:除了线性回归自带的p值,对于空间场,可能还需要进行Mann-Kendall趋势检验(非参数检验,对数据分布无要求)和Sen‘s斜率估计,这可以使用pymannkendall库实现,作为对线性回归结果的补充验证。

注意:线性趋势假设变化是匀速的,但实际气候系统复杂,变化可能存在阶段性(如快-慢-快)。因此,线性趋势是对长期变化的一种“概括”,在报告中需明确指出这一点,必要时可补充分段趋势分析。

3. 数据获取、质控与网格化处理

这是所有分析的基础,也是最耗时、最容易出错的环节。原始数据通常来自国家气象信息中心或全球再分析数据集(如CRU、ERA5)。这里假设我们拥有的是中国地面国际交换站的气温数据。

3.1 数据质控(Quality Control, QC)

原始数据不可避免存在缺测、可疑值和均一性问题。必须执行严格的QC流程:

  1. 格式规整化:将不同站点的数据(可能分散在不同文件)合并为一张大表,列包括:站号、经度、纬度、年份、月/年平均气温。使用Pandas的concatmerge功能。
  2. 处理缺失值:对于个别年份的缺失,可采用线性插值或前后年份平均填补。但对于连续缺失超过5年的站点,建议谨慎对待或剔除该站点该时段的数据。在Pandas中,可以用interpolate()进行插值,或用dropna()设定阈值删除。
  3. 剔除异常值:根据气候学常识设定物理合理范围。例如,中国大部分地区年平均气温应在-10°C至30°C之间。对于超出此范围的明显错误数据,应查找元数据或直接标记为缺失。可以使用条件筛选:df.loc[(df[‘temp’] < -10) | (df[‘temp’] > 30), ‘temp’] = np.nan
  4. 均一性检验(可选但重要):站点可能因迁址、仪器更换、观测环境改变而产生非气候因素的“跳跃”。这需要用到专门的均一化检验方法(如RHtests、MASH),工作量较大。对于初步趋势分析,可以注明未进行均一化处理是结果的一个不确定性来源。

3.2 站点数据网格化

为了得到连续的空间分布图,需要将离散的站点数据插值到规则的经纬度网格上。常用方法有反距离加权(IDW)、克里金(Kriging)或最近邻插值。

  • 实操选择:对于气温这种空间连续性较好的变量,反距离加权(IDW)是一个简单有效的选择。可以使用scipy.interpolate.griddata函数。
  • 关键参数
    • grid_lon, grid_lat:定义目标网格的经纬度向量,例如np.arange(70, 140, 0.5)定义东经70-140度,分辨率0.5度的经度网格。
    • method=‘linear’:这里选择线性插值,对于缺测值较多的区域,可考虑method=‘nearest’
    • 注意事项:插值前务必确保站点分布相对均匀。对于青藏高原西部等站点极稀疏区域,插值结果不确定性很大,通常需要在图中以阴影或打点标注,或直接掩膜掉。
# 示例代码片段:使用xarray和scipy进行网格化(简化版) import xarray as xr import numpy as np from scipy.interpolate import griddata # 假设已有包含所有站点多年平均气温的DataFrame `df`,列有['lon', 'lat', 'temp_avg'] # 创建目标网格 grid_lon = np.arange(70, 140.1, 0.5) grid_lat = np.arange(15, 55.1, 0.5) grid_lon_mesh, grid_lat_mesh = np.meshgrid(grid_lon, grid_lat) # 准备插值 points = df[['lon', 'lat']].values values = df['temp_avg'].values # 执行IDW插值 (这里用线性插值作为示例) grid_temp = griddata(points, values, (grid_lon_mesh, grid_lat_mesh), method='linear') # 将结果包装成xarray DataArray,方便后续处理 da_grid = xr.DataArray(grid_temp, dims=['lat', 'lon'], coords={'lat': grid_lat, 'lon': grid_lon})

4. 逐格点线性趋势计算与显著性检验

将1960-2020年每年的网格数据(假设已处理为年均温)堆叠成一个时间x纬度x经度的三维数组。接下来的任务是对每一个(纬度, 经度)格点上的61个时间点数据,进行一元线性回归。

4.1 批量计算趋势场

使用xarrayapply_ufunc功能或numpy的向量化操作,可以高效地一次性计算所有格点的趋势。

import numpy as np import xarray as xr # 假设 `da` 是一个xarray DataArray,维度为 ('year', 'lat', 'lon') # 计算时间维度(年份序列),用于回归 years = da['year'].values.astype(float) # 确保是浮点数 # 为每个格点计算趋势(斜率) def linear_trend(y): # y 是一个一维时间序列 if np.isnan(y).any(): # 如果包含NaN,返回NaN return np.nan # 使用numpy的polyfit进行一阶多项式拟合,返回斜率和截距 slope, intercept = np.polyfit(years, y, 1) return slope * 10 # 转换为 °C/10年 # 沿时间维度应用函数 trend_slope = xr.apply_ufunc(linear_trend, da, input_core_dims=[['year']], vectorize=True, dask='parallelized', output_dtypes=[float])

4.2 趋势显著性检验

计算出的趋势可能只是随机波动造成的。需要进行统计检验,判断趋势是否显著(通常认为p值<0.05或0.1为显著)。线性回归本身可输出p值。

from scipy import stats def linear_trend_with_pvalue(y): if np.isnan(y).any() or len(y) < 2: return np.nan, np.nan slope, intercept, r_value, p_value, std_err = stats.linregress(years, y) return slope * 10, p_value # 返回趋势和p值 # 应用函数,得到趋势场和p值场 trend_result = xr.apply_ufunc(linear_trend_with_pvalue, da, input_core_dims=[['year']], vectorize=True, dask='parallelized', output_core_dims=[[], []], # 输出两个标量场 output_dtypes=[float, float]) trend_slope = trend_result[0] p_value = trend_result[1] # 创建显著性掩膜 (p < 0.05) significant_mask = p_value < 0.05

在最终的空间分布图上,通常只给通过显著性检验(如p<0.05)的区域填充颜色,不显著的区域以灰白色或打点表示,这样图形传达的信息更科学严谨。

5. 时空差异分析与可视化呈现

得到趋势场和显著性场后,就进入了核心的分析与解读阶段。

5.1 空间差异特征解读

通过绘制趋势空间分布图,我们能直观看到:

  • 整体格局:中国大部分地区很可能呈现一致的增温趋势,印证全球变暖的背景。
  • 地域分异
    • 北方 vs 南方:通常北方(尤其是东北、西北)的增温趋势会明显强于南方(如华南)。这被称为“北极放大效应”在中高纬度大陆的体现。
    • 高原 vs 平原:青藏高原作为高海拔地区,其增温速率往往显著高于同纬度东部平原,即“高海拔放大效应”。
    • 季节差异:冬季的增温趋势通常比夏季更显著。这需要分别计算各季节(DJF冬季, MAM春季, JJA夏季, SON秋季)的趋势进行分析。
  • 局部异常:可能存在某些局部区域趋势较弱甚至为负(降温),需要结合当地地形、下垫面(如城市化、水体、植被变化)进行具体分析。

5.2 多维度可视化技巧

一张好的图能自己说话。使用Cartopy和Matplotlib绘制时要注意:

  1. 色彩方案:选择发散色系(如RdBu_r)来表示升温和降温。中心色(白色或浅色)代表零趋势,红色系代表升温,蓝色系代表降温。务必添加颜色条。
  2. 叠加显著性:使用hatch(填充图案,如斜线)或增加点阵,在不显著的区域叠加一层图案,与显著的纯色填充区形成视觉区分。
  3. 添加地理信息:清晰绘制国界线、省界、主要河流、山脉标注,增加图件的可读性和专业性。Cartopy的add_feature功能很方便。
  4. 多子图布局:可以并排绘制全年、春、夏、秋、冬的趋势图,便于对比季节差异。
import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.pyplot as plt # 创建地图投影 proj = ccrs.PlateCarree() fig, ax = plt.subplots(figsize=(12, 8), subplot_kw={'projection': proj}) # 设置地图范围 ax.set_extent([70, 140, 15, 55], crs=proj) # 添加地理特征 ax.add_feature(cfeature.COASTLINE, linewidth=0.8) ax.add_feature(cfeature.BORDERS, linewidth=0.5, linestyle=':') ax.add_feature(cfeature.RIVERS, edgecolor='blue', linewidth=0.5, alpha=0.5) ax.add_feature(cfeature.LAKES, edgecolor='blue', facecolor='none', linewidth=0.5) # 绘制趋势填色图,仅对显著区域填色 # 假设 trend_slope 是趋势场, significant_mask 是显著性掩膜 plot_data = trend_slope.where(significant_mask) # 只保留显著格点 im = ax.pcolormesh(trend_slope.lon, trend_slope.lat, plot_data, cmap='RdBu_r', vmin=-0.5, vmax=0.5, transform=proj) # 设定合理的色标范围 # 添加颜色条 cbar = fig.colorbar(im, ax=ax, orientation='horizontal', pad=0.05, shrink=0.8) cbar.set_label('Temperature Trend (°C/decade)', fontsize=12) # 添加标题 ax.set_title('Linear Trend of Annual Mean Temperature in China (1960-2020)\n(Stippling indicates trends significant at p<0.05 level)', fontsize=14, pad=20) plt.tight_layout() plt.show()

5.3 时间序列分解

除了空间图,选取几个代表性区域(如华北平原、青藏高原、四川盆地、东北地区),绘制其1960-2020年的气温时间序列及拟合的趋势线,非常具有说服力。可以直观展示不同区域升温的“斜率”差异,以及年际波动的幅度。

6. 影响因素探讨:从统计关联到物理机制

分析出时空差异后,最关键也最具挑战性的一步是解释“为什么”。这里需要从统计分析和物理机理两个层面入手。

6.1 潜在影响因子梳理

影响中国气温变化趋势空间差异的因素是多元且交织的:

  1. 大尺度环流与海温
    • 北极涛动(AO)/北大西洋涛动(NAO):影响冬季风强度,从而影响中国东部尤其是北方的冬季气温。
    • 厄尔尼诺-南方涛动(ENSO):通过遥相关影响东亚季风,对夏季降水气温格局有重要调制。
    • 太平洋年代际振荡(PDO):其相位转换可能与我国气候趋势的阶段变化有关。
  2. 区域气候反馈
    • 雪冰-反照率反馈:在北方和高原,变暖导致积雪减少,地表反照率降低,吸收更多太阳辐射,进一步加剧变暖(正反馈)。这是北方和高原增温快的重要原因。
    • 水汽-温室效应反馈:变暖导致大气持水能力增加,水汽本身是强温室气体,形成另一个正反馈,但其效应空间分布较均匀。
  3. 下垫面与人类活动
    • 城市化(城市热岛效应, UHI):这是导致局部,特别是东部城市群区域增温趋势显著偏强的主要人为因素。需要将站点按城市站、乡村站分类,对比其趋势差异。
    • 气溶胶排放:硫酸盐等气溶胶有冷却效应,可能部分抵消温室气体的增温效应。我国历史上排放量大,其空间分布不均可能影响趋势格局。
    • 土地利用/覆盖变化:如退耕还林还草、沙漠化等,通过改变地表能量平衡(反照率、蒸散)影响局地气候。

6.2 统计关联分析方法

要定量探讨这些因子与气温趋势的关系,可以进行空间相关性分析或回归分析。

  • 空间相关场分析:计算气温趋势场与另一个空间场(如城市化率变化场、气溶胶光学厚度趋势场、积雪日数趋势场)的格点对格点的相关系数。可以使用xarraycorr函数。
  • 典型区域对比:将站点或格点按某种属性分组(如“城市组” vs “乡村组”;“高原组” vs “平原组”),分别计算各组平均的气温时间序列和趋势,然后进行对比和差异显著性检验(如t检验)。
  • 多元线性回归:以每个格点的气温趋势为因变量,以多个潜在影响因子(如纬度、海拔、城市化指数、气溶胶趋势等)的值为自变量,建立多元线性回归模型,评估各因子的贡献率。这需要将各因子数据统一插值到相同网格。

实操心得:影响因素分析最容易陷入“相关即因果”的误区。例如,发现气温趋势与城市化指数空间相关性高,不能直接断言就是城市化导致的,因为两者可能都受第三因素驱动,或存在复杂的相互作用。此时,需要结合物理机制文献、更精细的观测(如城乡对比站)或模式模拟(如关闭城市化的敏感性试验)来综合论证。在报告中,应谨慎表述为“XX因子可能与观测到的趋势差异有关联”,并指出分析的局限性。

7. 常见问题、不确定性分析与避坑指南

在实际操作中,会遇到各种预料之外的问题。以下是一些典型问题及解决思路:

7.1 数据相关问题

  • 问题:站点数据缺失严重,尤其早期(1960-1970年代)西部和高原地区。
    • 应对:1) 在网格化时,对于站点密度低于某个阈值的区域,输出结果时予以标注或留白。2) 考虑使用经过质量控制和插值的格点化再分析数据(如CRU TS、CN05.1)作为补充或对比。3) 明确将数据覆盖度不足作为结论的不确定性来源之一。
  • 问题:计算出的局部降温趋势是否真实?
    • 排查:首先检查该格点附近站点的原始数据序列,看是否有未剔除的异常值或迁站造成的跳跃。其次,查看该区域的土地利用变化(如新建大型水库、植被恢复),可能产生局地冷却效应。最后,进行统计显著性检验,如果降温趋势不显著(p>0.1),则可能只是随机波动。

7.2 方法与计算问题

  • 问题:线性趋势对序列起点和终点非常敏感。
    • 应对:进行敏感性分析。例如,分别计算1960-2020、1970-2020、1960-2010等不同时间段的趋势,观察趋势大小和空间格局是否稳定。如果变化很大,说明结论对时间段选择敏感,需在报告中说明。
  • 问题:Mann-Kendall检验结果与线性回归的显著性结果不一致。
    • 解读:M-K检验是非参数检验,对异常值不敏感,但检验的是“是否存在单调趋势”;线性回归t检验是参数检验,检验“斜率是否显著不为零”。两者前提不同,结果可能略有差异。通常以线性回归结果为主,M-K结果作为稳健性参考。若差异巨大,需检查数据是否符合线性回归的正态性、独立性等假设。

7.3 可视化与解读问题

  • 问题:颜色条范围设置不当,导致图形细节丢失或误导。
    • 技巧:不要使用默认的自动范围。先计算整个趋势场的均值、标准差和极端值(如1%、99%分位数)。设置vminvmax时,可以基于均值±2倍标准差,或直接使用1%和99%分位数,以突出主体空间格局,避免被极少数极端格点“拉平”整个色阶。
  • 问题:如何向非专业读者解释“每十年升温0.3°C”的意义?
    • 技巧:使用生活化的类比。例如,“过去60年,北京地区的年平均气温上升了约1.8°C,这相当于气候带向北推进了200多公里”或“积温的增加使得某些作物的适宜种植区向北向西扩展了”。将抽象的数字与具体的生态、农业影响联系起来。

这个从数据到图件再到机理解读的完整流程,其价值不仅在于产出一张漂亮的趋势空间分布图,更在于构建了一套应对类似时空变化分析问题的标准化、可复现的方法论。它提醒我们,任何基于数据的结论,都必须建立在严谨的数据处理、恰当的统计方法和对不确定性的清醒认识之上。最后,记得将所有的代码、数据处理步骤和参数选择记录在脚本或笔记中,这既是科研可重复性的要求,也能在未来面对类似项目时,极大地提升你的工作效率。