我使用DBSCAN使用Scikit-Learn(Python 2.7)聚集一些数据:
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(random_state=0)
dbscan.fit(X)
然而,我发现没有内置函数(除了“fit_predict”之外)可以将新数据点Y分配给原始数据X中标识的聚类.K-means方法有一个“预测”功能,但我希望能够对DBSCAN做同样的事情。像这样:
dbscan.predict(X, Y)
因此密度可以从X推断,但返回值(集群赋值/标签)仅适用于Y.从我所知道的,这个功能在R中可用,所以我假设它在某种程度上也可用于Python 。我似乎无法找到任何相关的文档。
此外,我已经尝试搜索为什么DBSCAN不能用于标记新数据的原因,但我没有找到任何理由。
答案 0 :(得分:16)
虽然Anony-Mousse有一些好处(聚类确实没有分类),但我认为分配新点的能力有其实用性。 *
基于关于DBSCAN的github.com/scikit-learn和robertlaytons想法的原始论文,我建议运行核心点并分配到您eps
内的第一个核心点的群集新观点。
然后,根据用于聚类的定义,保证您的点至少是指定群集的边界点。
(请注意,您的观点可能被视为噪音而未分配给群集)
我已经快速实施了:
import numpy as np
import scipy as sp
def dbscan_predict(dbscan_model, X_new, metric=sp.spatial.distance.cosine):
# Result is noise by default
y_new = np.ones(shape=len(X_new), dtype=int)*-1
# Iterate all input samples for a label
for j, x_new in enumerate(X_new):
# Find a core sample closer than EPS
for i, x_core in enumerate(dbscan_model.components_):
if metric(x_new, x_core) < dbscan_model.eps:
# Assign label of x_core to x_new
y_new[j] = dbscan_model.labels_[dbscan_model.core_sample_indices_[i]]
break
return y_new
通过聚类(dbscan_model = DBSCAN(...).fit(X)
获得的标签和从相同模型获得的标签在同一数据(dbscan_predict(dbscan_model, X)
)上有时会有所不同。我不太确定这是某个地方的错误还是随机性的结果。
编辑:我认为上述不同预测结果的问题可能源于边界点可能接近多个集群的可能性。如果你测试这个并找到答案,请更新。可以通过每次改组核心点或通过选择最接近而不是第一核心点来解决模糊性。
*)手头的案例:我想评估从我的数据子集中获得的集群是否对其他子集有意义,或者只是一个特例。 如果它概括,它支持集群的有效性和应用的预处理的早期步骤。
答案 1 :(得分:14)
群集不是分类。
群集未标记。如果你想把它挤进一个预测心态(这不是最好的主意),那么它基本上预测而不学习。因为没有可用于聚类的标记训练数据。它必须根据它看到的内容为数据构成新的标签。但是你不能在一个实例上做到这一点,你只能批量预测&#34;。
但scipys DBSCAN有问题:
random_state
:numpy.RandomState,可选:用于初始化中心的生成器。默认为numpy.random。
DBSCAN没有&#34;初始化中心&#34;,因为DBSCAN中没有中心。
几乎只有的聚类算法,您可以为旧聚类分配新点是k-means(及其多种变体)。因为它执行&#34; 1NN分类&#34;使用先前的迭代集群中心,然后更新中心。但大多数算法都不像k-means那样工作,所以你无法复制它。
R版本可能正在做的是使用1NN分类器进行预测;可能有额外的规则,即点被分配噪声标签,如果它们的1NN距离大于epsilon,则mabye也仅使用核心点。也许不是。
获取DBSCAN论文,它不讨论&#34;预测&#34; IIRC。
答案 2 :(得分:3)
这里的实现略有不同,效率更高。而且,不是采用eps半径内的第一个最佳核心点,而是采用最接近样本的核心点。
def dbscan_predict(model, X):
nr_samples = X.shape[0]
y_new = np.ones(shape=nr_samples, dtype=int) * -1
for i in range(nr_samples):
diff = model.components_ - X[i, :] # NumPy broadcasting
dist = np.linalg.norm(diff, axis=1) # Euclidean distance
shortest_dist_idx = np.argmin(dist)
if dist[shortest_dist_idx] < model.eps:
y_new[i] = model.labels_[model.core_sample_indices_[shortest_dist_idx]]
return y_new
答案 3 :(得分:1)
已经在此问题上发布了很好的答案。我的建议是尝试HDBSCAN。它提供了您可能需要的approximate_predict()
方法。
答案 4 :(得分:0)
尽管它不是完全相同的算法,但是您可以使用sklearn HDBSCAN对新点进行近似预测。参见here。
它是这样的:
clusterer = hdbscan.HDBSCAN(min_cluster_size=15, prediction_data=True).fit(data)
test_labels, strengths = hdbscan.approximate_predict(clusterer, test_points)