K-means聚类的Python实现

本文详细介绍了K-means聚类算法的思路,并提供了完整的Python代码实现,包括数据读取、规范化、距离计算、K-means算法核心部分及结果可视化。通过实例展示了如何利用K-means对数据进行聚类,并通过类内差异和类间差异之比评估聚类质量。算法对初始值敏感,适合处理大数据集和密集簇,但不适用于非凸形状簇和噪声数据。
摘要由CSDN通过智能技术生成


前言

  k均值聚类算法(k-means clustering algorithm)是一种迭代求解的聚类分析算法,其步骤是,预将数据分为K组,则随机选取K个对象作为初始的聚类中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。每分配一个样本,聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小。

一、K-means的算法思路

  • 任意选k个样本作为初始的簇中心。
  • 分别计算每个样本到这k个簇中心的距离,将该样本划分到距离最近的簇中心属于的那个簇。
  • 所有样本分类完毕后, 如分类结果与上次相同,则聚类结束。
  • 若分类结果与上次不同,取每个簇的各属性平均值对应的点为该簇新的簇中心,重复上述操作。

二、代码实现

程序主要有6个部分:

  • read_xslx(xslx_path)   #读取excel文件
  • standardization(v,mu,sigma)   #对一个数据规范化
  • standardize(data)   #对数据集规范化
  • calDistance(testingrow, trainingrow)   #计算样本间距离
  • Kmeans(dataset, k, centers, label)   #K-means算法部分
  • showData(dataSet)  #聚类结果可视化

1. 读取excel文件

#读取excel文件
####################################################################################################################
def read_xslx(xslx_path):

    dataSet = []                           # 先声明一个空list
    data = xlrd.open_workbook(xslx_path)   # 读取文件
    table = data.sheet_by_index(0)         # 按索引获取工作表,0就是工作表1

    for i in range(table.nrows):           # table.nrows表示总行数
        line = table.row_values(i)         # 读取每行数据,保存在line里面,line是list
        dataSet.append(line)               # 将line加入到data中,data是二维list
    dataSet = np.array(dataSet)            # 将data从二维list变成数组

    return dataSet
####################################################################################################################

2.对一个数据规范化

#规范化处理函数
#####################################################################################
def standardization(v,mu,sigma):
    v = (v - mu)/sigma
    return v
#####################################################################################

3. 对数据集规范化

#规范化数据
####################################################################################################################
def standardize(data):

    numTotal = data.shape[0]  # 记录测试数据集总条数
    attr1 = data[:, 0].astype(float)              #标准化过程
    attr2 = data[:, 1].astype(float)
    attr3 = data[:, 2].astype(float)
    attr4 = data[:, 3].astype(float)
    attr5 = data[:, 4].astype(float)
    attr6 = data[:, 5].astype(float)
    Data = data

    mu1 = np.mean(attr1)
    sigma1 = np.std(attr1)
    for i in range(0,numTotal):
        Data[:, 0][i] =  standardization(attr1[i],mu1,sigma1)

    mu2 = np.mean(attr2)
    sigma2 = np.std(attr2)
    for i in range(0,numTotal):
        Data[:, 1][i] =  standardization(attr2[i],mu2,sigma2)

    mu3 = np.mean(attr3)
    sigma3 = np.std(attr3)
    for i in range(0,numTotal):
        Data[:, 2][i] =  standardization(attr3[i],mu3,sigma3)

    mu4 = np.mean(attr4)
    sigma4 = np.std(attr4)
    for i in range(0,numTotal):
        Data[:, 3][i] =  standardization(attr4[i],mu4,sigma4)

    mu5 = np.mean(attr5)
    sigma5 = np.std(attr5)
    for i in range(0,numTotal):
        Data[:, 4][i] =  standardization(attr5[i],mu5,sigma5)

    mu6 = np.mean(attr6)
    sigma6 = np.std(attr6)
    for i in range(0,numTotal):
        Data[:, 5][i] =  standardization(attr6[i],mu6,sigma6)

    return Data
####################################################################################################################

4. 计算样本间距离

#计算距离
####################################################################################################################
def calDistance(testingrow, trainingrow):
    dist = ((float(testingrow[0]) - float(trainingrow[0])) ** 2 + (float(testingrow[1]) - float(trainingrow[1])) ** 2 \
            + (float(testingrow[2]) - float(trainingrow[2])) ** 2 + (float(testingrow[3]) - float(trainingrow[3])) ** 2\
            + (float(testingrow[4]) - float(trainingrow[4])) ** 2 + (float(testingrow[5]) - float(trainingrow[5])) ** 2) ** 0.5
    return dist
####################################################################################################################

5. K-means算法部分

