首页 > 解决方案 > 如何强制以特定的明显模式对数据进行聚类?

问题描述

我有大量车辆的“车速与发动机转速”值。我试图预测车辆在每个档位上花费的时间。

我在数据集上运行 K-Means 聚类并得到以下结果: 车速与发动机转速(~86000 点)

显然,我的算法未能捕捉到明显的模式。我想强制 K-Means(或任何其他聚类算法,就此而言)沿着六个倾斜线对数据进行聚类。相关代码片段:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.cluster import KMeans

plt.rcParams['figure.figsize'] = (16, 9)
plt.style.use('ggplot')

# Importing the dataset
data = pd.read_csv('speedRpm.csv')
print(data.shape)
data.head()

# Getting the data points
f1 = data['rpm'].values
f2 = data['speed'].values
X = np.array(list(zip(f1, f2)))

# Number of clusters
k = 5

kmeans = KMeans(n_clusters=k)
# Fitting the input data
kmeans = kmeans.fit(X)
# Getting the cluster labels
labels = kmeans.predict(X)
# Centroid values
centroids = kmeans.cluster_centers_

labeled_array = {i: X[np.where(kmeans.labels_ == i)] for i in range(kmeans.n_clusters)}

colors = ['r', 'g', 'b', 'y', 'c']
fig, ax = plt.subplots()
for i in range(k):
        points = np.array([X[j] for j in range(len(X)) if kmeans.labels_[j] == i])
        ax.scatter(points[:, 0], points[:, 1], s=7, c=colors[i])
ax.scatter(centroids[:, 0], centroids[:, 1], marker='*', s=200, c='#050505')

plt.show()

我如何确保聚类算法捕获正确的模式,即使它可能不是最有效的?

谢谢!

编辑

这次使用 DBSCAN 运行同一组点。在玩了一段时间后,得到了以下结果epsmin_samples

在此处输入图像描述

尽管仍然不完美且异常值太多,但该算法开始捕捉线性趋势。

代码:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.cluster import KMeans
from sklearn.cluster import DBSCAN

plt.rcParams['figure.figsize'] = (16, 9)
plt.style.use('ggplot')

# Importing the dataset
data = pd.read_csv('speedRpm.csv')
print(data.shape)
data.head()

# Getting the values and plotting it
f1 = data['rpm'].values
f2 = data['speed'].values
X = np.array(list(zip(f1, f2)))

# DBSCAN

# Compute DBSCAN
db = DBSCAN(eps=1.1, min_samples=3).fit(X)
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
labels = db.labels_

# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
print "Estimated Number of Clusters", n_clusters_

# Black removed and is used for noise instead.
unique_labels = set(labels)
colors = [plt.cm.Spectral(each)
          for each in np.linspace(0, 1, len(unique_labels))]
for k, col in zip(unique_labels, colors):
    if k == -1:
        # Black used for noise.
        col = [0, 0, 0, 1]

    class_member_mask = (labels == k)

    xy = X[class_member_mask & core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=14)

    xy = X[class_member_mask & ~core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),
             markeredgecolor='k', markersize=6)

plt.title('Estimated number of clusters: %d' % n_clusters_)
plt.show()

标签: pythonscikit-learndata-science

解决方案


高水平

这里有两个主要选项:

  1. 转换您的数据,以便 k-means 风格的聚类算法成功
  2. 选择不同的算法

次要选项:

  1. 通过强制初始化更智能来调整 kmeans

选项 2

Python在这里很好地描述了几种聚类算法。从链接中,一个(粗略裁剪的)有用的图形:

在此处输入图像描述

此行看起来与您的数据集相似;你试过高斯混合模型吗?GMM几乎没有众所周知的理论性质,但它的工作原理是根据从数据计算的后验来分配点属于每个聚类中心的概率。您通常可以使用 Sklearn 为您做的 kmeans 初始化它。

同样,基于密度的聚类算法(例如DBSCAN)似乎是一个合乎逻辑的选择。您的数据对密集簇进行了很好的分割,这似乎是一个很好的拓扑属性来过滤。在链接的维基百科页面上的图像中:

在此处输入图像描述

他们提供标题:

DBSCAN 可以找到非线性可分簇。该数据集无法用 k-means 充分聚类

这似乎说明了你的烦恼。


更多关于你的烦恼

Kmeans 是一种非常通用的算法,但它不是全局最优的,并且存在很多弱点。这里是密集阅读

除了像米老鼠问题这样的问题,kmeans 经常试图最小化到质心的简单欧几里得距离。虽然这对很多问题都很有意义,但在您的问题中却没有意义,因为集群的偏斜意味着这不是正确的衡量标准。请注意,上面显示的其他算法(如凝聚/层次聚类)使用类似的度量,具有类似的陷阱。

我没有介绍转换您的数据或调整 kmeans,因为后者实际上需要破解(或编写您自己的)聚类算法(鉴于 sklearn 和类似包的覆盖范围,我不建议用于简单的探索性问题),其中前者似乎是对您的确切数据敏感的本地解决方案。ICA可能是一个不错的开始,但该任务有很多选择


推荐阅读