2005-2025全国逐日平均气温栅格数据集构建:从站点数据到空间插值全流程
2026/9/10 18:39:15 网站建设 项目流程

做气候变化、农业区划、生态模型这类项目的人,手头最缺的往往不是算法,而是一份能直接用、精度过得去的长时间序列气温数据。2005到2025年,整整21年,每天一张全国范围的平均气温栅格图,这就是一套能支撑很多研究的基础底图。我是做GIS和气象数据处理出身的,这几年帮好几个团队搭建过类似的数据集,今天就把从数据源处理、空间插值到质量检验的完整思路和实操流程拆开讲。这篇东西适合搞气候分析的研究生、做灾害评估的工程师,也包括那些已经买了站点观测数据、但不知道怎么能转成栅格产品的人,按照文中的流程走一遍,基本能做出可用的逐日平均气温栅格数据集。

1. 数据需求与方案设计:先把用途搞明白,再做技术选型

1.1 站点数据到栅格数据,本质是从“点”到“面”的跨越

气象站观测得到的是单个点的气温数值,但气候模型、作物模型、生态模型需要的往往是空间连续的气温场。栅格数据把研究区划分为规则网格,每个像元存储一个气温值,这样才能和土地利用、NDVI、人口密度等其它栅格数据做逐像元运算,也才能按流域、行政区、任意多边形去做统计汇总。我经常打一个比方:站点数据像是十几个温度计挂在房间不同角落,栅格数据则是给整个房间铺了一张连续的“温度地图”。没有这张地图,你只能回答“某某站今天多少度”,有了它,才能回答“某个区域平均多少度、哪里出现了低温冷害”。

2005到2025年这个时间窗口选得很有讲究,21年跨度的逐日数据,既能用来算气候态平均值,也能分析极端事件的频率变化,还能支撑农业积温、采暖制冷度日等应用。相比只做典型年份(比如2008年雪灾、2013年高温),长序列逐日数据最大的优势在于可以灵活聚合——你要月平均就从逐日平均,你要生长期积温就按日累加,统计口径完全在自己手里。

1.2 日均温的统计口径,直接决定数据能不能复用

做逐日栅格之前,必须先把“日平均气温”的定义讲清楚。国内气象业务上通常用一天中02时、08时、14时、20时四次定时观测的算术平均值,但很多科研用户拿到的资料里只有日最高和日最低气温,这时会采用简化的二时次平均,也就是最高气温与最低气温的平均值(Tmean=(Tmax+Tmin)/2)。这两种口径的结果并不完全等价:四次观测平均更接近真实日均温,而最高最低平均在锋面过境、强对流天气下误差会明显偏大,山区夏季午后甚至能差出2到3摄氏度。

所以方案设计的第一步不是选插值方法,而是定口径、写文档。我的做法是无论最终用哪种口径,都会在数据说明文件里明确标注,并且在文件名里体现,比如使用最高最低平均的可以叫Tmaxmin,使用四次平均的可以叫T4obs。否则数据传递两轮之后,使用者根本不知道手里的日均温是怎么算出来的,后续做趋势分析或者积温计算都会埋下隐患。

2. 数据源选型与预处理:地基不牢,后面全是白搭

2.1 数据源选择:观测资料、再分析资料如何取舍

构建逐日气温栅格,数据源大致有三类:国家气象站逐日观测数据、区域自动站加密观测数据、再分析资料(如ERA5)。国家级站点空间分布相对均匀但密度不高,全国大约2400多个站,西部稀疏、东部密集,用于全国尺度插值时精度基本够用;区域自动站密度高,但历史一致性差,2010年前的站点数量远少于现在,而且观测仪器、维护水平参差不齐,早期数据有明显噪声。再分析资料(如ERA5)的好处是空间连续、时间序列完整,但它是模式同化产物,近地面气温在复杂地形、极端天气事件中的偏差可达4摄氏度以上,直接拿来当“真值”用容易出问题。

我的建议是:长时序气候网格产品,以国家级站观测数据为骨架,区域自动站只做验证和局地修正。不要不同来源混着插,否则站点密度在时间上剧烈变化,插值结果会出现虚假的年际波动。站点的密度不均匀问题也要正视:青藏高原、新疆南部站点稀疏,插值结果在这些区域的不确定性就是大,这不是换插值方法能解决的,需要在成果报告中写明“哪些区域可信、哪些区域仅供参考”。