#K-means
####################################################################################################################
def Kmeans(dataset, k, centers, label):
    within = 0            #类内差异
    between = 0            #类间差异
    numTotal = dataset.shape[0]            # 记录测试数据集总条数
    attrSum = np.zeros((k,6))
    SampleNum = np.linspace(0,0,k)
    for i in range(0, numTotal):           # 遍历每个样本
        distance = []                      # 记录该样本和各簇中心的距离
        for j in range(0, k):
            distance.append(calDistance(dataset[i], centers[j]))  # 计算距离
        pos = distance.index(min(distance))
        within += (min(distance))**2       #计算类内差异
        dataset[i][-1] = centers[pos][-1]   # 将该样本标记为与其距离最近的簇中心的标签
        attrSum[pos] = attrSum[pos] + dataset[i][:-1].astype(float)     # 累加该簇的各属性值,为之后求平均做准备
        SampleNum[pos] = SampleNum[pos] + 1           #记录各簇的样本个数,为之后求平均做准备

    for i in range(0, k):                #计算类间差异
        for j in range(i, k):
            between += (calDistance(centers[i], centers[j]))**2

    if((label == dataset[:,-1].astype(float)).all()):    # 判断标签有无变化,以判断聚类是否已收敛
        return dataset, within, between
    else:
        next_label = dataset[:,-1].astype(float)        # 新的标签
        for i in range(0, k):
            for j in range(0, 6):
                centers[i][j] = attrSum[i][j]/SampleNum[i]     #新簇中心的属性值
            centers[i][6] = i                      #新簇中心的标签
        next_centers = centers
        return Kmeans(dataset, k, next_centers, next_label)   #新的一次迭代
####################################################################################################################

6. 聚类结果可视化

#聚类结果可视化
####################################################################################################################
def showData(dataSet):
    label_pred = dataSet[:,-1]
    mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
    # 这里'or'代表中的'o'代表画圈,'r'代表颜色为红色,后面的依次类推
    j = 0
    for i in label_pred:
        plt.plot([float(dataSet[j][0])], [float(dataSet[j][3])], mark[int(i)], markersize=4)
        j += 1
    plt.show()
####################################################################################################################

完整代码

# -*- coding: utf-8 -*-     支持文件中出现中文字符
#########################################################################

"""
Created on Mon Nov 23 19:28:00 2020

@author: ixobgnew

代码功能描述:(1)读取xlsx文件
            (2)数据规范化处理
            (3)计算数据欧氏距离
            (4)K-means聚类算法
            (5)聚类结果可视化

"""
#####################################################################

import xlrd
import numpy as np
import random
import matplotlib.pyplot as plt

#读取excel文件
####################################################################################################################
def read_xslx(xslx_path):

    dataSet = []                           # 先声明一个空list
    data = xlrd.open_workbook(xslx_path)   # 读取文件
    table = data.sheet_by_index(0)         # 按索引获取工作表,0就是工作表1

    for i in range(table.nrows):           # table.nrows表示总行数
        line = table.row_values(i)         # 读取每行数据,保存在line里面,line是list
        dataSet.append(line)               # 将line加入到data中,data是二维list
    dataSet = np.array(dataSet)            # 将data从二维list变成数组

    return dataSet
####################################################################################################################

#规范化处理函数
#####################################################################################
def standardization(v,mu,sigma):
    v = (v - mu)/sigma
    return v
#####################################################################################

#规范化数据
####################################################################################################################
def standardize(data):

    numTotal = data.shape[0]  # 记录测试数据集总条数
    attr1 = data[:, 0].astype(float)              #标准化过程
    attr2 = data[:, 1].astype(float)
    attr3 = data[:, 2].astype(float)
    attr4 = data[:, 3].astype(float)
    attr5 = data[:, 4].astype(float)
    attr6 = data[:, 5].astype(float)
    Data = data

    mu1 = np.mean(attr1)
    sigma1 = np.std(attr1)
    for i in range(0,numTotal):
        Data[:, 0][i] =  standardization(attr1[i],mu1,sigma1)

    mu2 = np.mean(attr2)
    sigma2 = np.std(attr2)
    for i in range(0,numTotal):
        Data[:, 1][i] =  standardization(attr2[i],mu2,sigma2)

    mu3 = np.mean(attr3)
    sigma3 = np.std(attr3)
    for i in range(0,numTotal):
        Data[:, 2][i] =  standardization(attr3[i],mu3,sigma3)

    mu4 = np.mean(attr4)
    sigma4 = np.std(attr4)
    for i in range(0,numTotal):
        Data[:, 3][i] =  standardization(attr4[i],mu4,sigma4)

    mu5 = np.mean(attr5)
    sigma5 = np.std(attr5)
    for i in range(0,numTotal):
        Data[:, 4][i] =  standardization(attr5[i],mu5,sigma5)

    mu6 = np.mean(attr6)
    sigma6 = np.std(attr6)
    for i in range(0,numTotal):
        Data[:, 5][i] =  standardization(attr6[i],mu6,sigma6)

    return Data
