1. 项目背景与海洋热浪核心概念
1.1 为什么盯上南海北部这片海
南海北部是我这几年做海洋环境数据分析时绕不过去的一块区域。它横跨热带和亚热带,北接华南大陆,西靠北部湾,东连吕宋海峡,四季都有鲜明的海洋动力过程。平时做海表温度(SST)研究,大家关注的往往是台风路径、暖池变化、沿岸上升流,但海洋热浪这件事,这几年越来越值得单独拿出来看。
海洋热浪说白了就是海水温度异常偏高的持续性事件。过去大家觉得海洋变暖是缓慢的、均匀的,但实测数据和再分析资料都显示,水温会突然在某些海域“飙”上去,持续几天甚至几周,这种短时间尺度上的极端高温,对珊瑚礁、渔业和养殖业的影响往往比平均升温更直接。南海北部的珊瑚礁生态系统、近海养殖区分布密集,一旦发生热浪,经济损失和生态破坏都是实打实的。
做这个项目的原因也很简单:南海北部的热浪研究,大多还停留在区域平均、单点时间序列这种层面,缺乏系统性的“强度可视化”。而可视化恰恰是分析这类时空数据最直观的手段——一张图能看清热浪在哪一年爆发、覆盖多大范围、持续时间多长、海洋表面温度偏高到什么程度,这些信息用表格和数字很难讲清楚。
这个项目的目标,就是围绕南海北部(大约 15°N—23°N,110°E—120°E 区域)的海洋热浪强度,构建一套从数据下载、热浪识别、强度计算到可视化出图的完整分析流程,适合从事海洋环境数据分析、气候变化研究,或者对 Python 时空数据可视化感兴趣的从业者和学生参考。
1.2 海洋热浪的科学定义与强度指标
做热浪分析,第一步是统一口径。海洋科学界现在普遍接受的海洋热浪定义,沿用的是 Hobday 等人提出的框架:当某海域的海表温度连续超过该位置气候态(通常取 30 年基准)的第 90 百分位阈值,且持续时间至少 5 天,就定义为一次海洋热浪事件。
这个定义里有两个关键参数:阈值和持续时间。阈值不直接用绝对温度,而是用百分位数,是因为不同海域的“正常温度”差异很大,南海北部夏季表层能到 29°C,冬季可能只有 22°C,如果笼统地订一个绝对阈值,冬季永远触发不了。用逐日气候态的 90 百分位作为阈值,相当于给每个格点、每一天都设了一道动态门槛。
强度指标方面,项目中我重点计算了四类:
- 最大强度(Max Intensity):事件期间超过阈值的最大 SST 异常值,单位 °C。
- 平均强度(Mean Intensity):事件期间每日异常值的平均。
- 累计强度(Cumulative Intensity):事件期间每日异常值之和,单位 °C·天,这是最能反映事件总热量负担的指标。
- 持续时间(Duration):从开始到结束的天数。
这四个指标中,累计强度对生态响应的指示意义最大。举个生活化的例子:一天高烧 5°C 和一星期持续高烧 2°C,对身体的伤害完全不同,海洋生态也一样。珊瑚白化通常不是某一瞬间温度峰值造成的,而是累积热量负荷达到一定程度后的结果。所以可视化时,我特意把累计强度的空间分布图作为核心输出。
1.3 可视化在项目里解决什么问题
这个项目骨子里是一个“数据分析 + 可视化”的典型场景。南海北部的 SST 数据是三维的:经度、纬度、时间。三维以上的数据,光靠统计报表是看不出来的,必须靠可视化把时间维压成一条线、把空间维铺成一幅图。
那段时间我试过很多可视化工具组合,最后确定的主链路是 Python 的 xarray + Matplotlib + Cartopy,辅助用 Plotly 做交互式探索。为什么选这套组合?因为这套组合在海洋大气领域几乎是事实标准,数据读取、裁剪、计算、绘图全在同一个生态里搞定,不需要来回倒腾格式。
另外,可视化的另一个价值是给“异常”定性。热浪事件不是每年都有,哪一年是异常年,光靠看年均值曲线也能看出来,但没法回答“异常到底发生在哪片海域、强度分布如何”。画成空间分布图和热浪日历图之后,答案几乎是一目了然的——而且这种直观输出,对向非专业背景的决策者和公众传达结论,效果远好于任何统计表。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据准备与预处理
2.1 数据源选型与对比
做南海北部热浪分析,数据源选择直接影响结论可靠性。我对比过三个常用来源:NOAA 的 OISST(日尺度海表温度)、欧洲中期天气预报中心的 ERA5 海表温度,以及 GHRSST 融合产品。
| 数据源 | 空间分辨率 | 时间分辨率 | 数据覆盖 | 适用特点 |
|---|---|---|---|---|
| NOAA OISST | 0.25°×0.25° | 日 | 1981 年至今 | 时间序列长,适合气候态统计 |
| ERA5 | 0.25°×0.25° | 逐小时(可聚合为日) | 1940 年至今 | 有小时数据,适合极端事件过程分析 |
| GHRSST 融合产品 | 0.01°—0.05° | 日/逐小时 | 2002 年至今 | 分辨率高,但时间短,不适合算气候态 |
最终我选了 NOAA OISST 作为主数据源。理由很简单:热浪检测需要足够长的气候基准期,OISST 从 1981 年就开始逐日更新,到现在超过 40 年,可以稳定地计算 30 年气候态和百分位阈值。ERA5 虽然起点更早,但它的海表温度在早期较依赖于同化系统,某些年份的不确定性略高;而 GHRSST 虽然空间分辨率高,但时间覆盖不足,算不出可靠的气候态。
提示:做长时序极端事件分析,千万不要只看分辨率。时间长度和一致性才是第一位的。分辨率问题后期可以通过插值或局部放大来缓解,但数据源不连续带来的气候态偏差,是没法弥补的。
2.2 研究区域裁剪与网格处理
拿到 OISST 数据后,第一步是把全球场裁剪到南海北部子区域。OISST 原始数据是 NetCDF 格式,用 xarray 打开后直接按经纬度切片就行,非常简单:
python复制import xarray as xr
# 打开 OISST 日平均海表温度数据
ds = xr.open_dataset("oisst_daily_sst_1981_2023.nc")
# 裁剪南海北部区域(经度 110°E—122°E,纬度 15°N—23°N)
region = ds.sel(lon=slice(110, 122), lat=slice(15, 23))
# 时间维也裁剪一下,剔除可能存在的缺失日期
region = region.sel(time=slice("1982-01-01", "2022-12-31"))
裁剪区域范围的选择是有一点讲究的。南海北部没有绝对统一的标准边界,我经过几轮试验,最后用 110°E—122°E、15°N—23°N 这个范围。北边界 23°N 几乎贴近华南海岸线,能包含珠江口外的近岸海域;南边界 15°N 可以覆盖到西沙群岛附近的重要珊瑚礁区;东边界到 122°E 已经把吕宋海峡西侧包括进来了。
裁剪后有个细节要注意:近岸海域的 SST 数据受陆地掩膜和陆地径流影响,容易出现异常值和缺测。我在处理时用 region.sst 的 mask 属性检查了陆地点,并做了简单的质量控制,把明显超出物理范围的值(比如低于 0°C 或高于 40°C)标记为 NaN。
2.3 气候态基准与异常场计算
热浪检测的基础是逐日气候态。这里有个重要选择:气候态用 1982—2011 这 30 年还是全时段?实践中,官方 OISST 数据推荐的气候基准期是 1982—2011 年,这也是很多极端事件研究的通用选择。用较早的固定基准期,可以避免气候变化趋势本身被“平均”掉,从而让近年热浪事件更容易被识别出来。
计算气候态时,要先把日期归一化到“一年中的第几天”,然后求多年平均:
python复制# 剔除闰年影响,把时间统一到月-日
dayofyear = region.time.dt.dayofyear
# 按第几天分组求多年平均,得到逐日气候态(共366天,但可以去掉2月29日)
clim = region.sst.groupby(region.time.dt.dayofyear).mean(dim="time")
# 计算SST异常
anom = region.sst.groupby(region.time.dt.dayofyear) - clim
这里容易踩的坑是闰年。如果不处理 2 月 29 日,groupby 之后 366 天和 365 天的数组对不齐,会导致异常场出现一整天的偏差。我在代码里把 2 月 29 日的数据直接删除,再参与后续计算。
计算完逐日异常之后,还需要逐格点、逐日计算 90 百分位阈值。这里不能直接对整个时间维求百分位数,因为季节差异会被抹掉。正确做法是:对每个“日序”及其前后各 7 天(共 15 天窗口)的时间样本求 90 百分位,这样既保证样本量,又考虑了季节平滑性。
python复制# 计算逐日滑动窗口的90百分位阈值
# 窗口设为 15 天(前后各7天)
window = 7
threshold = xr.full_like(clim, np.nan)
for doy in range(8, 358):
doy_window_data = region.sst.sel(time=region.time.dt.dayofyear.isin(
np.arange(doy - window, doy + window + 1)
))
threshold[doy] = doy_window_data.reduce(np.nanpercentile, q=90, dim="time")
初版代码用双循环遍历格点,跑一次要一个多小时;后来改成纯 xarray 的分组运算,配合 dask 并行,总算压到了十几分钟。做这类时空分析,性能优化是绕不开的功课。
3. 海洋热浪强度检测与计算
3.1 阈值检测算法的核心思路
有了逐日阈值场,剩下的就是判断每个格点每天是否超过阈值,然后按“连续超过 5 天以上”合并成一个事件。
这里有个容易误解的地方:不是说有一天超过阈值就算热浪,必须连续 5 天以上。这个 5 天的设定在文献里有依据,主要为了剔除天气尺度(例如短暂的高压系统造成的升温)的临时波动,保证识别出的事件在统计和生态上都是有意义的事件。
另外,严格按定义,在两个独立事件之间如果只间隔 2 天或更短,应该合并成一个事件。因为对于生态影响来说,中间短暂降温不足以上系统恢复,仍然属于同一段持续压力。这个“缺口合并”规则在实际分析中尤其重要,能让事件数量和持续时间更贴近真实生态响应。
3.2 Python 实现要点
我选择用 xarray + pandas 逐格点检测。核心思路是:把每个格点的时间序列读成 pandas Series,然后逐日判断是否超过阈值,用连续段切割提取事件。
python复制import pandas as pd
import numpy as np
def detect_mhw(sst_daily, threshold_daily):
# 超过阈值标记为1,否则为0
exceed = (sst_daily > threshold_daily).astype(int)
# 找出连续超过阈值的时间段
# diff 可以识别状态切换点
change_points = exceed.diff().fillna(exceed)
# 事件开始位置:exceed从0变1;结束位置:从1变0
starts = np.where((change_points == 1) & (exceed == 1))[0]
ends = np.where((change_points == -1) & (exceed.shift(1) == 1))[0]
events = []
for s, e in zip(starts, ends):
duration = e - s + 1
# 剔除小于5天的事件
if duration >= 5:
events.append({
"start_idx": s,
"end_idx": e,
"duration": duration
})
return events
单格点跑起来很快,但南海北部个格点规模大概 48×32 = 1536 个,每个格点 40 年逐日数据,总共差不多 2200 万条记录。逐格点循环在纯 Python 里跑会非常非常慢。我实际用的方式是:把数据 reshape 为二维数组(时间 × 格点),用 numpy 的向量化操作直接对整个数组做状态检测,速度提升了两个数量级。对于 40 年日数据,单核跑也就一两分钟。
这里可以分享一个实战经验:不要迷信“纯向量化”。我当时在 numpy 向量化基础上用 numba 加速循环版本,效果也很不错,而且代码可读性反而更好。最终我保留了 numba 版本,因为后续要添加各种自定义指标,函数式编程更灵活。
3.3 强度指标的计算与语义
事件列表提取出来后,接下来计算每个事件的最大强度、平均强度、累计强度和峰值日期。
python复制def compute_intensities(sst_daily, threshold_daily, event):
s, e = event["start_idx"], event["end_idx"]
anomaly = sst_daily.iloc[s:e+1] - threshold_daily.iloc[s:e+1]
max_intensity = anomaly.max()
mean_intensity = anomaly.mean()
cum_intensity = anomaly.sum()
peak_date = sst_daily.index[s + anomaly.argmax()]
return {
"max_intensity": max_intensity,
"mean_intensity": mean_intensity,
"cum_intensity": cum_intensity,
"peak_date": peak_date
}
这里的“强度”都定义为超过阈值的异常量——异常量指 SST 减去气候态的差值,再减去阈值对应的基础偏高量。本质上它衡量的是“比正常热时还要热多少”,这个语义对生态响应解释很关键。
我在项目中把逐事件的强度结果重新组织成了不同层次的数据结构:
- 事件级:记录每一个热浪事件的起止日期、持续天数、各类强度。
- 年际网格:统计每年每个格点发生热浪的次数、最大累计强度、最长持续时间。
- 区域平均序列:整个研究区域每天的 SST 异常以及热浪天数的逐年变化。
这三层结构分别支撑不同类型的可视化:事件级适合做日历图和热度图;年际网格适合做空间分布;区域平均序列适合做趋势分析。
4. 可视化体系搭建与实现
4.1 静态地图可视化:热浪强度空间分布
可视化是这个项目的重头戏,我按“从区域到网格、从网格到事件”的逻辑设计了三类核心图件。
第一类是热浪累计强度空间分布图。它回答的问题最简单也最直接:南海北部哪个地方热浪期间累积热量最多?我用 Cartopy 绘制底图,叠加累计强度场和海岸线,色标选用从浅黄到深红连续过渡的方案——这类暖色系色标在温度场中最符合直觉,也最容易让读者看出强度梯度。
python复制import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
fig, ax = plt.subplots(
figsize=(10, 8),
subplot_kw={"projection": ccrs.PlateCarree()}
)
ax.set_extent([108, 124, 13, 25], crs=ccrs.PlateCarree())
ax.add_feature(cfeature.LAND, facecolor="lightgray")
ax.add_feature(cfeature.COASTLINE, linewidth=0.8)
ax.coastlines(resolution="50m")
im = ax.pcolormesh(
lon, lat, cum_intensity_grid,
cmap="YlOrRd", shading="auto",
vmin=0, vmax=150
)
cbar = fig.colorbar(im, ax=ax, shrink=0.8, label="Cumulative Intensity (°C·day)")
ax.set_title("Marine Heatwave Cumulative Intensity in Northern South China Sea", pad=12)
plt.savefig("mhw_cum_intensity_map.png", dpi=300, bbox_inches="tight")
做空间分布图有几个细节值得注意。第一,pcolormesh 和 imshow 的经纬度坐标要对齐,否则图形会整体偏移半个网格;第二,南海北部近岸区域有大量岛屿和小海湾,Cartopy 的海岸线分辨率最好用 50m,太低会掩盖沿岸细节;第三,色标的下限不一定从 0 开始,要看具体数据范围,但为了让不同年份对比起来一致,我做多年度对比图时统一了色标范围。
在实际出图时,我还叠加了重要生态区位点(如珊瑚礁保护区、主要渔场)的标记,这样可以让纯温度场和实际业务场景产生直接关联。
4.2 时间维度可视化:热浪日历图与事件时间线
空间分布图适合看“哪里”,但热浪在“何时”发生也同样重要。我做了两类时间图。
第一类是区域平均的 SST 异常时间序列图,以年为横轴,异常温度值为纵轴,把超过阈值的时段用红色高亮。这种图可以直观地看到热浪事件的频次和强度随年份的变化。
第二类是热浪事件日历图(Heatmap Calendar),用热力矩阵展示一年中每天是否发生热浪。行可以是一年的各个月,列是每个月内的日期,或者反过来。颜色深浅可以编码当天异常强度。为了不显得太散乱,我按事件峰值日期落入的月份聚合统计,做成月度热浪天数柱状图。
这类日历图的实现并不复杂,关键在于数据整理:把检测到的事件列表转换成“每日 0/1 标记”,再 reshape 成日历矩阵。
python复制# 将事件列表转为每日标记
daily_flags = pd.Series(0, index=time_index)
for evt in events:
daily_flags.iloc[evt["start_idx"]:evt["end_idx"]+1] = 1
# 按年和月聚合
monthly_counts = daily_flags.groupby([daily_flags.index.year, daily_flags.index.month]).sum()
做日历图时我踩过一个坑:年份边界上的事件跨越两年,如果简单按年份切割就会出现事件被截断的问题。我最后采用的方案是:事件识别在以完整时间序列上先跑完,再按事件起始日期的年份归属统计,这样一次跨年事件只会被计入起始年份。
4.3 交互式可视化:从静态图到可探查大屏
静态图能解决 80% 的分析展示需求,但探索性分析阶段,我还是需要能缩放、能查看具体数值的交互式可视化。我用了 Plotly 的 scatter_mapbox 和 heatmap 做了两个快速原型:
- 交互式区域平均异常曲线:鼠标悬停即可看某一年的具体异常值和事件起止日期。
- 多层级大屏布局:左边是南海北部区位图,右边是区域平均时间序列,中间是事件列表,联动筛选年份。
说实话,交互式大屏在这个项目里不是刚需,但它的价值在于汇报和科普:当面向非专业背景的观众时,交互式图表比静态图更能抓住注意力。对于纯科研汇报,我仍然推荐静态高分辨率图件,因为信息的准确性和可复现性比炫酷重要。
如果是做终端展示,我建议用 Flask + ECharts 搭一个轻量大屏,后端提供 JSON API 返回多年统计结果,前端用 ECharts 的 map、bar、line 组合展示。ECharts 对地图的支持很成熟,南海北部地图底图可以直接用中国地图裁剪或者 GeoJSON 区域,展示效果远超 Matplotlib。
提示:交互式可视化适合探索和汇报,但论文、报告或正式发布场景,务必使用静态图,并保证色标、坐标、图例完整可自明。
4.4 配色与图例设计经验
可视化图形最后效果好不好,往往不在技术服务,而在配色和排版的细节。我总结几条在海洋温度可视化里被反复验证有效的经验:
- 温度异常推荐使用红蓝双色发散色标,0 值用白色或浅灰色,正异常用红色系,负异常用蓝色系。这种配色符合人的直觉,也符合科学出版物惯用风格。
- 热浪累计强度这类“只有正值”的量,使用单色渐变(如 YlOrRd)即可,不要强行用发散色标。
- 色标最大值不要机械地设为数据的最大值,而要留出 5%—10% 的余量,防止空间分布图中少数极端格点把整体色带拉平,导致大多数区域颜色都偏浅,失去区分度。
- 所有颜色都需要考虑色盲友好性。红绿组合尽量避免,用橙蓝组合更保险。
我还养成了一个习惯:所有出图统一用 300 dpi 导出,字体大小保持在 10—12 pt 之间,图形尺寸至少 10 英寸宽。这样无论放大还是缩小打印,都不会出现锯齿或看不清的问题。
5. 结果解读与业务落点
5.1 可视化揭示的南海北部热浪变化特征
通过这套可视化流程,项目揭示了一些在表格中不容易看到的特征。
首先,从空间分布图上可以清晰地看到,南海北部的热浪累计强度高值区主要集中在广东近岸外海和海南岛东北部海域,这与夏季沿岸上升流强度的区域差异有关。近岸区域水深浅,热量交换快,在持续高温背景下更容易积累极端热量。
其次,从时间序列图上可以看出,2016 年、2020 年和 2021 年是三个显著的极端热浪年份。2016 年主要受强厄尔尼诺事件的残余影响,南海北部水温异常偏高;2020 年则更多是局地海洋动力过程的作用。这个差异在空间分布图中体现得很明显——2016 年的高值区范围更广,而 2020 年的高值区更集中在局部海域。
这种“通过可视化发现空间格局差异”的过程,是传统数值统计很难替代的。我曾经试过用区域平均温度做逐年趋势分析,只能得出整体升温的单调结论,完全看不出事件空间格局的演变。
5.2 从图像到决策:热浪对生态与渔业的影响
可视化本身不是终点,关键是从图像中读出可用的信息。
南海北部是珊瑚礁分布的重要区域,特别是海南岛沿岸和西沙群岛一带。珊瑚白化的触发阈值通常与累计热量有关。如果我们把热浪累计强度分布图和珊瑚礁分布叠加,就可以快速识别生态风险最高的区域,为海洋保护区管理提供科学依据。
渔业方面,南海北部的灯光围网渔业主要依赖近岸高生产力海域。热浪会造成鱼类分布变化,某些经济鱼种会在高温期间游向更深或更凉的水域,或者出现生长减缓。可视化分析可以帮助渔业管理部门预判热浪事件对渔获量的潜在冲击,进而调整捕捞配额或建议渔民转移到受影响较小的海域。
5.3 可视化成果的常态化输出
这个项目做到后期,已经不只是一次性的分析,而是一套可以按年滚动的流程。我把计算和绘图代码封装成独立的模块,每年更新数据后,运行一条命令就能输出当年的热浪监测图件。
这部分工作对实际业务非常有价值:与其每年从零开始分析,不如固化流程,把结果输出成统一格式的图表,形成年度热浪监测报告。这让我深刻体会到,可视化项目最有价值的交付物,不只是一张漂亮的图,而是一套可持续运行的分析流水线和能直接辅助决策的图形语言。
6. 常见问题与踩坑记录
6.1 数据版本与气候态基准的陷阱
做热浪分析的人,第一个容易踩坑的地方就是数据版本不统一。OISST 本身有 v2.0 和 v2.1 版本,两者之间存在细微的系统偏差,尤其是在 2016 年前后。如果你混合使用了不同版本的数据而不做偏差校正,检测出的热浪事件数量和强度会出现虚假的跳跃。
我建议在项目文档里写明数据版本号、下载日期、气候基准期。整个分析流程中,任何图件、表格只有在版本一致的前提下才有可比性。这个要求听起来很基础,但我见过不少团队因为忽略这个细节,导致不同年份的结果根本没法放在一起看。
6.2 阈值计算窗口选择的争议
90 百分位阈值计算的窗口大小,本身就是一个需要仔细斟酌的参数。窗口太窄(比如只用当天),样本量小,对于 40 年数据每天只有 40 个样本,95 分位数极不稳定;窗口太宽(比如 31 天),季节信号被过度平滑,会低估夏季的阈值、高估冬季的阈值。
我试过 11 天、15 天、31 天三种窗口,实验结果差距不小:窗口过宽会让热浪事件在季节转换期(比如 5 月和 11 月)更容易被误检。最终我选择了 15 天窗口,这是文献里比较常见的配置,不过如果你的数据时间跨度大或者季节变化剧烈,还是应该做敏感性试验,而不是直接抄参数。
6.3 可视化输出时的坐标与投影问题
用 Cartopy 画南海北部图件时,投影方式选择也会影响读图效果。我最初用了 Mercator 投影,靠近高纬度的形变虽然对南海影响不大,但当我把底图换成 Lambert Conformal 或 PlateCarree 之后,发现沿海边界线的形状展示效果差别明显。在这个纬度,用 PlateCarree 直线经纬网反而最直观。
另外,如果图中要标注岛屿或重要地点,一定要使用地图投影坐标而不是经纬度散点。我经常看到同事在图上标记位置时直接把经纬度当作坐标往图上点,结果偏离实际位置几百公里——这不是开玩笑,这种错误在可视化项目中非常常见。
6.4 性能优化与代码组织的建议
最后说一点工程化层面的心得。处理长时间序列数据时,数据处理代码和可视化代码一定要分离。我第一版所有代码混在同一个 Jupyter Notebook 里,改一个参数要全局运行一次,效率极低。后来拆成:
- 数据预处理模块:处理下载、裁剪、气候态计算,输出中间 NetCDF/CSV。
- 热浪检测模块:输入日 SST 和阈值场,输出事件级结果。
- 可视化模块:读取事件结果,输出各类图件。
每个模块独立运行、独立调试,修改可视化参数时再也不用重新跑数据,效率提升非常明显。
注意:做海洋数据分析,别把中间结果直接丢掉。推荐把裁剪后的 SST、气候态、阈值场、事件列表全部保存为 NetCDF 或 CSV 格式。一旦后续需要调整绘图参数,或者复查某个可疑的检测结果,直接读中间文件就能快速定位,而不是从原始数据重跑一遍。保存中间结果占不了多少空间,但能为你省下大量调试时间。
7. 实际项目中的补充经验
除了上面这些主线内容,我在这个项目里还有几点比较零碎但很实用的小经验,一并分享出来。
关于区域平均序列的可视化,我用的方法是绘制带置信区间的曲线。把区域内的格点按四分位数算出一个带状区间,而不是只画平均值。这样能看到区域内部的差异,很多单看均值时被掩盖的信息就这样显现了出来。
关于对比不同年份的热浪强度,我做了“年度热浪强度排名表”,用排序条形图展示最热的前十个年份。这种图给决策者看效果特别好,一目了然。
关于代码仓库的管理,建议从一开始就用 Git 维护。这个项目迭代过程中我频繁微调阈值参数、窗口大小和色标范围,没有版本控制的话,很容易陷入“改坏了却不知道哪一步改坏的”的困境。我用 Git 记录每一次 commit,并在 commit message 里写清楚修改了什么参数、为什么改,后期回溯问题非常方便。
另外,项目命名和目录结构也值得花一点时间设计。数据、代码、图件、文档分开存放,用日期命名字文件夹,能避免后期找文件找到崩溃。
在当前数据条件下,我做的所有可视化工作都还集中在海表温度。实际上,海洋热浪也可以发生在海面以下,次表层热浪对珊瑚礁生态系统的影响可能更直接。后续如果想扩展,可以考虑把 Argo 浮标数据的次表层温度剖面纳入分析,画出不同深度的热浪强度剖面图——这也是这个方向下一步值得深耕的内容。
