手写K-means及K-means++

手写K-means及K-means++经典算法及实战

前段时间在学校,看了一篇关于K-means-u的聚类论文,当时对聚类只是听过,但对许多经典算法和练习都不够,所以今天专门记录一下,当然也查阅了网上许多资料,如果本文哪有纰漏,欢迎各位的批评和建议

关于K-means和K-means++的算法流程,我这里就不细讲了,之前做过一个PPT,点击下方链接即可查看

https://slides.com/huozhang/clusting/fullscreen

K-means

# 手写k-means算法

# 导入必要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


def distance(dec1, dec2) -> float:
    return np.sqrt(np.sum(np.square(dec1 - dec2)))  # 计算欧式距离


def K_Means(data, k):
    K = np.random.uniform(0, 10, (k, data.shape[1]))  # 初始化中心点位矩阵
    ret = np.zeros([data.shape[0], data.shape[1]])  # 构造一个答案矩阵
    flag = True  # 定义标记变量
    count = 1
    while flag:

        flag = False
        for i in range(data.shape[0]):
            minIndex = -1  # 定义得到最短距离的时候的临时中心点位
            minDis = np.inf  # 定义最短距离
            for j in range(K.shape[0]):
                dis = distance(data[i], K[j])  # 计算距离
                if dis < minDis:
                    minDis = dis
                    minIndex = j

            # 计算完成后 将所属距离点位填入答案矩阵
            ret[i][0] = minDis
            ret[i][1] = minIndex

        # 计算好位置了,现在开始重新计算均值,查询中心点
        for i in range(k):
            cluster = data[ret[:, 1] == i]
            # print("中心点位——————", cluster)
            if len(cluster) == 0:
                pass
            else:
                center = np.mean(cluster, axis=0)
                # print("center 为", center)
                # print("K[i] 为", K[i])
                if (center == K[i]).all():  # 这里必须用all()因为 ndarry()的性质 具体大家可以百度
                    pass
                else:
                    K[i] = center
                    flag = True

        _X = K[:, 0]
        _Y = K[:, 1]
        plt.scatter(_x, _y)
        plt.scatter(_X, _Y, marker='X', color='r')
        plt.title("%d  of convergence" % count)
        count += 1
        plt.show()


if __name__ == '__main__':
    # 构造三簇数据
    data1 = np.random.uniform(0, 2, (10, 2))
    data2 = np.random.uniform(3, 6, (10, 2))
    data3 = np.random.uniform(8, 10, (10, 2))
    data = np.r_[data1, data2, data3]  # 按列上下合并数据  np.c_[]是按行 左右合并

    _x = data[:, 0]
    _y = data[:, 1]
    plt.rcParams['font.sans-serif'] = 'SimHei'
    # plt.scatter(_x, _y, marker="o")
    # plt.show()
    K_Means(data, 3)  # 自定义定义3个中心

代码进行了可视化展示,可以看看每次收敛效果


高维数据

import numpy as np


def distance(vecA, vecB):
    """数据节点的距离,欧氏距离"""
    dist = (vecA - vecB) * (vecA - vecB).T  # 比较大小,可以忽略开方
    return dist[0, 0]


def randCent(data, k):
    n = np.shape(data)[1]  # 属性的个数
    centroids = np.mat(np.zeros((k, n)))  # 初始化k个聚类中心
    for j in range(n):  # 初始化聚类中心每一维的坐标
        minJ = np.min(data[:, j])
        rangeJ = np.max(data[:, j]) - minJ
        # 在最大值和最小值之间随机初始化
        centroids[:, j] = minJ * np.mat(np.ones((k , 1))) + np.random.rand(k, 1) * rangeJ
    print('k-Means的随机初始化的聚类中心为\n', centroids)
    return centroids


