1. 单站逐时潮位序列的预处理与质量检查
为什么预处理决定后续分潮参数的可靠性
调和分析的最小二乘拟合假设输入序列是等间隔、同一时间基准、同一单位、无粗差的逐时潮位。原始观测往往同时存在三类问题:缺测(仪器故障、通信中断)、异常值(船舶撞击、气泡、结冰、传感器漂移)、以及时间与单位不统一(地方时/UTC混用、单位混用为cm与m)。这些缺陷若直接进入设计矩阵,会污染M2、S2、K1、O1的振幅与迟角估计,且残差检查难以定位原因。因此本例的任务是:把一段原始逐时序列处理成可追溯的干净序列,并留下处理日志。
处理流程与判定逻辑
下面这张图给出本例的核心处理链:从原始序列出发,依次经过时间基准统一、单位换算、缺测识别、异常值标记与插补决策,最终输出干净序列与处理日志。图中同时标出每一步的判定条件与记录内容,便于复现。
用一段可运行的Python完成预处理
下面代码以教学示例数据演示完整流程。示例序列为24小时逐时潮位,故意植入1个缺测(-999)、1个尖峰异常(+3.2 m跳变)、以及单位混用(部分为cm)。代码输出干净序列与处理日志,可直接运行验证。
import pandas as pd
import numpy as np
# ---------- 1. 构造教学示例原始序列 ----------
# 时间:2024-06-01 00:00 至 23:00,地方时 UTC+8
times = pd.date_range("2024-06-01 00:00", periods=24, freq="h", tz="Asia/Shanghai")
# 模拟半日潮,振幅约1.2m,均值2.0m
h_true = 2.0 + 1.2 * np.sin(2 * np.pi * np.arange(24) / 12.42)
raw = h_true.copy()
raw[5] = -999.0 # 缺测哨兵值
raw[12] = raw[12] + 3.2 # 尖峰异常
raw[18] = raw[18] * 100 # 单位误为cm
raw = np.round(raw, 3)
df = pd.DataFrame({"time_local": times, "raw": raw})
# ---------- 2. 时间基准统一:地方时 -> UTC ----------
df["time_utc"] = df["time_local"].dt.tz_convert("UTC")
# ---------- 3. 单位换算:识别cm量级并转为m ----------
# 站点历史水位范围约 0.5~3.5 m,超过10视为cm
df["h_m"] = np.where(df["raw"].abs() > 10, df["raw"] / 100.0, df["raw"])
# ---------- 4. 缺测识别 ----------
df["is_missing"] = (df["h_m"] == -999.0) | df["h_m"].isna()
# ---------- 5. 异常值标记(基于一阶差分) ----------
df["dh"] = df["h_m"].diff().abs()
# 阈值1.5 m/h;缺测点不参与差分判断
df["is_outlier"] = (df["dh"] > 1.5) & (~df["is_missing"])
# 异常点不删除,先标记
df.loc[df["is_outlier"], "h_m"] = np.nan
# ---------- 6. 插补决策:连续缺测<=3h线性插补 ----------
df["h_clean"] = df["h_m"].interpolate(method="linear", limit=3, limit_direction="both")
df["quality"] = 0
df.loc[df["is_missing"], "quality"] = 1
df.loc[df["is_outlier"], "quality"] = 2
df.loc[df["h_clean"].isna(), "quality"] = 3
# ---------- 7. 输出干净序列与处理日志 ----------
clean = df[["time_utc", "h_clean", "quality"]].rename(columns={"h_clean": "h_m"})
log = df[["time_utc", "raw", "h_m", "quality"]].copy()
log["action"] = "正常"
log.loc[df["is_missing"], "action"] = "缺测标记"
log.loc[df["is_outlier"], "action"] = "异常标记"
log.loc[df["quality"] == 1, "action"] = "缺测+插补"
log.loc[df["quality"] == 3, "action"] = "不可用"
print("干净序列前5行:")
print(clean.head())
print("\n处理日志(仅显示非正常记录):")
print(log[log["action"] != "正常"])
print("\n统计:缺测", df["is_missing"].sum(), "个,异常", df["is_outlier"].sum(), "个,不可用", (df["quality"]==3).sum(), "个")
结果判读与通过条件
运行后应看到:缺测1个(第5小时)、异常1个(第12小时)、单位换算1个(第18小时)。第5小时与第12小时因连续缺测长度均为1(≤3h),被线性插补,质量标记为1或2;第18小时经单位换算后恢复正常,质量标记为0。若某段连续缺测超过3小时,该段质量标记为3,不参与后续最小二乘拟合。通过条件为:输出序列时间间隔严格为1h、单位为m、无-999哨兵值、质量标记列完整、日志中每条修改均可回溯到原始值。
注意:异常值阈值1.5 m/h需按站点潮差调整。强潮河口半日潮涨落可达数米每小时,固定阈值会误判正常潮汐为异常。建议先用站点历史序列统计一阶差分的99%分位数作为阈值,并在日志中记录阈值来源。
时间基准统一时务必确认原始时间戳的时区。若原始记录为地方时但未标注时区,需向数据提供方确认,不可默认按UTC处理,否则后续分潮迟角会整体偏移。