DBSCAN
算法步骤
-
设置每个对象为未访问
-
随机选择一个未访问的点 p p p,标记 p p p表示访问
-
如果p的半径为 n n n的邻域中至少存在
MinPts
个对象- 我们就创建一个新的簇,并将 p p p加入 c c c
- 设
N
是 p p p邻域中对象的集合 - 对在
N
N
N中的每个点
p
′
p'
p′
- 如果
p
′
p'
p′是未访问的
- 标记 p ′ p' p′为访问节点
- 如果这个 p ′ p' p′也是核心节点,那么将它范围内的点加入到 N N N
- 如果 p ′ p' p′还不是任何簇中的成员,我们就把 p ′ p' p′加入 c c c
- 如果
p
′
p'
p′是未访问的
- 输出簇 c c c
-
否则,标记 p p p为噪声
-
重复以上直到每个点被访问
实现
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
如何选择距离也会对结果产生很大的影响。