我正在使用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方法有一个“predict”函数,但我希望能够对DBSCAN进行同样的操作。大概是这样的:
dbscan.predict(X, Y)
因此,密度可以从X推断出来,但返回值(集群分配/标签)仅适用于Y。据我所知,这种功能在R中可用,因此我假设它在Python中也可以使用。我就是找不到这方面的任何文档。
此外,我还试图寻找DBSCAN不能用于标记新数据的原因,但我没有找到任何理由。
虽然Anony Mouse有一些优点(集群确实不是分类),但我认为分配新点的能力很有用*
基于DBSCAN和robertlaytons关于github的原始论文。com/scikit学习,我建议运行核心点并将第一个核心点分配到新点的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(...). fi(X)
和在相同数据上从相同模型获得的标签(dbscan_predict(dbscan_model,X)
)有时会有所不同。我不太确定这是某个地方的错误还是随机性的结果。
编辑:我认为上述不同预测结果的问题可能源于一个边界点可能靠近多个集群的可能性。如果您测试并找到答案,请更新。含糊不清可以通过每次洗牌核心点或选择最接近的而不是第一个核心点来解决。
*)手头的案例:我想评估从我的数据子集获得的集群对其他子集是否有意义,或者只是一个特例。如果它泛化,它支持群集的有效性和应用的预处理的早期步骤。
聚类不是分类。
聚类是未标记的。如果你想把它压缩成一种预测思维(这不是最好的主意),那么它基本上是在不学习的情况下预测的。因为没有可用于聚类的标记训练数据。它必须根据看到的内容为数据添加新标签。但你不能在一个实例上做到这一点,你只能“批量预测”。
但scipys DBSCAN有点问题:
随机状态
:numpy。随机状态,可选:
用于初始化中心的生成器。默认numpy.random.
DBSCAN不会“初始化中心”,因为DBSCAN中没有中心。
几乎唯一的聚类算法,你可以分配新的点到旧的聚类是k-均值(和它的许多变化)。因为它使用先前的迭代群集中心执行“1NN分类”,然后更新中心。但是大多数算法并不像k-均值那样工作,所以你不能复制它。
R版本可能正在做的是,使用1NN分类器进行预测;如果点的1NN距离大于epsilon,则可能需要额外的规则,即为点指定噪声标签,mabye也只使用核心点。也许不是。
获取DBSCAN论文,它不讨论“预测”IIRC。
这里是一个稍有不同且更高效的实现。此外,不是获取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