2.2 质量控制处理:缺测、异常值、台站迁移必须逐一排查

站点观测数据从来不是拿来就能用的,质量控制是整套流程里最花时间、也最容易被低估的一步。我处理过不少数据集,第一个遇到的问题就是缺测。单站连续缺测不超过5天的,可以用相邻站回归或气候值插补;连续缺测超过5天的,我倾向于在插值当天直接剔除该站,而不是强行用统计方法填补——长时间缺测后“脑补”出来的值,会以虚假的空间平滑掩盖真实气候事件。

异常值检测我通常用双保险:第一道是百分位法,计算该站历史同期(比如过去21年的同一天前后各15天)的0.01至99.99百分位,超出范围的标记为可疑;第二道是空间一致性检测,将待检站值与周围100公里内站点的值做对比,如果偏差超过了同期空间标准差的三倍,就标记为异常,交给人工判断。台站迁移的问题也很隐蔽,2005到2025期间不少台站搬迁过,迁站后海拔、周围环境都变了,气温序列会出现系统性台阶。查询台站元数据、标记断点、必要时分段处理,这一步偷懒,后面插值出来的空间分布就会在断点年份出现一圈古怪的“突变”。

3. 空间插值核心细节:如何在复杂地形下把精度挤出来

3.1 插值方法对比:IDW、克里金与回归残差法

空间插值方法的选择,直接决定栅格数据在站点稀少区域的表现。反距离权重(IDW)原理简单、计算快,但它有一个天生的毛病——“牛眼效应”,也就是站点周围会出现同心圆状的数值伪影,在站点分布不均时会非常难看。普通克里金通过半变异函数建模空间自相关性,能给出插值误差估计,理论上比IDW更优雅,但逐日数据量巨大,每天重新拟合半变异函数很耗时,而且克里金对非平稳过程(比如气温随海拔的确定性变化)并不敏感,如果研究区横跨青藏高原和华北平原,直接普通克里金插值会把山地和平原混为一谈。

我自己最推荐的是“多元回归+残差插值”的组合方案:先用气温与经纬度、海拔建立回归模型,拟合出大尺度气候趋势面(这部分抓住了“随纬度升高变冷、随海拔升高变冷”的确定性规律),再对回归残差做IDW或克里金插值(这部分抓住局地小尺度波动),最后两者相加。这个方案在复杂地形区的表现,实测下来比单纯的IDW或克里金有明显优势,而且计算量可控。简单说就是把“确定性趋势”和“随机剩余”分开处理,各有各的方法,而不是一锅烩。

3.2 气温直减率:复杂地形插值的胜负手

全国尺度逐日气温插值,最难的不是算法本身,而是如何把地形的影响“塞”进去。对流层大气中气温随海拔升高而降低,平均直减率大约是0.6摄氏度每100米,但这个值并非固定不变——夏季、午后、湿润条件下直减率偏小,冬季、夜间、干燥条件下偏大,山区甚至会出现逆温层。用固定直减率做校正,夏季山区午后误差能到5摄氏度以上。

实操中我一般这样处理:把全年分组,每组内建立气温与经纬度、海拔的逐步回归方程,把海拔系数作为“当日直减率的经验估计”。比如按候(5天)分组,一年73组,每组拟合一次回归,就能捕捉直减率的季节变化。然后对残差插值,再到高分辨率DEM上逐像元使用当日回归方程计算趋势面。这样一来,每个像元的气温都包含了海拔校正,山脉走向、河谷盆地的局部气候特征都能呈现出来。这个方法比固定直减率略麻烦,但精度提升非常明显,尤其是做积温、低温冷害这类对温度绝对值敏感的应用,值得多花这一步。

3.3 分辨率和坐标系统的选择

分辨率不是越高越好。全国范围的逐日栅格,如果做到1公里分辨率,数据量约为960万个像元,Float32单精度存储,一天大约38MB,21年累计超过280GB;如果做到5公里,一天大约6MB,21年总计大约50GB。对大多数气候统计和区域评估研究来说,5公里分辨率已经足够,但如果要结合高分辨率土地利用做精细化农业区划,1公里版本会更合适。我的建议是交付两种规格:一套5公里用于快速分析和长期趋势统计,一套1公里用于精细化应用,两套数据用同一条处理流程生成,保证一致性。