####################################################################################################################

#计算距离
####################################################################################################################
def calDistance(testingrow, trainingrow):
    dist = ((float(testingrow[0]) - float(trainingrow[0])) ** 2 + (float(testingrow[1]) - float(trainingrow[1])) ** 2 \
            + (float(testingrow[2]) - float(trainingrow[2])) ** 2 + (float(testingrow[3]) - float(trainingrow[3])) ** 2\
            + (float(testingrow[4]) - float(trainingrow[4])) ** 2 + (float(testingrow[5]) - float(trainingrow[5])) ** 2) ** 0.5
    return dist
####################################################################################################################

#K-means
####################################################################################################################
def Kmeans(dataset, k, centers, label):
    within = 0            #类内差异
    between = 0            #类间差异
    numTotal = dataset.shape[0]            # 记录测试数据集总条数
    attrSum = np.zeros((k,6))
    SampleNum = np.linspace(0,0,k)
    for i in range(0, numTotal):           # 遍历每个样本
        distance = []                      # 记录该样本和各簇中心的距离
        for j in range(0, k):
            distance.append(calDistance(dataset[i], centers[j]))  # 计算距离
        pos = distance.index(min(distance))
        within += (min(distance))**2       #计算类内差异
        dataset[i][-1] = centers[pos][-1]   # 将该样本标记为与其距离最近的簇中心的标签
        attrSum[pos] = attrSum[pos] + dataset[i][:-1].astype(float)     # 累加该簇的各属性值,为之后求平均做准备
        SampleNum[pos] = SampleNum[pos] + 1           #记录各簇的样本个数,为之后求平均做准备

    for i in range(0, k):                #计算类间差异
        for j in range(i, k):
            between += (calDistance(centers[i], centers[j]))**2

    if((label == dataset[:,-1].astype(float)).all()):    # 判断标签有无变化,以判断聚类是否已收敛
        return dataset, within, between
    else:
        next_label = dataset[:,-1].astype(float)        # 新的标签
        for i in range(0, k):
            for j in range(0, 6):
                centers[i][j] = attrSum[i][j]/SampleNum[i]     #新簇中心的属性值
            centers[i][6] = i                      #新簇中心的标签
        next_centers = centers
        return Kmeans(dataset, k, next_centers, next_label)   #新的一次迭代
####################################################################################################################

#聚类结果可视化
####################################################################################################################
def showData(dataSet):
    label_pred = dataSet[:,-1]
    mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
    # 这里'or'代表中的'o'代表画圈,'r'代表颜色为红色,后面的依次类推
    j = 0
    for i in label_pred:
        plt.plot([float(dataSet[j][0])], [float(dataSet[j][3])], mark[int(i)], markersize=4)
        j += 1
    plt.show()
####################################################################################################################

k = 8
Data113 = read_xslx(r'e:/Table/机器学习/1123/attribute_113.xlsx')
Data114 = read_xslx(r'e:/Table/机器学习/1123/attribute_114.xlsx')
data = np.vstack((Data113[1:,1:],Data114[1:,1:]))       #合并数据集
Data = standardize(data)           #数据规范化
numTotal = Data.shape[0]       # 记录测试数据集总条数

ram_center = []               #记录初始随机簇中心
pos = random.sample(range(0,numTotal), k)    #产生初始随机的簇中心
for i in range(0, k):
    Data[pos[i]][-1] = i            #簇中心的标签
    ram_center.append(Data[pos[i]])        #簇中心的属性值

label = Data[:,-1].astype(float)             #初始标签
Data_kmenas, within, between = Kmeans(Data, k, ram_center, label)   #调用K-means算法
quality = within/between
print(quality)
showData(Data_kmenas)


运行结果

  聚类的质量可以用类内差异和类间差异之比,即 w / b来表示,该值越小,聚类效果越好。
1)k=2时
w / b = 54.438898140288025
在这里插入图片描述
2)k=3时
w / b= 13.602127888428452
在这里插入图片描述

3)k=4时
w / b= 6.997101980295051
在这里插入图片描述

总结

  K-means算法是解决聚类问题的一种经典算法,有着简单快速的优点。同时对于处理大数据集,该算法是相对可伸缩和高效率的。此外,当结果簇是密集的时候,算法实现的效果会较好。
  但是,K-means算法也有着不少确定缺点。使用该算法一方面要先给出k,一方面也要先选择出随机的初始值,但这个初始值的选择最后会导致不同的聚类结果,也就是说算法对初值敏感。虽然这个问题可以通过多次聚类来缓解,但仍不能根本上解决。此外,该算法也不适合于发现非凸面形状的簇或者大小差别很大的簇。而且,它对于噪声和孤立点数据是敏感的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值