作业一:DBSCAN算法的实现

一、作业内容:

在二维空间内随机创建500个数据对象构建数据集D,每个维度的取值范围为[0,10],设置三组不同的的(Eps,Minpts)值,使用DBSCAN算法对数据集D进行聚类分析。
作业要求:输出原始数据对象分布和聚类分布。可参考下图,不同聚类可用不同符号标注,如+,○,□,△等。

二、DBSCAN算法

1.主要概念

Ε邻域:给定对象半径为Ε内的区域称为该对象的Ε邻域;
核心对象:如果给定对象Ε领域内的样本点数大于等于MinPts,则称该对象为核心对象;
直接密度可达:对于样本集合D,如果样本点q在p的Ε领域内,并且p为核心对象,那么对象q从对象p直接密度可达。
密度可达:对于样本集合D,给定一串样本点p1,p2….pn,p= p1,q= pn,假如对象pi从pi-1直接密度可达,那么对象q从对象p密度可达。
密度相连:存在样本集合D中的一点o,如果对象o到对象p和对象q都是密度可达的,那么p和q密度相联。

2.算法执行步骤

(1)检测数据库中尚未检查过的对象p,如果p未被处理(归为某个簇或者标记为噪声),则检查其邻域,若包含的对象数不小于minPts,建立新簇C,将其中的所有点加入候选集N;
(2)对候选集N 中所有尚未被处理的对象q,检查其邻域,若至少包含minPts个对象,则将这些对象加入N;如果q 未归入任何一个簇,则将q 加入C;
(3)重复步骤2),继续检查N 中未处理的对象,当前候选集N为空;
(4)重复步骤1)~3),直到所有对象都归入了某个簇或标记为噪声。

3.伪代码描述如下:

输入:数据对象集合D,半径Eps,密度阈值MinPts
输出:聚类C
DBSCAN(D, Eps, MinPts)
Begin
init C=0; //初始化簇的个数为0
for each unvisited point p in D
mark p as visited; //将p标记为已访问
N = getNeighbours (p, Eps);
if sizeOf(N) < MinPts then
mark p as Noise; //如果满足sizeOf(N) < MinPts,则将p标记为噪声
else
C= next cluster; //建立新簇C
ExpandCluster (p, N, C, Eps, MinPts);
end if
end for
End

其中ExpandCluster算法伪码如下:

ExpandCluster(p, N, C, Eps, MinPts)
add p to cluster C; //首先将核心点加入C
for each point p’ in N
mark p' as visited;
N’ = getNeighbours (p’, Eps); //对N邻域内的所有点在进行半径检查
if sizeOf(N’) >= MinPts then
N = N+N’; //如果大于MinPts,就扩展N的数目
end if
if p’ is not member of any cluster
add p’ to cluster C; //将p' 加入簇C
end if
end for
End ExpandCluster

《数据分析方法》

作业一:DBSCAN算法的实现
学号: 1814010130
班级: 软件18-1
姓名: 张立辉

作业一:DBSCAN算法的实现

一、程序运行结果

1.原始数据集数据分布如图1所示。

图1 原始数据集数据分布图

2.三组不同Eps,Minpts值下聚类结果。

(1)Eps=0.3,Minpts=10时,聚类结果如图2所示。
图2 Eps=0.3,Minpts=10时,聚类结果图

(2)Eps=0.3,Minpts=1时,聚类结果如图3所示。
图3 Eps=0.3,Minpts=1时,聚类结果图

(3)Eps=0.03,Minpts=10时,聚类结果如图4所示。
图4 Eps=0.03,Minpts=10时,聚类结果图

二、聚类结果分析
1.随m值增大时,
聚类结果较差

2.随n值增大时,
要求较大的内存支持,I/0消耗也很大

三、程序主要代码

from sklearn import datasets
import numpy as np
import random
import matplotlib.pyplot as plt
import time
import copy


def find_neighbor(j, x, eps):
    N = list()
    for i in range(x.shape[0]):
        temp = np.sqrt(np.sum(np.square(x[j] - x[i])))  # 计算欧式距离
        if temp <= eps:
            N.append(i)
    return set(N)