坐标系统方面,全国范围我推荐使用Albers等积圆锥投影或Lambert等角圆锥投影做面积计算和空间统计,发布时同时提供WGS84地理坐标的GeoTIFF版本,方便在GIS软件里直接叠加其它数据。有一个细节容易被忽略:栅格数据必须确保像元对齐,不同时期生成的两张栅格,如果投影参数或像元左上角坐标有微小差异,后续逐像元运算时就会出现错位,表现为时间序列上明显的“锯齿”。我在批处理中会把投影参数写死在一个配置文件里,每次生成都从同一个模板复制地理变换信息,这样就不会出现对齐问题。

4. 批处理工作流实操:从原始站点数据到逐日栅格

4.1 全流程步骤拆解

整个批处理流程我分成六个环节,每个环节都有明确的输入输出,方便中途检查:

  1. 数据清洗标准化:统一站点编号、日期格式、字段命名,把各来源数据整理成统一CSV或Parquet格式。
  2. 计算日平均气温:按既定口径计算逐日平均温,同时保留最高、最低气温字段备用。
  3. 分组拟合回归趋势面:按候或月分组,对每组建气温与经纬度、海拔的回归方程。
  4. 残差空间插值:对每个站点当日观测值与回归趋势面的差值做IDW或克里金插值,生成残差栅格。
  5. 叠加生成最终栅格:用回归方程在DEM上计算趋势面栅格,加上残差栅格,得到当日的最终气温栅格。
  6. 质量检查与归档:检查极值范围、空间分布、与站点实测值的差异,生成质量报告,再归档保存。

图示这个流程,其实就像做一道菜:清洗是备菜,回归趋势面是调底味,残差插值是加 garnish,最后叠加起锅。每一步都有独立的中间产物,哪一步出了问题都能回溯定位。

4.2 Python实现思路与关键代码

我主要用Python做整套流程,核心库是GDAL、NumPy和PyKrige,插值前的数据管理用Pandas。下面给一个残差插值加趋势面叠加的核心代码片段,抛砖引玉:

import numpy as np import pandas as pd from osgeo import gdal from scipy.interpolate import griddata def interpolate_daily_tmaxmin(stations_df, dem_path, out_path, date_str): """ stations_df: 包含 lon, lat, elev, tmean 的DataFrame dem_path: 与目标栅格同投影的DEM路径 out_path: 输出GeoTIFF路径 """ # 读取DEM ds = gdal.Open(dem_path) geotransform = ds.GetGeoTransform() elev = ds.ReadAsArray().astype(np.float32) rows, cols = elev.shape # 网格坐标 lon0, lat0 = geotransform[0], geotransform[3] xres, yres = geotransform[1], geotransform[5] lon_grid = lon0 + (np.arange(cols) + 0.5) * xres lat_grid = lat0 + (np.arange(rows) + 0.5) * yres lon_mesh, lat_mesh = np.meshgrid(lon_grid, lat_grid) # 1. 拟合回归趋势面:tmean ~ lon + lat + elev X = np.column_stack([stations_df['lon'], stations_df['lat'], stations_df['elev']]) y = stations_df['tmean'].values X_design = np.column_stack([np.ones(len(X)), X]) coef, _, _, _ = np.linalg.lstsq(X_design, y, rcond=None) # 2. 计算残差 fitted = X_design @ coef residual = y - fitted # 3. 残差空间插值(IDW,也可以用克里金) points = stations_df[['lon', 'lat']].values # 注意:全国范围用投影坐标更好,这里简化为经纬度 grid_res = griddata(points, residual, (lon_mesh, lat_mesh), method='linear') # 4. 计算趋势面并叠加残差 trend = coef[0] + coef[1]*lon_mesh + coef[2]*lat_mesh + coef[3]*elev result = trend + grid_res # 5. 写出GeoTIFF driver = gdal.GetDriverByName('GTiff') out_ds = driver.Create(out_path, cols, rows, 1, gdal.GDT_Float32) out_ds.SetGeoTransform(geotransform) out_ds.SetProjection(ds.GetProjection()) out_band = out_ds.GetRasterBand(1) out_band.WriteArray(result) out_band.SetNoDataValue(-9999.0) out_ds.FlushCache() print(f"{date_str} 完成") # 示意调用 # for date_str in date_list: # stations_df = load_stations(date_str) # interpolate_daily_tmaxmin(stations_df, dem_path, out_path, date_str)

