
本文详解如何对含 mmsi 和 departures_count 分组标识的船舶轨迹数据,按 10 分钟固定频率进行重采样,并对 calc_speed、coursechange 等多列执行线性/立方插值,避免 groupby + resample 直接调用导致的 nan 或重复值问题。
在船舶 AIS 数据分析中,原始记录常存在不规则采样间隔(如数秒至数十分钟不等),而下游建模或可视化往往要求统一时间粒度(如每 10 分钟一个观测点)。直接使用 groupby(...).resample('10Min').interpolate() 容易失败——因为 resample 在分组内默认以原始索引为基准聚合,若未对齐目标网格,会返回全 NaN 或错误复用首尾值。
正确做法是:为每个 (mmsi, departures_count) 子组独立构建 10 分钟规则时间网格,再通过 reindex → interpolate → reindex 三步完成精准插值。该方案兼顾上采样(填补密集点间的空隙)与下采样(跨大间隔插值),且天然支持多列同步处理。
以下为完整实现代码(支持线性与立方插值):
import pandas as pd
import numpy as np
def resample_trip(g, freq='10Min', method='linear'):
"""
对单个航次子组执行等间隔重采样插值
:param g: 分组后的 DataFrame(已设 timestamp 为 DatetimeIndex)
:param freq: 目标频率,如 '10Min'
:param method: 插值方法,'linear' / 'cubic' / 'time' 等
:return: 重采样后 DataFrame,索引为规则时间点
"""
if len(g) == 0:
return g
# 步骤1:生成覆盖全程的规则时间网格(向上/向下取整到 freq 边界)
start = g.index[0].floor(freq)
end = g.index[-1].ceil(freq)
rng = pd.date_range(start, end, freq=freq)
# 步骤2:扩展索引并插值(limit_direction='both' 确保两端可插)
g_extended = g.reindex(g.index.union(rng))
g_interp = g_extended.interpolate(method=method, limit_direction='both')
# 步骤3:精确提取目标网格点(丢弃原始非网格点)
result = g_interp.reindex(rng)
return result
# 预处理:确保 timestamp 为 datetime 并设为索引
df["timestamp"] = pd.to_datetime(df["timestamp"])
df = df.set_index("timestamp")
# 按 MMSI 与航次分组,应用自定义重采样函数
out = df.groupby(["mmsi", "departures_count"], group_keys=False).apply(
resample_trip, freq='10Min', method='cubic' # ← 切换 cubic 即可启用三次样条插值
)
print(out.head(10))✅ 关键优势说明:
- 分组隔离性:每个 (mmsi, departures_count) 独立生成时间网格,避免跨航次污染;
- 插值鲁棒性:reindex(...union...) 确保原始点全部保留,interpolate(limit_direction='both') 允许双向延伸插值(尤其对起止点有效);
- 方法灵活:仅需修改 method 参数即可切换 'linear'(默认)、'cubic'(需至少 4 个点)、'time'(按实际时间距离加权)等;
- 多列安全:所有数值列(如 calc_speed, coursechange, lat, lon)自动同步插值,无需逐列指定。
⚠️ 注意事项:
- 若某航次数据点少于 2 个,cubic 插值将退化为线性或报错,建议增加判断逻辑(如 if len(g)
- coursechange(航向变化)存在周期性(0°/360°跳变),线性插值可能产生不合理中间值,此时应先做相位解缠(np.unwrap)再插值;
- 对超大数据集(228 万+ 行),可添加 progress_apply 或分块处理提升响应速度。
通过该方法,您将获得严格对齐的 10 分钟粒度轨迹数据,为后续速度趋势分析、航向模式挖掘或深度学习建模奠定高质量时序基础。









