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~18 帧连续段 与温度无显著关联 三参数同时缺失 集中于高波高时段 缺失指示变量正相关 波高:低值段缺失 低于 0.1 m 被截断 缺失与真值相关 统计量:缺失率、连续段长 小时分布、共缺相关矩阵 缺失机制判断 MCAR 缺失与观测值无关 盐度掉线段 MAR 缺失依赖其他可观测参数 风暴通信中断段 MNAR 缺失依赖未观测的真值 波高低值截断 判据:共缺相关 + 缺失组 与非缺失组观测值差异 针对性补全策略 短块线性/样条插值 限 ≤ 6 帧,标注来源 适用盐度掉线段 多参数回归/多重插补 用温度、波高预测盐度 适用 MAR 共缺段 不插值,保留缺失标记 或按截断阈值做区间估计 适用波高 MNAR 段 输出:补全数据 + 缺失标记列 + 策略适用性说明

从统计结果到补全动作

得到上述统计后,按“缺失段长度 + 机制”两个维度落地补全:

  • 盐度短块缺失(≤6 帧,MCAR):用时间索引线性插值或三次样条,插值后新增 sal_filled 标记列,保留原始缺失位置,便于后续质量追溯。
  • 风暴共缺段(MAR):以温度、波高为自变量对盐度做回归插补,或用多重插补生成多组候选值并取中位数;对温度本身缺失的帧,优先用相邻浮标或再分析数据做外部参照,而不是长时间外推。
  • 波高低值截断(MNAR):不做点插值,改为记录“<0.1 m”的区间标记,统计时按区间中值或截断分布处理;若下游模型需要完整序列,应显式加入缺失指示特征,让模型自己学习截断效应。
连续缺失超过 18 帧(约 3 h)的段落不要用插值硬填,应标记为“不可用段”并从有效样本中剔除,否则会系统性压低方差、扭曲极值统计。
验证补全效果时,可从完整段中人为挖去同样长度的块,比较插值值与真值的 RMSE;对 MNAR 段则比较补全前后波高分布的偏度变化,偏度明显偏移说明插值引入了偏差。