聚簇算法KMM,Kmedoids,DBScan

该博客探讨了KMM和Kmedoids两种聚类算法在处理数据时对离群点的敏感性,发现KMM受到离群点较大影响,而Kmedoids则较小。同时介绍了DBSCAN密度聚类算法,通过随机生成的数据展示了其在聚类过程中的效果。
摘要由CSDN通过智能技术生成
初始化数据:
def compare(a):	# 用于排序
    return a[0]

def distance(d, p):	# 计算两向量的距离
    res = 0.0
    for i in range(len(d)):
        res += pow(d[i] - p[i], 2)
    return pow(res, 0.5)
    
def initData_normal(num, u, x):
    """
    以均值:x,协方差:u的正态分布生成num个数据
    :param num: 数据数量
    :param u: 正态分布协方差
    :param x: 正态分布均值
    :return:
    """
    data_label.append(np.random.multivariate_normal(u, x, num))
    data.extend(data_label[-1])

def initData_circular(num, f):
    X, Y = make_circles(n_samples=num, noise=0.1, factor=f)
    X1 = []
    X2 = []
    for i in range(len(Y)):
        if Y[i] == 0:
            X1.append(X[i])
        else:
            X2.append(X[i])
    data_label.append(X1)
    data_label.append(X2)
    data.extend(X)

KMM / Kmedoids:

class KMM:
    def __init__(self, k, d):
        """
        :param k: 中心点个数
        :param d: 输入数据
        """
        self.p = [[] for _ in range(k)]
        self.d = d
        self.dividual = [[] for _ in range(k)]  # k个list,对每个中心点,记录归于该中心点的数据下标

    def div(self):  # 对数据进行分类
        self.dividual = [[] for _ in range(len(self.dividual))]
        for i in range(len(self.d)):    # 遍历每个数据
            index = -1
            min_dis = float('inf')
            for j in range(len(self.p)):    # 对每个数据遍历所有中心点
                dis = distance(self.d[i], self.p[j])
                if dis < min_dis:  # 记录离该数据最近的中心的
                    min_dis = dis
                    index = j
            if index != -1:
                self.dividual[index].append(i)  # 将该数据归于该中心点

    def move(self):
        """
        用于KMM
        :return: 相对上次迭代,中心的位置是否发生变化?
        """
        temp = self.p.copy()
        self.p = [[0, 0] for _ in range(len(self.p))]
        for i in range(len(self.p)):    # 对归属于同一中心点的数据,计算其平均坐标值
            for j in self.dividual[i]:
                self.p[i][0] += self.d[j][0] / len(self.dividual[i])
                self.p[i][1] += self.d[j][1] / len(self.dividual[i])

        for i in range(len(self.p)):
            for j in range(len(self.p[i])):
                if temp[i][j] != self.p[i][j]:
                    return True
        return False

    def move_d(self):
        """
        用于Kmedoids
        :return:
        """
        temp = self.p.copy()
        self.p = [[0, 0] for _ in range(len(self.p))]
        for i in range(len(self.p)):
            td = []
            for j in self.dividual[i]:
                td.append([distance(self.d[j], self.p[i]), j])
            td.sort(key=compare)
            if len(td) > 0:
                self.p[i] = self.d[td[int(len(td)/2)][1]].copy()

        for i in range(len(self.p)):
            for j in range(len(self.p[i])):
                if temp[i][j] != self.p[i][j]:
                    return True
        return False

    def show(self):
        dl = [[] for _ in range(len(self.dividual))]
        for i in range(len(self.dividual)):
            for j in self.dividual[i]:
                dl[i].append(self.d[j])
        for i in range(len(dl)):
            plt.plot(*np.array(dl[i]).T, '.', label='s' + str(i))
        plt.plot(*np.array(self.p).T, '*', label='p')
        print(self.p)
        plt.axis('scaled')
        plt.legend()
        plt.show()

    def run(self, m_d, max_t=20):
        """
        :param m_d: 选择kmm('m')/kmedoids('d')
        :param max_t: 最大迭代次数
        """
        self.p = [[] for _ in range(len(self.p))]
        for i in range(len(self.p)):  # 随机生成中心点坐标
            index = random.randint(0, len(data)-1)
            self.p[i].extend([data[index][0], data[index][1]])

        flag = True
        if m_d == 'd':
            while flag and max_t > 0:
                self.div()
                flag = self.move_d()
                max_t -= 1
        else:
            while flag and max_t > 0:
                self.div()
                flag = self.move()
                max_t -= 1
        self.show()

main:

if __name__ == '__main__':
    # initData_circular(1000, 0.1)
    initData_normal(50, [5, 5], [[1, 0], [0, 1]])   # 随机生成50个符合均值为[5,5], 方差为[[1,0],[0,1]]的正态分布的数据
    initData_normal(50, [5, -5], [[1, 0], [0, 1]])
    initData_normal(50, [-5, 5], [[1, 0], [0, 1]])
    initData_normal(50, [-5, -5], [[1, 0], [0, 1]])
    initData_normal(5, [-50, -50], [[1, 0], [0, 1]])    # 设置离群点
    initData_normal(5, [-30, -30], [[1, 0], [0, 1]])    # 设置离群点
    kmm = KMM(4, data)
    kmm.run('m')
    kmm.run('d')
结果:
KMM

受离群点影响较大 在这里插入图片描述

Kmedoids:

受离群点影响较小在这里插入图片描述

DBScan:

class DBScan:
    def __init__(self, th, core_num, d):
        """
        :param th: 核心圆的半径
        :param core_num: 核心对象圆内最少数目
        :param d: 数据
        """
        self.th = th
        self.core_num = core_num
        self.d = d
        # 初始化每个对象的归属为-1
        self.classes = [-1] * len(d)
        self.clusters = []
        self.sum = len(d)

    def spread(self):
        sum = self.sum
        while sum != 0:     # 循环直到所有数据被归类
            # index:随机选择一个未被归类的数据对象的下标
            index = random.randint(0, len(self.d) - 1)
            while self.classes[index] != -1:
                index = (index + 1) % len(self.d)

            # 将index下标的数据作为一个簇
            self.classes[index] = len(self.clusters)
            self.clusters.append([index])
            sum -= 1

            for i in self.clusters[-1]:     # 循环迭代该簇中数据的下标
                for j in range(len(self.d)):    # 循环迭代所有数据的下标
                    # 某下标j的数据未被归类,且与核心对象距离<小于th:将其归入该核心对象的同类
                    if self.classes[j] == -1 and distance(self.d[i], self.d[j]) <= self.th:
                        self.classes[j] = self.classes[index]
                        self.clusters[-1].append(j)
                        sum -= 1

    def show(self):
        temp = []
        for c in self.clusters:
            temp.append([])
            for i in c:
                temp[-1].append(self.d[i])
            temp[-1] = np.array(temp[-1])
        show(np.array(temp))

main:

if __name__ == '__main__':
    initData_circular(1000, 0.1)    # 初始化数据
    dbscan = DBScan(0.2, 10, data)
    dbscan.spread()
    dbscan.show()

结果

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值