我的朋友最近在搞大数据研究,但是他本人又懒得写代码,于是找人帮他用DBSCAN聚类一些数据,但是啊。这些聚类效果,不是很满意。或者说,它希望能对DBSCAN进行一些改进,于是就找到了我。可我毕竟不是个大佬,只能瞎改。
虽然自己没学过python,可是毕竟要恰饭的。只能硬着头皮去研究。于是我打开DBSCAN的源码,因为你要对一个东西进行改进,必须先理解它。
def dbscan(X, eps=0.5, min_samples=5, metric='minkowski', metric_params=None,
algorithm='auto', leaf_size=30, p=2, sample_weight=None,
n_jobs=None):
这个时候我们就看到了大量的参数,于是就有必要搞懂这些参数,在这里我翻阅了官方文档。但是很多用使用DBSCAN只使用了eps和min_sample两个参数。但是我阅读源码的时候发现sample_weight,于是相对这个参数进行一些小尝试。
if not eps > 0.0:
raise ValueError("eps must be positive.")
这句没的说,要保证eps大于0
X = check_array(X, accept_sparse='csr')
if sample_weight is not None:
sample_weight = np.asarray(sample_weight)
check_consistent_length(X, sample_weight)
首先对X验证和转换(除合法性校验以外,其并没有对特征和目标值进行任何处理)
然后就是假如sample_weight非空,那么就把现有的sample_weight转换为np.asarray,同时检验X和sample_weight第一维是否一致。检查数组中的所有对象是否具有相同的形状或长度
if metric == 'precomputed' and sparse.issparse(X):
neighborhoods = np.empty(X.shape[0], dtype=object)
X.sum_duplicates() # XXX: modifies X's internals in-place
# set the diagonal to explicit values, as a point is its own neighbor
with ignore_warnings():
X.setdiag(X.diagonal()) # XXX: modifies X's internals in-place
X_mask = X.data <= eps
masked_indices = X.indices.astype(np.intp, copy=False)[X_mask]
masked_indptr = np.concatenate(([0], np.cumsum(X_mask)))
masked_indptr = masked_indptr[X.indptr[1:-1]]
# split into rows
neighborhoods[:] = np.split(masked_indices, masked_indptr)
else:
neighbors_model = NearestNeighbors(radius=eps, algorithm=algorithm,
leaf_size=leaf_size,
metric=metric,
metric_params=metric_params, p=p,
n_jobs=n_jobs)
neighbors_model.fit(X)
# This has worst case O(n^2) memory complexity
neighborhoods = neighbors_model.radius_neighbors(X, eps,
return_distance=False)
首先我们不管if里面的,那个是对稀疏矩阵(X)的一些处理,但是一般数据集都不是稀疏矩阵,所以击中精力研究else里面的。
首先创建了一个名neighbors_model的NearestNeighbors 执行无监督的最近邻方法
说起这个NearestNeighbors那又是另外一回事了。我们今天的重点是sample_weight,所以没必要在追溯下去了。然后以X作为训练数据拟合模型。
然后使用neighbors_model的radius_neighbors
在一个或多个点的给定半径内找到相邻点。返回数据集中每个点的索引和距离在一个球的大小’ ’ ‘半径’ '周围的查询点数组中。边界上的点包括在结果中。结果点并不一定按到它们的距离排序查询点。
if sample_weight is None:
n_neighbors = np.array([len(neighbors)
for neighbors in neighborhoods])
else:
n_neighbors = np.array([np.sum(sample_weight[neighbors])
for neighbors in neighborhoods])
对sample_weight的处理,如果sample_weight是没有的,就把
neighborhoods的长度作为sample_weight
否则就按照neighborhoods的索引将sample_weight加和作为新的sample_weight。
labels = np.full(X.shape[0], -1, dtype=np.intp)
labels填满了-1(-1代表噪声点)
core_samples = np.asarray(n_neighbors >= min_samples, dtype=np.uint8)
dbscan_inner(core_samples, neighborhoods, labels)
return np.where(core_samples)[0], labels
返回结果