实现二分k-means算法

(1) 算法思路:
二分 k-means 算法,此算法不需要标签变量,在 k-means 算法的基础上需要通过四个特征变量将 Iris 进行聚类。目标:通过 Iris 的四个特征值进行聚类,得到每个聚类中的质心,并把聚类结果写入文件中。

(2) 算法原理基础:
在原理上跟 k-means 上差不多相同。

(3) 算法步骤:

把整个数据集看成一个簇,计算质心
将这个簇分成两个簇
选择满足条件的可以分解的簇,选择条件为簇元素的个数和 SSE 大小
使用 k-mean 算法将可分裂的簇分成两个簇
重复(2)(3)步,直到满足 k 值
(4)代码

from numpy import *
from matplotlib import pyplot as plt

def load_data_set(fileName):
    dataSet = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split(',')
        fltLine = list(map(float, curLine))#将列表里面的字符串变成float
        dataSet.append(fltLine)
    return dataSet

def distance_euclidean(vector1, vector2):
    return sqrt(sum(power(vector1-vector2, 2)))

def rand_center(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k, n)))
    # 遍历特征值
    for j in range(n):
        minJ = min(dataSet[:, j])
        # 计算每一列的范围值
        rangeJ = float(max(dataSet[:, j]) - minJ)
        centroids[:, j] = minJ + rangeJ * random.rand(k, 1)
    return centroids

def k_means(dataSet, k, distMeas = distance_euclidean, creatCent = rand_center):
    m = shape(dataSet)[0] # 行数
    # 建立簇分配结果矩阵,第一列存放该数据所属中心点,第二列是该数据到中心点的距离
    clusterAssment = mat(zeros((m, 2)))
    centroids = creatCent(dataSet, k)  # 质心,即聚类点
    # 用来判定聚类是否收敛
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):  # 把每一个数据划分到离他最近的中心点
            minDist = inf  # 无穷大
            minIndex = -1  #初始化
            for j in range(k):
                # 计算各点与新的聚类中心的距离
                distJI = distMeas(centroids[j,:],dataSet[i,:])
                if distJI < minDist:
                    # 如果第i个数据点到第j中心点更近,则将i归属为j
                    minDist = distJI
                    minIndex = j
            # 如果分配发生变化,则需要继续迭代
            if clusterAssment[i, 0] != minIndex:
                clusterChanged = True
            # 并将第i个数据点的分配情况存入字典
            clusterAssment[i, :] = minIndex, minDist**2
        for cent in range(k):  # 重新计算中心点
            # 去第一列等于cent的所有列
            ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]]
            # 算出这些数据的中心点
            centroids[cent, :] = mean(ptsInClust, axis=0)
    return centroids, clusterAssment

def biKmeans(dataMat, k, distMeas=distance_euclidean):
    """二分k-means算法"""
    m = shape(dataMat)[0] #获得数据集的样本数
    clusterAssment = mat(zeros((m, 2))) #初始化一个元素全为0的(m,2)的矩阵
    centroid0 = mean(dataMat, axis=0).tolist()[0] #获取数据每一列的均值,组成一个一维数组
    centList = [centroid0] # 用一个列表来保留所有的质心
    for j in range(m): #遍历数据中的每个数据集样本
        # 计算当前聚类为一类时各个数据点距离质心的平方距离
        clusterAssment[j, 1] = distMeas(mat(centroid0), dataMat[j, :]) ** 2
    while (len(centList) < k):#循环,直到达到k类
        lowestSSE = inf #将当前最小误差设置为正无穷大
        for i in range(len(centList)):#遍历每个聚类
            # 因此首先先比较clusterAssment[:,0].A==cent的真假,如果为真则记录了他所在的行,因此在用切片进行取值
            ptsInCurrCluster = dataMat[nonzero(clusterAssment[:, 0].A == i)[0], :]
            # 对该类利用二分k-均值算法进行划分,返回划分后结果,及误差
            centroidMat, splitClustAss = k_means(ptsInCurrCluster, 2, distMeas)
            # 划分数据的SSE与未划分的之和作为本次划分的总误差
            sseSplit = sum(splitClustAss[:, 1])   #计算该划分后两个类的误差平方和
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1]) #计算数据集中不属于该类的数据的误差平方和
            #划分第i类后总误差小于当前最小总误差
            if (sseSplit + sseNotSplit) < lowestSSE:
                bestCentToSplit = i  # 第i类作为本次划分类
                bestNewCents = centroidMat   # 第i类划分后得到的两个质心向量
                bestClustAss = splitClustAss.copy()   # 复制第i类中数据点的聚类结果即误差值
                lowestSSE = sseSplit + sseNotSplit # 将划分第i类后的总误差作为当前最小误差
        # 数组过滤筛选出本次2-均值聚类划分后类编号为1数据点,将这些数据点类编号变为1
        # 当前类个数+1,作为新的一个聚类
        bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
        # 同理,将划分数据集中类编号为0的数据点的类编号仍置为被划分的类编号,使类编号
        # 连续不出现空缺
        bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
        #  更新质心列表中的变化后的质心向量
        centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0]
        # 添加新的类的质心向量
        centList.append(bestNewCents[1, :].tolist()[0])
        # 重新分配最好簇下的数据(质心)以及SSE
        clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
        # 返回聚类结果
    return mat(centList), clusterAssment

# 测试
datMat = mat(load_data_set('testSet.txt'))
centList, clusterAssment = biKmeans(datMat, 4)
print("质心结果:", centList)
print("聚类结果:", clusterAssment)
# 可视化
plt.scatter(array(datMat)[:, 0], array(datMat)[:, 1], c=array(clusterAssment)[:, 0].T)
plt.scatter(centList[:, 0].tolist(), centList[:, 1].tolist(), c="r")
plt.show()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要变胖哇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值