DBSCAN
算法步骤
-
设置每个对象为未访问
-
随机选择一个未访问的点ppp,标记ppp表示访问
-
如果p的半径为nnn的邻域中至少存在
MinPts
个对象- 我们就创建一个新的簇,并将ppp加入ccc
- 设
N
是ppp邻域中对象的集合 - 对在NNN中的每个点p′p'p′
- 如果p′p'p′是未访问的
- 标记p′p'p′为访问节点
- 如果这个p′p'p′也是核心节点,那么将它范围内的点加入到NNN
- 如果p′p'p′还不是任何簇中的成员,我们就把p′p'p′加入ccc
- 如果p′p'p′是未访问的
- 输出簇ccc
-
否则,标记ppp为噪声
-
重复以上直到每个点被访问
实现
def DBSCAN(poi,radius=1,MinPts=5):
def edis(x,y):
return math.sqrt(sum((x[i]-y[i])**2 for i in range(len(x))))
unvisit=set([i for i in range(len(poi))]) # 创建访问表
Clusters=[-1]*len(poi) # 一个hash表,判断哪个点对应哪个簇
c=0
while len(unvisit):
# 随机选一个中心点
p=random.choice(list(unvisit))
unvisit.remove(p)
# 邻域展开!
neighbor=set()
for id,pn in enumerate(poi):
if edis(poi[p],pn)<=radius:
neighbor.add(id)
if len(neighbor)>=MinPts:
# 创建一个新的簇
Clusters[p]=c
# 遍历邻域点
while neighbor:
id=neighbor.pop()
if id in unvisit:
unvisit.remove(id)
# 找他的邻域
n=set()
for Id,pi in enumerate(poi):
if edis(poi[id],pi)<radius:
n.add(Id)
if len(n)>=MinPts:
# 合并
neighbor|=n
# 如果这个点不属于任何簇,将其加入c
if Clusters[id]==-1:
Clusters[id]=c
# 否则,我们认为p是一个噪声(-1)
c+=1
return Clusters
print(DBSCAN(points))
结果展示
嗯,离群点被标记为了-1
如何选择距离也会对结果产生很大的影响。