def DBSCAN(X, eps, min_Pts):
    k = -1
    neighbor_list = []  # 用来保存每个数据的邻域
    omega_list = []  # 核心对象集合
    gama = set([x for x in range(len(X))])  # 初始时将所有点标记为未访问
    cluster = [-1 for _ in range(len(X))]  # 聚类
    marker = ["s" for _ in range(len(X))]
    for i in range(len(X)):
        neighbor_list.append(find_neighbor(i, X, eps))
        if len(neighbor_list[-1]) >= min_Pts:
            omega_list.append(i)  # 将样本加入核心对象集合
    omega_list = set(omega_list)  # 转化为集合便于操作
    while len(omega_list) > 0:
        gama_old = copy.deepcopy(gama)
        j = random.choice(list(omega_list))  # 随机选取一个核心对象
        k = k + 1
        Q = list()
        Q.append(j)
        gama.remove(j)
        while len(Q) > 0:
            q = Q[0]
            Q.remove(q)
            if len(neighbor_list[q]) >= min_Pts:
                delta = neighbor_list[q] & gama
                deltalist = list(delta)
                for i in range(len(delta)):
                    Q.append(deltalist[i])
                    gama = gama - delta
        Ck = gama_old - gama
        Cklist = list(Ck)
        for i in range(len(Ck)):
            cluster[Cklist[i]] = k
            marker[Cklist[i]] = "o"
        omega_list = omega_list - Ck
    return cluster, marker


centers = [[5, 5], [7, 6], [7, 3]]
# 产生的数据个数
n_samples = 500
# 生产数据:此实验结果受cluster_std的影响,或者说受eps 和cluster_std差值影响
X, lables_true = datasets.make_blobs(n_samples=n_samples, centers=centers, cluster_std=0.4,
                                     random_state=0, center_box=(0, 10))
# n_samples(int/array):如果参数为int,代表总样本数;如果参数为array-like,数组中的每个数代表每一簇的样本数。
# n_features(int):样本点的维度。
# centers(int):样本中心数。如果样本数为int且centers=None,生成三个样本中心;如果样本数(n_samples)为数组,则centers 要么为None,要么为数组的长度。
# cluster_std(float/sequence of floats):样本中,簇的标准差。
# center_box(pair of floats (min, max)):每个簇的上下限。
# shuffle(boolean):是否将样本打乱。
# random_state(int/RandomState instance /None):指定随机数种子,每个种子生成的序列相同,与minecraft地图种子同理。

plt.figure()
plt.scatter(X[:, 0], X[:, 1])
plt.show()
X = np.array(X)
eps = 0.3
min_Pts = 10
begin = time.time()
C, k = DBSCAN(X, eps, min_Pts)
end = time.time()
plt.figure()
plt.scatter(X[:, 0], X[:, 1], c=C)
plt.show()

附:MATLAB代码

clc;
clear all;
close all;
x=0+10*rand(500,2);
y=0+10*rand(1,500);
data1=x;
% 显示数据
plot(data1(:,1),data1(:,2),'bo');
hold on;
grid on;
data=data1; 
N=3;%设置聚类数目
[m,n]=size(data);
pattern=zeros(m,n+1);
center=zeros(N,n);%初始化聚类中心
pattern(:,1:n)=data(:,:);
for x=1:N
    center(x,:)=data( randi(500,1),:);%第一次随机产生聚类中心
end
while 1
distence=zeros(1,N);
num=zeros(1,N);
new_center=zeros(N,n);
 
for x=1:m
    for y=1:N
    distence(y)=norm(data(x,:)-center(y,:));%计算到每个类的距离
    end
    [~, temp]=min(distence);%求最小的距离
    pattern(x,n+1)=temp;         
end
k=0;
for y=1:N
    for x=1:m
        if pattern(x,n+1)==y
           new_center(y,:)=new_center(y,:)+pattern(x,1:n);
           num(y)=num(y)+1;
        end
    end
    new_center(y,:)=new_center(y,:)/num(y);
    if norm(new_center(y,:)-center(y,:))<0.10
        k=k+1;
    end
end
if k==N
     break;
else
     center=new_center;
end
end
[m, n]=size(pattern);
 
%最后显示聚类后的数据
figure;
hold on;
for i=1:m
    if pattern(i,n)==1 
         plot(pattern(i,1),pattern(i,2),'r*');
         plot(center(1,1),center(1,2),'ko');
    elseif pattern(i,n)==2
         plot(pattern(i,1),pattern(i,2),'g+');
         plot(center(2,1),center(2,2),'ko');
    elseif pattern(i,n)==3
         plot(pattern(i,1),pattern(i,2),'bs');
         plot(center(3,1),center(3,2),'ko');
    elseif pattern(i,n)==4
         plot(pattern(i,1),pattern(i,2),'yo');
         plot(center(4,1),center(4,2),'ko');
    else
         plot(pattern(i,1),pattern(i,2),'m.');
         plot(center(4,1),center(4,2),'ko');
    end
end
grid on;

运行结果

最后修改:2021 年 05 月 11 日
如果觉得我的文章对你有用,请随意赞赏