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

如何实现Python中低耗时的DTW算法以完成大规模时间序列聚类

时间序列聚类优化:降低DTW+层次聚类的计算耗时

我有1977位客户的时间序列数据,每个序列包含17544个数据点(两年的小时级数据),需要将客户划分到对应聚类群组。以下是我的实现代码(list_of_lists为存储各客户时间序列的列表):

from dtw import dtw
import numpy as np

# 定义自定义距离函数
def my_dist(x, y):
   return np.abs(x - y) 

# 计算两两DTW距离
distances = np.zeros((len(list_of_lists), len(list_of_lists)))
for i in range(len(list_of_lists)):
  for j in range(i+1, len(list_of_lists)):
      x = list_of_lists[i]
      y = list_of_lists[j]
      distance, *rest = dtw(x, y, dist=my_dist)
      distances[i,j] = distance
      distances[j,i] = distance

from sklearn.cluster import AgglomerativeClustering
# 基于距离矩阵执行层次聚类
clustering = AgglomerativeClustering(n_clusters=cluster_count, metric=None, 
            linkage='average')
labels = clustering.fit_predict(distances)
print(labels)

但当前代码计算耗时过长,以下是几种能大幅减少计算时间的优化方案:

1. 替换为更快的DTW近似实现

原生DTW是精确算法,时间复杂度为O(n*m)(n、m为序列长度),对于17544长度的序列,单次计算成本极高。fastdtw是DTW的近似实现,时间复杂度降至O(n+m),速度提升数十倍,且精度损失在可接受范围内。

安装依赖:

pip install fastdtw

修改代码:

from fastdtw import fastdtw
import numpy as np

# 直接用numpy的L1距离(比自定义Python函数快)
def my_dist(x, y):
    return np.linalg.norm(x - y, ord=1)

# 计算距离矩阵
n_samples = len(list_of_lists)
distances = np.zeros((n_samples, n_samples))
for i in range(n_samples):
    for j in range(i+1, n_samples):
        distance, _ = fastdtw(list_of_lists[i], list_of_lists[j], dist=my_dist)
        distances[i,j] = distance
        distances[j,i] = distance

2. 对时间序列做降维处理

17544个小时级数据包含大量冗余信息,可通过聚合或特征提取缩短序列长度:

  • 按时间周期聚合:比如将每24小时的小时数据聚合为日均值/最大值/最小值,序列长度从17544压缩到730(两年共730天);
  • 提取周期特征:针对小时级数据,提取每周/每日的模式特征(比如每天各小时的均值、每周各天的均值);
  • PCA降维:对序列做PCA,保留95%以上的方差,将高维序列压缩到低维空间。

示例:按天聚合小时数据

# 假设list_of_lists中每个元素是形状为(17544,)的numpy数组
aggregated_series = []
for series in list_of_lists:
    # 重塑为(730, 24),按天分组
    daily_groups = series.reshape(-1, 24)
    # 取每天的均值作为聚合后的数据点
    daily_mean = daily_groups.mean(axis=1)
    aggregated_series.append(daily_mean)
# 之后用aggregated_series代替原list_of_lists计算距离

3. 并行化距离矩阵计算

原代码用单线程循环计算两两距离,可通过多进程并行加速,利用CPU多核资源。使用joblib实现并行:

from joblib import Parallel, delayed
import numpy as np
from fastdtw import fastdtw

def compute_pairwise_dist(i, j, data):
    distance, _ = fastdtw(data[i], data[j], dist=lambda x,y: np.linalg.norm(x-y, ord=1))
    return (i, j, distance)

n_samples = len(aggregated_series)
# 生成所有i<j的索引对
index_pairs = [(i,j) for i in range(n_samples) for j in range(i+1, n_samples)]

# 并行计算,n_jobs设为CPU核心数
results = Parallel(n_jobs=-1)(delayed(compute_pairwise_dist)(i,j,aggregated_series) for i,j in index_pairs)

# 填充距离矩阵
distances = np.zeros((n_samples, n_samples))
for i,j,d in results:
    distances[i,j] = d
    distances[j,i] = d

4. 更换聚类算法,避免全量距离矩阵计算

层次聚类需要预先计算O(n²)的全量距离矩阵,对于1977个样本,这需要近400万次DTW计算。可更换为无需全量距离矩阵的算法:

  • HDBSCAN:基于密度的聚类,支持自定义距离函数,可通过近似最近邻减少计算量;
  • DBSCAN:配合BallTree/KDTree加速最近邻搜索;
  • k-medoids(PAM):用sklearn_extra.cluster.KMedoids,可自定义距离,但需注意样本量。

示例:用HDBSCAN结合fastdtw

import hdbscan
from fastdtw import fastdtw

# 定义距离函数供HDBSCAN使用
def dtw_distance(x, y):
    distance, _ = fastdtw(x, y, dist=lambda a,b: np.linalg.norm(a-b, ord=1))
    return distance

# HDBSCAN会自动处理距离计算,无需预先生成全量矩阵
clusterer = hdbscan.HDBSCAN(metric=dtw_distance, min_cluster_size=5)
labels = clusterer.fit_predict(aggregated_series)

5. 使用内置优化的距离函数

原代码自定义的my_dist是Python层面的函数,调用开销大。直接使用numpy的内置距离计算,或fastdtw支持的内置距离(比如'manhattan'),这些实现经过C级优化,速度更快:

# 无需自定义函数,直接传入'manhattan'给fastdtw
distance, _ = fastdtw(x, y, dist='manhattan')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:52:48