4. 海洋浮标多参数数据缺失模式分析
浮标多参数缺失模式:先分清“缺在哪”,再决定“怎么补”
海洋浮标通常以固定时间间隔(如10 min或1 h)上报温度、盐度、波浪有效波高、波周期等参数。实际数据中,缺失往往不是随机散点,而是呈现明显的时间聚集(连续多帧同时缺失)和参数共缺(盐度与温度同时丢失,波浪参数单独丢失)。判断缺失机制是选择补全策略的前提:完全随机缺失(MCAR)可用简单插值;随机缺失(MAR)需借助相关参数建模;非随机缺失(MNAR)则要警惕传感器故障或环境超限,盲目插值会制造虚假数据。
下面用一段可运行的 Python 代码完成三件事:构造一份带真实缺失模式的浮标示例数据、统计时空缺失分布、用“缺失指示变量之间的相关性 + 缺失与观测值的关联”给出机制判断,并输出补全策略建议。
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
# ---------- 1. 构造示例浮标数据(10 min 间隔,30 天) ----------
n = 30 * 24 * 6
t = pd.date_range("2024-06-01", periods=n, freq="10min")
df = pd.DataFrame({"time": t})
# 温度:日周期 + 缓慢漂移
df["temp"] = 18 + 3 * np.sin(2 * np.pi * np.arange(n) / 144) + rng.normal(0, 0.2, n)
# 盐度:与温度弱负相关,叠加潮周期
df["sal"] = 34.5 - 0.15 * (df["temp"] - 18) + 0.3 * np.sin(2 * np.pi * np.arange(n) / 74) + rng.normal(0, 0.05, n)
# 有效波高:受风过程影响,呈阵发性
df["wave_hs"] = np.abs(rng.gamma(2.0, 0.4, n))
# ---------- 2. 注入三类缺失模式 ----------
# (a) 盐度传感器间歇性掉线:连续 6~18 帧成块缺失,且与温度无关
sal_missing = np.zeros(n, dtype=bool)
i = 500
while i < n:
if rng.random() < 0.02:
L = rng.integers(6, 19)
sal_missing[i:i+L] = True
i += L
i += 1
# (b) 恶劣海况下通信中断:波高较大时温度、盐度、波高同时缺失(MAR/MNAR 混合)
storm = df["wave_hs"] > 1.6
comm_drop = storm & (rng.random(n) < 0.35)
# (c) 波高在低值段被阈值截断上报(MNAR:低于 0.1 m 记为缺失)
wave_low_missing = df["wave_hs"] < 0.1
df.loc[sal_missing, "sal"] = np.nan
df.loc[comm_drop, ["temp", "sal", "wave_hs"]] = np.nan
df.loc[wave_low_missing, "wave_hs"] = np.nan
# ---------- 3. 时空缺失分布统计 ----------
params = ["temp", "sal", "wave_hs"]
miss = df[params].isna()
print("各参数缺失率:")
print((miss.mean() * 100).round(2).astype(str) + " %")
# 按小时统计缺失率,观察时间聚集
df["hour"] = df["time"].dt.hour
hourly = miss.groupby(df["hour"]).mean() * 100
print("\n按小时缺失率(%):")
print(hourly.round(1))
# 连续缺失长度分布(以盐度为例)
def run_lengths(mask):
out, c = [], 0
for v in mask:
if v:
c += 1
elif c:
out.append(c); c = 0
if c: out.append(c)
return np.array(out)
for p in params:
rl = run_lengths(miss[p].values)
if len(rl):
print(f"{p}: 缺失段数={len(rl)}, 最长连续缺失={rl.max()} 帧, 平均={rl.mean():.1f} 帧")
# ---------- 4. 缺失机制判断 ----------
# 4.1 参数共缺:缺失指示变量两两相关
ind = miss.astype(int)
print("\n缺失指示变量相关矩阵:")
print(ind.corr().round(2))
# 4.2 缺失与观测值的关联:缺失时段的波高均值 vs 非缺失时段
for p in params:
obs = df.loc[~miss[p], "wave_hs"]
mis = df.loc[miss[p], "wave_hs"]
if len(mis.dropna()) > 0:
print(f"{p} 缺失时波高均值={mis.mean():.2f}, 非缺失时={obs.mean():.2f}")
# 4.3 盐度缺失与温度是否相关(检验 MCAR)
print("\n盐度缺失时温度均值=%.2f, 非缺失时=%.2f" % (
df.loc[miss["sal"], "temp"].mean(), df.loc[~miss["sal"], "temp"].mean()))
示例输出会呈现几个关键信号:盐度缺失率明显高于温度;盐度缺失段以 6~18 帧的连续块为主,说明是传感器掉线而非随机丢包;三个参数在通信中断时段同时缺失,缺失指示变量之间相关系数显著为正;波高缺失时段的波高均值偏高(风暴)或偏低(阈值截断),说明缺失与观测值本身相关,属于 MNAR 迹象。盐度缺失时温度均值与非缺失时接近,则支持该通道为 MCAR。
从统计结果到补全动作
得到上述统计后,按“缺失段长度 + 机制”两个维度落地补全:
- 盐度短块缺失(≤6 帧,MCAR):用时间索引线性插值或三次样条,插值后新增
sal_filled标记列,保留原始缺失位置,便于后续质量追溯。 - 风暴共缺段(MAR):以温度、波高为自变量对盐度做回归插补,或用多重插补生成多组候选值并取中位数;对温度本身缺失的帧,优先用相邻浮标或再分析数据做外部参照,而不是长时间外推。
- 波高低值截断(MNAR):不做点插值,改为记录“<0.1 m”的区间标记,统计时按区间中值或截断分布处理;若下游模型需要完整序列,应显式加入缺失指示特征,让模型自己学习截断效应。
连续缺失超过 18 帧(约 3 h)的段落不要用插值硬填,应标记为“不可用段”并从有效样本中剔除,否则会系统性压低方差、扭曲极值统计。
验证补全效果时,可从完整段中人为挖去同样长度的块,比较插值值与真值的 RMSE;对 MNAR 段则比较补全前后波高分布的偏度变化,偏度明显偏移说明插值引入了偏差。