def kmeans(data, k, centroids):
    m, n = np.shape(data)                 # m:样本的个数,n:特征的维度
    subCenter = np.mat(np.zeros((m, 2)))  # 初始化每一个样本所属的类别
    change = True                         # 判断是否需要重新计算聚类中心
    while change == True:
        change = False  # 重置

        # 1、 将样本点划分给离它最近的簇
        for i in range(m):
            minDist = np.inf          # 设置样本与聚类中心之间的最小的距离,初始值为正无穷
            minIndex = 0              # 所属的类别
            for j in range(k):
                # 计算i和每个聚类中心之间的距离
                dist = distance(data[i, ], centroids[j, ])
                if dist < minDist:
                    minDist = dist
                    minIndex = j
            # 判断是否需要改变
            if subCenter[i, 0] != minIndex:  # 需要改变
                change = True
                subCenter[i, ] = np.mat([minIndex,minDist])

        # 2、重新计算聚类中心
        for j in range(k):
            sum_all = np.mat(np.zeros((1, n)))
            r = 0  # 每个类别中的样本的个数
            for i in range(m):
                if subCenter[i, 0] == j:  # 计算第j个类别
                    sum_all += data[i, ]
                    r += 1
            for z in range(n):
                try:
                    centroids[j, z] = sum_all[0, z] / r
                except:
                    print (" r is zero")
    return subCenter, centroids





def main():
	# your data and number of K
	ctr = randCent(data,k)
	sub,cnt = kmeans(data,k,ctr)
    return


if __name__ == "__main__":
    main()


K-means++

主要解决K-means初始点位选择问题,k-means++即基本解决了这一问题,点位选择完成过后,进行相应的收敛和迭代,以减少迭代次数和SSE(最大平方误差和)

# 手写k-means++算法

# 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
import random
import math


def euler_distance(point1, point2) -> float:
    """
    计算两点之间的欧拉距离,支持多维
    """
    distance = 0.0
    for a, b in zip(point1, point2):  # 将x,y对应成元组进行计算
        distance += math.pow(a - b, 2)
    return math.sqrt(distance)


def distance(point, cluster) -> float:
    min_dist = math.inf
    for i, centroid in enumerate(cluster):
        dist = euler_distance(centroid, point)  # 计算每一个点到每个簇的距离
        if dist < min_dist:
            min_dist = dist
    return min_dist  # 返回离簇最近的距离


def KMeansplus(data, k) -> list:
    """
    K-means++ 主要解决K-means的初始点位选择问题,返回点位后,再进行收敛,此函数仅完成簇点选取
    :param data: 数据集(测试集)
    :param k: 簇个数
    :return: 返回簇x,y列表
    """
    cluster_list = []
    cluster_list.append(random.choice(data).tolist())
    ret = [0 for _ in range(len(data))]  # 构造距离空列表
    # print(ret)
    for _ in range(1, k):  # 这里从第二个点开始,因为第一个点是随机的
        sum_dis = 0
        for i, point in enumerate(data):
            ret[i] = distance(point, cluster_list)
            sum_dis += ret[i]  # 累加距离
        sum_dis *= random.random()  # 利用轮盘法*[0~1]里面的随机数
        for i, point_dis in enumerate(ret):
            sum_dis -= point_dis  # 依次减距离
            if sum_dis <= 0:
                cluster_list.append(data[i].tolist())  # 直到sum_dis为0时 将此点作为第二个点
                break

    return cluster_list  


if __name__ == '__main__':
    # 构造三簇数据
    data1 = np.random.uniform(0, 2, (20, 2))
    data2 = np.random.uniform(3, 6, (20, 2))
    data3 = np.random.uniform(8, 10, (20, 2))
    data = np.r_[data1, data2, data3]  # 按列上下合并数据  np.c_[]是按行 左右合并

    np.random.shuffle(data)

    _x = data[:, 0]
    _y = data[:, 1]
    plt.rcParams['font.sans-serif'] = 'SimHei'
    plt.scatter(_x, _y, marker="o")

    center = KMeansplus(data, 3)  # 自定义定义3个中心
    # print("k-means++ 的中心为\n", center)
    center = np.array(center)
    center_x = center[:, 0]
    center_y = center[:, 1]
    plt.scatter(center_x, center_y, marker='X', c='r')
    plt.show()
    

可以看见在点位选择上基本和以完成收敛的点位非常接近,之后再通过几次收敛达到最佳

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值