这段代码有几个细节需要注意。第一是网格坐标的构建,用(np.arange(cols)+0.5)*xres是取像元中心,而不是像元边界,这个细节很多人会忽略,但它直接影响栅格与站点的空间匹配关系。第二是lstsq求解回归系数,用最小二乘一次算完,比用sklearn的LinearRegression更省内存,在逐日循环里能快不少。第三是残差插值用scipy的griddata,方法选linear,它在边界处会外插产生NaN,后续要用最近邻或者填-9999处理。

4.3 大数据量下的并行与存储方案

2005到2025年大约7660天,逐日插值如果单线程跑,每个文件算它3到5秒,一天天跑下来也要七八个小时,加上回归拟合和IO,整体耗时差不多一两天。实际生产环境中,我一般用Python的multiprocessing或者直接上分布式调度,把7660天的任务按年份拆给多个进程,16核机器一晚上就能跑完一年半载的数据。注意并行时要小心临时文件夹的写入冲突,给每个进程分配独立的中间目录,最后再由一个汇总任务做质量检查和归档。

存储层面,逐日GeoTIFF文件数量会达到七千多个,管理起来要规范目录结构。我的习惯是year/month/date_tmaxmin_1km.tif这样分层,并同时生成NetCDF格式的集合文件,把所有天的数据打包到一个NetCDF里,维度为(时间, 纬度, 经度),方便用xarray直接做时间维度的切片和统计。NetCDF还有一个好处是能压缩存储,一日气温数据用NetCDF压缩后只有GeoTIFF的1/3到1/5,适合长期保存。

5. 数据质量检验与常见问题排查

5.1 交叉验证怎么做才靠谱

栅格数据生成之后,不能看一眼“颜色好看”就完事,必须做定量验证。最常用的方法是交叉验证:把站点分成训练集和验证集,用训练集建模型插值,在验证集站点上比较预测值与实测值。我一般用十折交叉验证,计算平均绝对误差(MAE)、均方根误差(RMSE)和决定系数(R2)。对这个全国逐日气温数据集,理想情况下MAE在1摄氏度以内、RMSE在1.5摄氏度以内,复杂地形区(如横断山区)和站点稀疏区(如藏北高原)的误差会明显偏大,这要在数据文档里单独说明。

除了数值指标,空间分布合理性检查也很重要。我会随机抽查几十天,把栅格结果和站点实测值叠加显示,重点看三点:一是等温线是否沿山脉走向、海岸线走向延伸,二是盆地、谷地是否出现明显的局地高温或低温中心,三是高海拔山区是否比周边明显偏冷。这几年检查中,我发现最常见的问题出在“牛眼效应”上,尤其在站点密集但数据质量差的地区,解决方法是改用克里金或增加趋势面回归的惩罚,减弱孤立点的影响。

5.2 时间序列一致性:最容易被忽略的坑

逐日数据最怕的是时间不一致。我们做的是21年长序列,任何某个时间段内的插值参数、站点数量、质量控制的改变,都可能在年际对比中制造虚假信号。比如2010年后区域自动站大量增加,如果把这些站混入插值,2010年后的插值结果会因为站点密度上升而出现“空间图案变化”,容易被误读为气候变化。

所以我在设计流程时,会固定一个“站点集合”——即使是区域自动站参与插值,也只使用一套在2005到2025年间始终保持观测的固定站点,这样至少在空间代表性上是稳定的。但对于国家级站点来说,这部分站点密度已经固定,用它们做长序列分析不用担心站点变迁带来的伪变化。另外,检查逐年平均值、逐年极端值是否平滑过渡、没有异常跳变,这个步骤必不可少。

5.3 数据使用中的常见问题速查

整理几个使用这套数据时最常遇到的问题,做成了速查表:

