资讯详情

GEE极端降水指数计算全流程:从CHIRPS数据到R95p趋势分析

📅 2026/10/17 20:58:43 | 华诺云谱 👁 阅读
GEE极端降水指数计算全流程:从CHIRPS数据到R95p趋势分析
简介这份资源是一套基于Google Earth EngineGEE与NASA GPM IMERG V07数据的极端降水分析代码包服务于遥感、水文与灾害风险研究场景也适合有一定GEE基础、希望直接复用降水分析脚本的开发者。代码完整覆盖从研究区域ROI加载到日降水构造、阈值天数统计、三天滑动窗口累计、峰值日期提取等核心流程运行后可直接输出平均日降水、最大日降水、极端降水天数等分布图层与图表可用于洪涝风险评估、流域降水过程及极端天气监测等场景。资源包共3个文件约6KB以GEE脚本inscode、HTML页面与gitignore配置为主结构轻量便于导入GEE后替换为自定义ROI直接使用。目前已有120人学习对需要快速验证GPM降水分析思路或构建降水指数工作流的读者具有实用参考价值脚本中清晰的函数边界也便于继续扩展为年际或季节尺度的降水趋势分析。1. 先用GEE把极端降水指数算出来一个比“下载—计算—再粘贴”快一个数量级的路径做极端降水分析最浪费时间的往往不是算法本身而是数据来回路。以前我拿到一个陌生流域要先从气候数据平台把近30年逐日降水栅格一格格下到本地再写脚本做年度聚合、算第95百分位阈值最后生成R95p、Rx1day、CDD这些指标。硬盘吃紧不说换一个研究区整套流程又要重跑一遍。GEE极端降水分析把这条链路直接搬到了云端在Google Earth Engine里按日期过滤CHIRPS或ERA5-Land的逐日降水集合用reduce沿时间维求阈值和累计值几分钟就能输出整个区域的极端降水指数图。你省掉的不只是下载时间还有裁剪、坐标对齐、分块读取这些体力活。这套方案适合做气候风险评估、水文设计暴雨、减灾区划的从业者和研究生前提是愿意花半小时把“时间聚合”和“数据单位”这两个坎迈过去而它们恰恰是大多数人翻车的地方。2. 降水数据产品怎么选CHIRPS / ERA5-Land / IMERG 的时空尺度与单位陷阱2.1 三种常用降水产品的GEE集合ID与适用边界极端降水指数对数据源的敏感程度比大多数人想象的高。同一场暴雨在CHIRPS里可能被平滑成一次中等强度降水在IMERG里却能抓到小时级的强对流核心。所以第一步不是写代码而是想清楚你要回答什么问题再去选数据产品。我自己的经验是长期趋势和气候态分析用CHIRPS打底它从1981年覆盖到现在0.05度分辨率约5.6公里在无站点地区也有一套完整插值ERA5-Land适合做再分析对比和长序列研究物理过程一致性好单位是米切记要换算IMERG适合做短历时强降水诊断半小时一个时次空间分辨率0.1度但对极端值的估计存在卫星反演特有的偏差。数据产品GEE集合ID空间分辨率时间分辨率降水单位主要用途CHIRPS DailyUCSB-CHG/CHIRPS/DAILY0.05度逐日mm/day气候态、长期趋势、干旱半干旱区ERA5-Land Daily AggregatedECMWF/ERA5_LAND/DAILY_AGGR0.1度逐日m需乘1000再分析对比、缺测区域补序列ERA5 Daily AggregatedECMWF/ERA5/DAILY0.25度逐日m需乘1000全球尺度、与站点再分析匹配IMERG V06 Half-HourlyNASA/GPM_L3/IMERG_V060.1度30分钟mm/h需乘以0.5并累加短历时强降水、极端小时雨强选型有一条朴素的判断标准如果你的研究区是山地或者沿海CHIRPS的站点插值会把地形抬升引起的局地暴雨抹掉如果你的目标是“近20年极端降水有没有变强”IMERG序列太短还是用CHIRPS或ERA5-Land算到2020年左右更稳。另外不要一上来就同时跑三个数据集做“对比”单位不统一时对比出来的全是假象先跑通CHIRPS一条线后面再替换数据源做敏感性分析成本最低。2.2 先做时间聚合再做极端阈值单位换算与湿日定义GEE里面最隐蔽的坑是数据产品长得像“日降水”实际不是。CHIRPS的precipitation波段直接就是mm/day拿来就能用ERA5-Land的Daily Aggregated虽然叫“日聚合”但total_precipitation的单位是米不乘以1000就直接算R95p阈值会整体偏小三个数量级IMERG更麻烦它的precipitationCal单位是mm/h每个时间步代表半小时降水率必须先累加成自然日总量再进入指数计算。我见过不只一次有人拿IMERG的半小时影像直接做集合max然后把一次30分钟强降水当成“日极端最大值”写进论文这个偏差在气候态统计里会被放大。// ERA5-Land 日降水m 转 mm并统一波段名 var era5Daily ee.ImageCollection(ECMWF/ERA5_LAND/DAILY_AGGR) .filterBounds(roi) .filterDate(studyStart, studyEnd) .map(function(img) { var mm img.select(total_precipitation).multiply(1000).rename(precipitation); return mm.copyProperties(img, [system:time_start]); }); // IMERG 半小时序列先把一天内所有时次求和再乘 0.5 得到日总量 var imergDaily ee.ImageCollection( ee.List.sequence(0, 3650).map(function(n) { var d ee.Date(studyStart).advance(n, day); var daySum ee.ImageCollection(NASA/GPM_L3/IMERG_V06) .filterBounds(roi) .filterDate(d, d.advance(1, day)) .select(precipitationCal) .sum() .multiply(0.5) .rename(precipitation) .set(system:time_start, d.millis()); return daySum; }) );上面IMERG这段是生成3650个“日影像”服务端压力比较大实际使用时建议只对目标年份或者单个汛期跑不要一口气铺满10年。计算逻辑上每个半小时时次的降水率乘以0.5小时得到该半小时的降水深度把48个时次累加就是一个自然日的总量。它的时间边界用的是UTC日期如果你的研究区在东八区日界要按当地时区做偏移否则下午到凌晨的降水会被切到错误的“日”里。我一般会在研究区先打印几个已知站点的日期对齐情况再批量展开。湿日定义也要在数据层就统一。ETCCDI的极端指数里湿日通常指日降水大于等于1mm的日子小于1mm一律视为干日。这个定义直接影响百分位阈值如果不先把干日剔除直接用全部日序列算第95百分位像中国北方这种干日占大半的区域P95阈值会被大量0值拉到0.2mm以下后面R95p算出天文数字。所以下面的代码在算阈值之前会先对集合做一次updateMask(gte(1))把湿日单独筛出来。3. 核心代码从逐日降水到R95p / Rx1day / Rx5day / CDD-CWD3.1 最小骨架研究区、时间范围与日降水集合以下代码可以直接粘贴到GEE编辑器里跑前提是你有一个自己的研究区FeatureCollection。如果没有可以先画一个矩形Geometry替换。注意基准期和分析期一定要分开定义极端降水指数里的百分位阈值要求固定在一个气候基准期上不能把分析期内的每一天都拿去算百分位否则等于用测试数据定标准。// 研究区换成你自己的asset或画一个矩形 var roi ee.FeatureCollection(projects/your-project/assets/roi); // 分析期与基准期分开定义 var studyStart 2001-01-01; var studyEnd 2020-12-31; var baseStart 2001-01-01; var baseEnd 2010-12-31; // CHIRPS 日降水单位 mm/day直接可用 var chirps ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterBounds(roi) .filterDate(studyStart, studyEnd) .map(function(img) { return img.select(precipitation) .clip(roi) .copyProperties(img, [system:time_start]); }); print(日降水影像总数, chirps.size());filterBounds加filterDate是GEE所有气候分析的骨架缺一不可。clip到roi的目的是提前裁掉研究区以外的计算量尤其是后面做滑动窗口和iterate时每多一个无效像素都会放大运行时间。copyProperties保留了影像的时间戳后续按年份切片和排序都依赖它。3.2 百分位阈值与R95p / R99p基准期与湿日过滤百分位阈值是极端降水指数的分水岭。R95p的本意是“湿日里超过第95百分位阈值的那些日子的年累计降水量”所以第一步是在基准期内筛出湿日第二步用reduce沿时间维求逐像素的P95。注意这里reduce的对象是ImageCollection不是单张影像ee.Reducer.percentile([95])会按每个像素把所有湿日影像的时间序列做百分位统计。// 基准期湿日集合只保留 1mm 的日子 var baseWet ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterBounds(roi) .filterDate(baseStart, baseEnd) .map(function(img) { var pr img.select(precipitation); return pr.updateMask(pr.gte(1)); }); // 逐像元求第95 / 第99百分位阈值 var p95 baseWet.reduce(ee.Reducer.percentile([95])).rename(p95); var p99 baseWet.reduce(ee.Reducer.percentile([99])).rename(p99); Map.addLayer(p95.clip(roi), {palette: [blue, yellow, red]}, P95 threshold);阈值算完之后把它应用到逐年集合上。常见误区是直接在年度集合里逐日判断img.gt(p95)但GEE的波段对比要求两边波段名和空间尺寸一致p95是单波段影像日降水影像也是单波段对比没问题。真正的坑在于有些日影像在裁切后某些像素是NoDataNoData像素参与gt比较会返回NoData最后sum时把这些像素按0处理导致个别栅格年累计值偏低。稳妥做法是在sum之前先focal或unmask(0)确认或者让p95影像带上与日影像一致的空间掩膜。// 逐年R95p超过P95阈值的那些天的日降水累计 var years ee.List.sequence(2001, 2020); var r95pByYear ee.ImageCollection(years.map(function(y) { var yearStart ee.Date.fromYMD(y, 1, 1); var yearEnd ee.Date.fromYMD(y, 12, 31); var yearColl chirps.filterDate(yearStart, yearEnd); var exceedDays yearColl.map(function(img) { var pr img.select(precipitation); return pr.updateMask(pr.gt(p95)); }); return exceedDays.sum() .rename(r95p) .set(year, y) .clip(roi); })); // 同理可得 R99p把阈值换成 p99 即可这里有一个容易被忽略的细节updateMask(pr.gt(p95))是“把不超过阈值的日子变成透明”再利用sum对透明像素按0处理。如果某一天降水本身就是NoData那它在gt判断里仍然是NoDatasum时可能整列像素变成0。所以建议在数据预处理阶段先用unmask(0)把缺测日固定为0宁可把缺测当天当作干日也不要让NoData污染整年的累计。是否接受这个近似取决于你的研究区降水观测数据的完整性CHIRPS这类产品缺测很少基本可接受。3.3 Rx1day与Rx5daymax聚合与滑动窗口的GEE写法Rx1day是一年内最大单日降水量直接对年度集合做max即可没什么悬念。Rx5day要的是连续5日累计降水量的最大值这是GEE里最容易被写错的地方。很多人直接用vector的滑动窗口思路去循环每一天在GEE里逐日filterDate做窗口累计代码能跑但慢到怀疑人生。正确做法是先把一年内的日影像按时间排序再用slice从集合里不断切出5张影像求和最后对整个窗口集合做max。这里的slice是ImageCollection的切片不是波段切片它要求集合严格有序且无缺测这也是后面避坑章节要重点讲的地方。// Rx1day年内日降水最大值 var rx1dayByYear ee.ImageCollection(years.map(function(y) { var yearStart ee.Date.fromYMD(y, 1, 1); var yearEnd ee.Date.fromYMD(y, 12, 31); return chirps.filterDate(yearStart, yearEnd) .max() .rename(rx1day) .set(year, y) .clip(roi); })); // Rx5day连续5日累计的最大值用slice做滑动窗口 var rx5dayByYear ee.ImageCollection(years.map(function(y) { var yearStart ee.Date.fromYMD(y, 1, 1); var yearEnd ee.Date.fromYMD(y, 12, 31); var yearColl chirps.filterDate(yearStart, yearEnd) .sort(system:time_start); var windowCount yearColl.size().subtract(5); var windows ee.ImageCollection( ee.List.sequence(0, windowCount).map(function(offset) { return yearColl.slice(offset, offset.add(5)).sum(); }) ); return windows.max() .rename(rx5day) .set(year, y) .clip(roi); }));slice(offset, offset.add(5))会在服务端计算偏移量对每个offset生成一个含5张影像的临时集合sum出一张5日累计图再在363张累计图里取max。这里有一个必须说清楚的前提CHIRPS的逐日产品几乎每天一张按时间排序后365张影像对应365天slice的索引等价于“第几天到第几天”。一旦数据里有缺失日slice索引就错位了所谓5日窗口实际跨了6天甚至7天。另一个性能细节是这对大区域比较吃内存建议rayCast前先clip到roi并且scale不要低于数据源原生分辨率。3.4 CDD / CWD 游程统计用iterate在栅格上做连续计数连续干日数CDD和连续湿日数CWD的难点在于它需要沿时间轴做游程计数而GEE没有内置的run-length reducer。常见做法是把日序列二值化成0/1再用iterate逐日更新两个状态量当前连续计数和历史上最大连续计数。iterate在GEE里是服务端循环每次回调接收前一天的字典状态返回当天的新状态最后从字典里取出最大连续天数。下面这段代码把CDD和CWD合并到一次iterate里比分开跑两遍快一倍。// 把日序列变成二值1为湿日0为干日 var binary chirps.map(function(img) { return img.select(precipitation).gte(1).toInt() .set(system:time_start, img.get(system:time_start)); }); // 初始状态四个单波段图像 var init ee.Dictionary({ curWet: ee.Image.constant(0).toInt(), maxWet: ee.Image.constant(0).toInt(), curDry: ee.Image.constant(0).toInt(), maxDry: ee.Image.constant(0).toInt() }); var runs binary.iterate(function(img, prev) { prev ee.Dictionary(prev); var cw ee.Image(prev.get(curWet)); var mw ee.Image(prev.get(maxWet)); var cd ee.Image(prev.get(curDry)); var md ee.Image(prev.get(maxDry)); var wet img; var dry img.Not().toInt(); var nw wet.multiply(cw.add(1)); // 湿日则累加干日清零 var nd dry.multiply(cd.add(1)); // 干日则累加湿日清零 return ee.Dictionary({ curWet: nw, maxWet: mw.max(nw), curDry: nd, maxDry: md.max(nd) }); }, init); var cwd ee.Image(ee.Dictionary(runs).get(maxWet)).rename(CWD).clip(roi); var cdd ee.Image(ee.Dictionary(runs).get(maxDry)).rename(CDD).clip(roi);这段iterate返回的是整个分析期内最大的连续湿/干日数而不是逐年值。如果想得到逐年CDD需要把binary按年份切片在外层再套一个years.map循环。iterate是全图逐像素遍历影像越多、范围越大耗时越长我一般先把binary裁剪到核心研究区再跑。另外要注意CDD/CWD的定义在不同文献里并不完全一致有的用小于1mm有的用小于0.1mm你一旦选定了某个阈值全文所有年份都得用同一个别今天小于1、明天小于0.1。4. 趋势与导出在GEE里出斜率图把时间序列交给本地做Mann-Kendall4.1 用sensSlope在GEE里直接出趋势图极端降水指数的空间趋势图是很多人最想从GEE里直接拿走的东西。GEE内置的ee.Reducer.sensSlope()是基于Theil-Sen估计的稳健回归斜率比普通最小二乘对离群值鲁棒得多很契合极端降水这种方差大的数据。直接把逐年指标集合reduce一下就得到每个像素的斜率影像。这里有个版本相关的坑不同GEE版本返回的波段名可能从slope/offset变成slope/intercept/pValue所以不要硬编码波段名先打印确认。// 以逐年R95p为例输出斜率空间分布 var r95pTrend r95pByYear.reduce(ee.Reducer.sensSlope()); print(sensSlope波段名, r95pTrend.bandNames()); // 可视化斜率单位是 mm/year var slopeVis r95pTrend.select(slope).clip(roi); Map.addLayer(slopeVis, {min: -2, max: 2, palette: [red, white, blue]}, R95p slope);sensSlope输出的斜率不像Mann-Kendall那样附带显著性p值它只是把多年序列的稳健趋势估计出来了。很多人直接把斜率图当成“显著趋势”放进论文这是不严谨的。我一般把GEE出的斜率图当作快速扫描工具看空间格局、找热点区域正式的显著性检验放到本地对站点序列做MK检验。你还要注意样本量问题如果只分析5年sensSlope的斜率毫无统计意义极端指数趋势分析通常至少需要15到20年。4.2 批量导出GeoTIFF与站点CSV三个必设参数导出是另一个翻车高发区。直接Export.image.toDrive不设参数经常在任务列表里挂半小时然后报错。我的固定习惯是三个参数必设scale设为数据源原生分辨率CHIRPS约5566米IMERG约11132米region显式指定roimaxPixels设到1e13。不要用默认的scale去导出GEE会自动重采样但重采样后的极端降水栅格和原始计算结果会不一致。// 导出斜率GeoTIFF Export.image.toDrive({ image: r95pTrend.select(slope).clip(roi), description: r95p_slope_2001_2020, folder: gee_extreme_precip, region: roi, scale: 5566, crs: EPSG:4326, maxPixels: 1e13, fileFormat: GeoTIFF, formatOptions: {cloudOptimized: true} }); // 按站点提取逐年R95p导出CSV var stations ee.FeatureCollection(projects/your-project/assets/stations); var annualAtPoints r95pByYear.map(function(img) { var y img.get(year); var pts img.reduceRegions({ collection: stations, reducer: ee.Reducer.mean(), scale: 5566 }); return pts.map(function(f) { return f.set(year, y); }); }).flatten(); Export.table.toDrive({ collection: annualAtPoints, description: r95p_by_year_stations, folder: gee_extreme_precip, fileFormat: CSV });reduceRegions返回的每个Feature默认带一个mean属性也就是该站点所在像素的R95p值。注意scale设成5566时reduceRegion取的是站点所在栅格像元的均值如果站点落在像素边缘这个值会有一定代表性偏差。站点比较密集时我建议先把站点转成小缓冲区圆形区域再用mean还原一个邻域均值而不是碰运气用单点。用flatten展平年度结果时如果站点数量太大GEE会报集合大小超限常见处理是按年份分批导出或先在本地遍历task。4.3 本地MK检验从CSV到pymannkendall的最小脚本导出的CSV拿回本地后趋势检验就交给Python生态了。pymannkendall是GEE之外最顺手的补位工具一条命令就能算Mann-Kendall的tau、p值和趋势方向。我习惯在GEE里按站点导出“宽表”每一行是一个站点每一列是年份这样读进Pandas后直接做逐站点循环。import pandas as pd from pymannkendall import original_test df pd.read_csv(r95p_by_year_stations.csv) # 先看一眼GEE导出的列名 print(df.columns) # 按站点分组逐年做MK检验 station_col [c for c in df.columns if c.startswith(station)][0] for st_id, sub in df.groupby(station_col): sub sub.sort_values(year) ser sub[mean].dropna().values if len(ser) 10: # 样本太少时MK检验的p值参考意义有限 print(st_id, 样本不足跳过) continue result original_test(ser) print(st_id, trend:, result.trend, p:, round(result.p, 4), tau:, round(result.Tau, 4))original_test返回的对象里trend是趋势方向字符串increasing/decreasing/no trendp是双尾显著性Tau是Kendall秩相关系数。少于10个年序列不建议下结论这是MK检验对样本量的基本要求。还有一点常被忽略MK检验假设序列独立但极端降水指数往往存在年际自相关尤其ENSO影响明显的区域序列里混入显著自相关会让p值偏乐观。严谨做法是先用滞后1阶自相关检查有自相关时改用修正方差版本的MK检验或者对序列做pre-whitening这些在pymannkendall的文档里有对应实现。5. GEE极端降水分析避坑六个翻车现场与排查顺序5.1 数据单位与聚合顺序三个最常见的“算错”现场现象一ERA5-Land跑出来所有极端指数比站点观测大三个数量级。原因几乎可以锁定在单位上。ERA5-Land Daily Aggregated的total_precipitation单位是米而CHIRPS是毫米没做multiply(1000)就混进同一套代码结果当然全线崩溃。解决方法是统一入口所有数据进分析流程前都rename成precipitation并换算成mmCHIRPS原样ERA5-Land乘1000IMERG先累加再乘0.5。我甚至建议把换算逻辑写成一个公共函数三种数据源各自map一遍后面代码只认precipitation这一个波段名。现象二IMERG直接对半小时集合做max得出“日最大降水”数值异常偏小或偏大。原因是半小时降水率不能等同于日尺度极端值。IMERG每个时间步的precipitationCal是mm/h表示该半小时内的降水强度想得到日总量必须累加48个时次。如果你只需要“日最大小时雨强”那也要先按小时聚合再取小时尺度上的最大值两种口径差出一个量级。解决方法是把日聚合放在最前聚合完成后所有极端指数只面向日累计序列计算。现象三P95阈值算出来只有0.2mmR95p却大得离谱。原因是没有过滤干日。直接用全部日序列算第95百分位干旱区大量0值会把阈值压到接近干日的水平R95p统计的就不再是“极端湿日”而是“所有稍微下点雨的日子”。解决方法是严格按ETCCDI的湿日定义先updateMask(pr.gte(1))再在湿日集合上做percentile。5.2 导出任务与集合顺序三个“跑不动 / 结果对不上”的现场现象四导出任务一直显示Running两小时后失败报Requested array size exceeds maximum。原因是没设maxPixels或者没把影像clip到roiGEE试图导出全球范围的重采样结果。解决方法是三连clip(roi)、scale设为数据源原生分辨率、maxPixels设1e13。跨大区域时还要考虑拆块导出我通常按行政区或经纬度网格把研究区切成几块分别输出后本地再镶嵌。现象五Rx5day的结果在不同年份之间明显跳变甚至出现超过物理上限的值。原因多半是集合里有缺失日期导致slice索引错位。CHIRPS虽然整体完整但有些区域在特定日期可能没值如果用了IMERG自己聚合的日序列缺失更常见。slice是按集合顺序切片的一旦中间缺了一天所谓5日窗口实际上跨了6天累计量自然偏大。解决方法是进入滑动窗口之前先print(yearColl.size())和首尾日期跟日历天数核对有缺测就先补齐或者干脆改用本地滑动窗口计算。现象六Export.table.toDrive在站点很多时报错CSV文件一直生成不了。原因是flatten后的FeatureCollection规模太大超过了GEE客户端集合上限。解决方法是按年份循环导出多个CSV或者把reduceRegions的collection按区块拆分再在本地合并。我自己的惯例是每个task控制在一千个站点以内宁可多开几个task也不要挑战单次导出的上限。6. 进阶从网格点到流域尺度以及用合成数据验证算法没算错6.1 用reduceRegions把指数聚合到流域注意面积权重网格尺度跑通后大多数实际项目最终要落到流域或行政区均值。直接把流域内所有像素做简单平均在高海拔山区会出问题因为小面积高值栅格会被大面积低值栅格稀释。常见做法是用像元面积作为权重做加权平均。GEE里的reduceRegions支持把pixelArea作为一个权重波段传进去用weightedMean reducer实现。// 对逐年R95p做流域面积加权平均 var basins ee.FeatureCollection(projects/your-project/assets/basins); var basinR95p r95pByYear.map(function(img) { var weighted img.select(r95p) .addBands(ee.Image.pixelArea().rename(weights)) .reduceRegions({ collection: basins, reducer: ee.Reducer.weightedMean({ weightField: weights }), scale: 5566, tileScale: 4 }); return weighted.map(function(f) { return f.set(year, img.get(year)); }); }).flatten();tileScale设成4可以缓解reduceRegions在大范围计算时的内存压力。加权平均结果和简单平均之间差多少取决于流域内部的降水空间异质性湿润山区两个结果可能差10%以上这也是为什么我建议每次都把两种结果都算出来对比一下而不是默认选一种。6.2 合成数据自测10行本地Python反算验证GEE结果最后一条经验也是我个人每次换研究区都会做的动作先用一组合成数据验证整个计算链路。GEE是个黑匣子等跑完20年数据再发现阈值算错返工成本极高。我会在本地生成一个模拟站点的逐日降水序列用同样的阈值和指数定义算出P95、R95p、Rx1day再把这组序列扔进GEE里跑一遍同名输出两边数字对上了才放心铺开全区域。import numpy as np # 生成3650天模拟降水Gamma分布主体 干日清零 rng np.random.default_rng(42) pr rng.gamma(2, 5, size3650) pr[pr 1] 0 # 与湿日定义对齐 # 与GEE一致只用湿日1mm算P95 wet pr[pr 1] p95 np.percentile(wet, 95) r95p pr[(pr 1) (pr p95)].sum() rx1day pr.max() print(P95:, round(p95, 2), R95p:, round(r95p, 2), Rx1day:, round(rx1day, 2))这组数字打印出来后我用同样的100天合成序列在GEE里构造一个临时ImageCollection跑同一套指数函数然后对比。这个习惯帮我抓出过不止一次波段名写错、单位漏乘、日期边界差半天的问题。黑匣子不可怕可怕的是黑匣子第一次输出就让你觉得“好像也合理”然后带着错数据跑完整个项目。每接一个新区域我都先找一个有实测极端降水的站点对一下量级再放全图批量计算。希望这个流程对你也有用祝一次跑通。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