如何用K-means聚类由连续连线组成的轨迹?
完整轨迹聚类的可行方法
针对连续线段构成的完整轨迹聚类,不能直接用单线段的K-means思路——因为轨迹是有序的连续序列,需要结合轨迹的整体形态、序列特征来处理,以下是几种实用方案:
方案1:提取轨迹全局特征 + 传统聚类
把每条轨迹转换成固定长度的特征向量,再用你熟悉的K-means这类算法聚类。核心是提取能代表轨迹整体特征的指标,比如起点/终点坐标、轨迹总长度、平均方向角、x/y方向最大位移、拐点数量等。
示例代码
import numpy as np from sklearn.cluster import KMeans # 提取单条轨迹的特征 def extract_trajectory_features(trajectory_df): # 把连续线段转换成完整点序列 points = [(trajectory_df.iloc[0]['start_x'], trajectory_df.iloc[0]['start_y'])] for _, row in trajectory_df.iterrows(): points.append((row['end_x'], row['end_y'])) points = np.array(points) # 计算各类特征 start_point = points[0] end_point = points[-1] total_length = np.sum(np.linalg.norm(points[1:] - points[:-1], axis=1)) # 计算线段方向角的均值 dx = points[1:, 0] - points[:-1, 0] dy = points[1:, 1] - points[:-1, 1] avg_angle = np.mean(np.arctan2(dy, dx)) # x/y方向的最大位移 max_disp_x = np.max(points[:,0]) - np.min(points[:,0]) max_disp_y = np.max(points[:,1]) - np.min(points[:,1]) # 统计拐点数量(方向角变化超过90度的次数) angle_diff = np.abs(np.diff(np.arctan2(dy, dx))) inflection_count = np.sum(angle_diff > np.pi/2) # 拼接成特征向量 return np.hstack([start_point, end_point, total_length, avg_angle, max_disp_x, max_disp_y, inflection_count]) # 假设你有一个列表trajectory_dfs,每个元素是一条完整轨迹的DataFrame feature_matrix = np.array([extract_trajectory_features(df) for df in trajectory_dfs]) # 运行K-means聚类 kmeans = KMeans(n_clusters=3) # 替换为你需要的聚类数 cluster_labels = kmeans.fit_predict(feature_matrix)
方案2:动态时间规整(DTW) + 基于距离的聚类
如果轨迹长度不一但形态相似(比如一条是另一条的慢放/快放版),可以用DTW计算两条轨迹的相似度,再用层次聚类或DBSCAN这类基于距离的算法。DTW能对齐不同长度的序列,精准计算形状相似度。
示例代码
import numpy as np from sklearn.cluster import AgglomerativeClustering from scipy.spatial.distance import cdist import fastdtw # 需要先安装:pip install fastdtw # 把每条轨迹转换成点序列 def get_trajectory_points(trajectory_df): points = [(trajectory_df.iloc[0]['start_x'], trajectory_df.iloc[0]['start_y'])] for _, row in trajectory_df.iterrows(): points.append((row['end_x'], row['end_y'])) return np.array(points) trajectory_points_list = [get_trajectory_points(df) for df in trajectory_dfs] # 定义DTW距离计算函数 def dtw_similarity(t1, t2): distance, _ = fastdtw.fastdtw(t1, t2, dist=lambda x,y: np.linalg.norm(x-y)) return distance # 生成两两轨迹的距离矩阵 distance_matrix = cdist(trajectory_points_list, trajectory_points_list, metric=dtw_similarity) # 用层次聚类 cluster = AgglomerativeClustering(n_clusters=3, affinity='precomputed', linkage='average') cluster_labels = cluster.fit_predict(distance_matrix)
方案3:轨迹简化 + 特征聚类
如果轨迹节点太多,先通过Douglas-Peucker算法简化轨迹,保留关键节点(比如拐点),再用简化后的轨迹提取特征或直接聚类——既能大幅减少计算量,又能保留轨迹核心形态。
示例代码
import numpy as np from sklearn.cluster import KMeans # Douglas-Peucker轨迹简化算法 def douglas_peucker(points, epsilon): if len(points) <= 2: return points.tolist() start, end = points[0], points[-1] # 计算中间点到线段的距离 cross = (end[0]-start[0])*(start[1]-points[1:-1,1]) - (start[0]-points[1:-1,0])*(end[1]-start[1]) distances = np.abs(cross) / np.linalg.norm(end - start) max_dist_idx = np.argmax(distances) + 1 if distances[max_dist_idx-1] > epsilon: left = douglas_peucker(points[:max_dist_idx+1], epsilon) right = douglas_peucker(points[max_dist_idx:], epsilon) return left[:-1] + right else: return [start.tolist(), end.tolist()] # 简化所有轨迹(epsilon根据坐标尺度调整,值越大简化越彻底) simplified_trajectories = [] for df in trajectory_dfs: raw_points = get_trajectory_points(df) simplified = np.array(douglas_peucker(raw_points, epsilon=5.0)) simplified_trajectories.append(simplified) # 提取简化轨迹的特征并聚类 feature_matrix = [] for simp_pts in simplified_trajectories: start = simp_pts[0] end = simp_pts[-1] total_length = np.sum(np.linalg.norm(simp_pts[1:] - simp_pts[:-1], axis=1)) avg_angle = np.mean(np.arctan2(simp_pts[1:,1]-simp_pts[:-1,1], simp_pts[1:,0]-simp_pts[:-1,0])) feature_matrix.append(np.hstack([start, end, total_length, avg_angle, len(simp_pts)])) kmeans = KMeans(n_clusters=3) cluster_labels = kmeans.fit_predict(np.array(feature_matrix))
注意事项
- 如果轨迹包含时间信息,可以加入总时长、平均速度等时间特征,提升聚类准确性。
- 若坐标是经纬度,需先转换为平面坐标(比如UTM投影)再计算距离,避免球面距离带来的误差。
- DTW计算量较大,当轨迹数量超过100条时,建议先做特征降维,或使用近似DTW算法优化速度。
内容的提问来源于stack exchange,提问作者Magnus
相关产品推荐
相关产品推荐