问题现象可能原因处理建议
河谷、盆地区域栅格值异常偏高站点多位于河谷,插值时高密度站点主导趋势面在地形相对高程大的区域,使用回归残差法并加大海拔权重
局部等温线呈同心圆状IDW插值产生的牛眼效应改用克里金,或对站点做聚类抽稀后再插值
同一天不同版本数据值不同日平均气温统计口径不同核对数据文件名和元数据,统一口径后再比较
时间序列上某年出现整体跳变站点集合变化或台站迁移检查该年份站点元数据和插值参数是否变更
与站点实测值相比,栅格值在山区偏差大站点稀疏、直减率估计不准使用当日分组回归的直减率,而不是固定值
栅格间配准错位投影参数或像元对齐不一致统一从同一模板复制地理变换参数

这些坑我前前后后都踩过,尤其是牛眼效应和口径混乱,耗费了不少时间。好的数据集必须自带元数据文档,把处理口径、站点来源、误差情况讲清楚,这份文档的价值有时比数据本身还高。

6. 栅格数据的通用扩展应用场景

6.1 与其它栅格产品的叠加分析

既然做出来的是标准栅格数据,它和市面上常见的栅格数据产品天然兼容,比如热搜词里提到的“全国城市形态栅格数据集”、地下水位栅格数据这类产品,本质都是同一套空间数据范式。气温栅格和城市形态栅格叠加,就能分析城市热岛强度与建成区密度的关系;和地下水位栅格叠加,就能评估气候变暖对地下水补给的影响。栅格数据的最大优势就在于格式统一、坐标对齐后可以直接做逐像元运算,而不需要复杂的空间匹配和拓扑运算。

这类分析我做过不少,主要经验是:数据在分析之前先检查坐标系、分辨率、范围是否一致,不一致的用重采样或裁剪统一到同一网格,然后再进入计算。很多人拿着气温是WGS84、城市形态是Albers投影的数据就直接做栅格计算器,结果因为投影不一致导致结果整体偏移了几十公里,这是很冤的错误。

6.2 从逐日到多时间尺度产品的聚合

逐日栅格数据最灵活的地方在于能够按需聚合。可以聚合成逐月平均、逐季平均、逐年平均做气候态分析;也可以计算年积温、极端高温日数、霜冻日数等指标;还可以提取任意时间段的距平和异常,做气象灾害监测。我自己的经验是,做聚合时先做好“按像元累加”的中间结果,再除以有效天数,这样能处理缺测像元的问题——在某个像元上如果某几天没有值,累加时按有效天数计数,最后除以计数即可,避免把缺测当成0值算进平均。

对绝大多数研究者来说,拿到标准逐日气温栅格,相当于获得了一个可以自由加工的原材料,而不是一个固定不可变的成品。这也是我坚持把整套方法论写出来的原因——授人以鱼不如授人以渔,有了流程和数据,后续的所有扩展分析都成了顺理成章的填空题。

7. 一点经验之谈

数据集的构建流程说完了,最后聊一点跟技术无关、但又比技术更磨人的体会。做2005到2025年这个跨度的时间序列栅格数据,真正的难点从来不是插值算法或者Python代码,而是耐心——耐心地处理每一个异常值、耐心地核对每一个年份的站位变化、耐心地给每一步操作写文档。我见过太多人花了大力气把数据跑出来,却因为不愿意写元数据说明,最后自己半年后再看都不确定当时处理的细节。

另外一个很深的体会是:先做小范围验证,再铺开到全国。刚开始做的时候,我直接在Python里循环跑全年,结果跑到第五天发现趋势面系数异常,返工浪费了半天时间。后来改成先挑两个月做全流程验证,把误差、耗时、存储量全部确认一遍,再批量跑剩下来的7600多天,效率反而高出一大截。做这类工程化的数据产品,稳定压倒一切,慢就是快。

最后一句话送给大家:做气候变化空间分析,数据质量永远大于分析方法。一套经过严格质量控制、口径清晰、文档完备的气温栅格数据集,哪怕只用简单的趋势分析,也能得出可信的结论;反过来,即使算法再花哨,垃圾进垃圾出,结果也毫无意义。希望这篇思路和流程的梳理,能帮到正在跟气温数据较劲的你。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询