如图1所示为汽车行进过程中产生的数据,图2为一个汽车行驶运动学的片段。
■ 图1 汽车行进中产生的数据
■ 图2 运动学片段
本文的任务是将汽车行进的数据切分为一个个的运动学片段,再对这些运动学片段进行分类,从而获得每一类运动学片段的代表片段,合成汽车工况。
01、基于K-Means的汽车行驶运动学片段分类
下面用K-Means来对运动学片段进行聚类。聚类时关键的一点在聚类数目的选取上,本文将采取两种方案来对聚类数目进行选取,如代码清单1所示。首先是定义并计算SSE的函数,并绘制出随着聚类数目变化,SSE变化的折线图。
代码清单1 绘制SSE变化曲线图函数
def getSSE(input):
# 存储不同簇数的SSE值
distortions = []
for i in range(1, 11):
km = KMeans(n_clusters=i, init="k-means++", n_init=10, max_iter=300, tol=1e-4, random_state=0)
km.fit(input)
distortions.append(km.inertia_)
# 绘制结果
plt.plot(range(1, 11), distortions, marker='o')
plt.xlabel("Cluster_num")
plt.ylabel("SSE")
plt.show()
由于原始数据中,不同特征的量纲不尽相同,如果使用原始数据直接进行聚类,会存在量纲不一致的问题,即数字较大的特征会对模型产生较大影响,因此在进行聚类之前,还需要进行数据归一化的处理,如代码清单2修改main函数为
代码清单2 主函数1
if __name__ == "__main__":
feature = cutPart()
scaler = preprocessing.StandardScaler().fit(feature)
feature = scaler.transform(feature)
getSSE(feature)
运行main函数,可得SSE变化图如图3所示。
■ 图3 SSE随聚类数目变化折线图
如图3所示,随着聚类数目增多,并没有出现及其明显的手肘,但通过观察可得,当聚类数目为2或3时,误差下降的幅度是最为明显的,从4开始往后,基本上就处于线性递减的状态。因此,最优的聚类数目可能为2、3、4中的某一个。
下面通过轮廓图从另一个角度进行分析。首先是绘制轮廓图有关的函数,如代码清单2所示。
代码清单2 绘制轮廓图函数
def getSilehotte(input, n_cluster):
km = KMeans(n_clusters=n_cluster, init="k-means++", n_init=10, max_iter=300, tol=1e-4, random_state=0)
y_km = km.fit_predict(input, n_cluster)
# 获取簇的标号
cluster_labels = np.unique(y_km)
silehoutte_vals = silhouette_samples(input, y_km, metric="euclidean")
y_ax_lower, y_ax_upper = 0, 0
y_ticks = []
for i, c in enumerate(cluster_labels):
# 获得不同簇的轮廓系数
c_silhouette_vals = silehoutte_vals[y_km == c]
c_silhouette_vals.sort()
y_ax_upper += len(c_silhouette_vals)
color = cm.jet(i / n_cluster)
plt.barh(range(y_ax_lower, y_ax_upper), c_silhouette_vals, height=1.0, edgecolor="none", color=color)
y_ticks.append((y_ax_lower + y_ax_upper) / 2)
y_ax_lower += len(c_silhouette_vals)
silehoutte_avg = np.mean(silehoutte_vals)
plt.axvline(silehoutte_avg, color="red", linestyle="--")
plt.yticks(y_ticks, cluster_labels + 1)
plt.ylabel("Cluster")
plt.xlabel("Silehotte_value")
plt.show()
该函数的输入为特征以及聚类数目,每个聚类数目可得一个轮廓图。如代码清单3修改main函数为
代码清单3 主函数2
if __name__ == "__main__":
feature = cutPart()
scaler = preprocessing.StandardScaler().fit(feature)
feature = scaler.transform(feature)
getSilehotte(feature, 2)
getSilehotte(feature, 3)
getSilehotte(feature, 4)
分别绘制聚类数目为2、3、4时的轮廓图帮助后期分析,运行脚本可得如图4、图5、图6所示。
■ 图4 聚类数目为2时轮廓图
■ 图5 聚类数目为3时轮廓图
■ 图6 聚类数目为4时轮廓图
如图4、图5、图6所示,当聚类个数为2时,轮廓系数表现不错,说明可以有效地聚类。当聚类数目上升到3时,第一类开始出现负值,即聚类效果开始下降,进一步上升到4时,出现了严重的数目不均等,第4类的数目远远小于前三类,基于对轮廓图的分析,可以选择聚类数目为2或者3。
倘若将运动学片段分为3类,并对每一类进行分析,可以发现这三类中,第1类平均速度最高,怠速时间比例最低,代表在畅通路段行驶的工况;第3类平均车速最低,怠速时间比例最高,代表拥堵路段行驶的工况;第2类代表一般工况。
对每一类按照距离聚类中心的距离从小到大进行排序,就可以得到每一类的典型运动学片段。在将这些运动学片段进行组合,就可以最终获得汽车行驶工况。
如图7所示为基于K-Means方法合成的汽车行驶工况。
■ 图7 合成代表工况