You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于单变量时间戳数据集实现Univariate Time series clustering

单变量时间序列聚类落地流程

1. 数据预处理

先把原始数据规整成算法能接收的格式,这步偷懒后面全是无效结果:

  • 按sample#字段分组,每个分组对应一条独立时间序列,组内按time-stamp升序排列。如果是固定间隔采样的场景,先补全所有缺失的时间点,缺失的观测值根据业务场景选线性插值、前向填充处理;如果序列本身不等长,要么统一截断/补值到相同长度,要么后续选支持不等长输入的距离度量。
  • 做基础异常值过滤:用3σ原则或者滑动窗口中位数滤波去掉明显离群的尖峰值,避免极端值干扰距离计算。
  • 按需做归一化:如果不同样本的观测值基线差异很大(比如不同传感器的读数偏移差几个量级),对每条序列单独做Z-score标准化,避免幅值大的样本主导距离计算;如果聚类需要保留原始幅值差异(比如要区分高负载、低负载的运行序列)就跳过这步。

2. 相似度度量选择

这步是决定聚类效果的核心,90%的无效聚类都是度量选错了,单变量时序常用的度量按优先级选择:

  • DTW(动态时间规整)距离:通用场景首选,支持时间轴上的弹性对齐,对序列的时间平移、局部拉伸压缩鲁棒,不等长序列也能直接计算。计算时可以加10%序列长度的窗口约束,能把计算速度提好几倍。
  • 特征向量距离:如果样本量很大、序列很长,DTW计算太慢,可以先给每条序列提取固定维度的特征:比如时域的均值、方差、峰值、自相关系数,频域的FFT主成分、小波分解系数,把时序转成普通特征向量之后再算欧氏距离就行,速度快但会损失部分时序形状细节。
  • Shapelet距离:如果你的聚类目标是识别具有相似局部形状的序列(比如都包含同形态的故障脉冲),可以选Shapelet度量,可解释性强但计算成本高,适合小样本场景。
  • 直接用欧氏距离:仅适合长度完全一致、时间轴完全对齐、没有任何偏移的序列,大部分场景效果很差,不推荐优先尝试。

3. 聚类算法选型

根据数据规模和是否已知聚类数选择即可:

  • 层次聚类(AGNES):样本量小于1000时优先选,不需要初始化质心,直接喂提前算好的DTW距离矩阵就能跑,可以通过树状图直观选聚类数,链接规则选average或者ward效果最稳。
  • K-Shape/DTW-KMeans:专门针对时序优化的K-Means变种,中等规模样本(几千条)用这个速度快,需要提前指定聚类数,比普通K-Means对时序的适配性好很多。
  • DBSCAN:如果你不知道要聚成几类、数据里混有异常离群序列,就搭配DTW距离用DBSCAN,能自动筛掉噪声样本、自动确定簇数,缺点是需要调邻域半径eps和最小样本数min_samples两个参数。

注意:别直接拿原始时序喂普通K-Means,它默认用欧氏距离计算相似度,对时序的时间偏移极其敏感,出来的簇基本没有业务意义。

4. 聚类数确定与结果校验

  • 用需要指定聚类数的算法时,不要随意拍k值,遍历2到业务上能接受的最大簇数,算每个k对应的轮廓系数(越接近1说明簇内越紧凑、簇间差异越大)、邓恩指数(值越大效果越好),选指标拐点对应的k值就行。
  • 别只信数值指标,聚完一定要把同一个簇的所有序列叠到同一张折线图上看,确认同簇序列的趋势、形状确实符合业务逻辑,很多时候数值指标好看,但聚出来的结果完全不符合业务常识。

5. 最简实现参考(Python栈)

import pandas as pd
import numpy as np
from tslearn.utils import to_time_series_dataset
from tslearn.preprocessing import TimeSeriesScalerMeanVariance
from tslearn.clustering import TimeSeriesKMeans, silhouette_score
from scipy.spatial.distance import squareform
from scipy.cluster.hierarchy import linkage, fcluster
from fastdtw import fastdtw

# 读取并规整数据
df = pd.read_csv("your_dataset.csv")
series_collection = []
valid_sample_ids = []
for sample_id, group in df.groupby("sample#"):
    # 组内按时间戳排序
    group_sorted = group.sort_values("time-stamp")
    # 这里可以自行补全缺失时间点、填充缺失值的逻辑
    obs_values = group_sorted["single_obs"].values
    series_collection.append(obs_values.reshape(-1, 1))
    valid_sample_ids.append(sample_id)

# 转成时序算法要求的数组格式,自动补零对齐不等长序列
X = to_time_series_dataset(series_collection)
# 按需做单序列标准化
scaler = TimeSeriesScalerMeanVariance()
X_scaled = scaler.fit_transform(X)

# 方案1:小样本场景 DTW+层次聚类
sample_count = len(X_scaled)
dtw_dist_mat = np.zeros((sample_count, sample_count))
for i in range(sample_count):
    for j in range(i+1, sample_count):
        d, _ = fastdtw(X_scaled[i].flatten(), X_scaled[j].flatten(), radius=10) # radius是DTW的窗口约束
        dtw_dist_mat[i,j] = d
        dtw_dist_mat[j,i] = d
# 层次聚类,t设为你选的聚类数
linkage_matrix = linkage(squareform(dtw_dist_mat), method="average")
cluster_labels = fcluster(linkage_matrix, t=3, criterion="maxclust")

# 方案2:中大规模样本 DTW-KMeans
# 先遍历选最优k
for k in range(2, 7):
    model = TimeSeriesKMeans(n_clusters=k, metric="dtw", random_state=42)
    labels = model.fit_predict(X_scaled)
    s_score = silhouette_score(X_scaled, labels, metric="dtw")
    print(f"聚类数为{k}时的轮廓系数:{round(s_score,3)}")
# 选最优k训练最终模型
final_model = TimeSeriesKMeans(n_clusters=3, metric="dtw", random_state=42)
final_labels = final_model.fit_predict(X_scaled)

常见踩坑点

  • 不要上来就用复杂的深度学习聚类模型,先拿DTW+层次聚类/K-Shape跑通基线,90%的场景下传统方法效果比随便搭的深度模型好,调参成本还低。
  • 不要不做序列对齐、缺失值处理就直接跑算法,出来的结果没有任何参考价值。
  • 聚类结果最终要服务业务,不要纯看数学指标选结果,比如做设备故障聚类,同个簇的序列要对应同一种故障类型才是有效结果。

内容的提问来源于stack exchange,提问作者Farooq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 00:27:19