Python K-means聚类算法

今天在搞数模比赛时候研究了一下K-means均值算法,感觉这次写的还不错,发表成我的第一篇文章吧,我觉得发布文章也是一个好的总结,关于K-means算法的原理我就不过多赘述,站里有很多优秀的文章,这里主要介绍一下我实现的过程

库的引用

首先需要引用的库

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import math
import numpy as np
import random

数据读取

然后从excel中读取数据,将读取的数据用列表存储

def Read_Dataset():
    file_path = r'E:\pycharm\classify\Dataset.xlsx'
    data = pd.read_excel(file_path, header=1)

    dataset_frame = data.iloc[:,222:235]#用iloc读取文件
    dataset = []
    for i in range(len(dataset_frame.iloc[:,0])):#遍历列长
        dataset.append(dataset_frame.iloc[i,:].tolist())#将每一行数据存入一个list,最终存入二维数组dataset
    return dataset

算法部分

距离计算

计算每个点对于簇中心的距离,返回包含所有信息的列表

def Instance_Caculate(dataset,center_point_list):
    characteristic_number = len(dataset[0])#数据特征数量
    distance_list = []#最终返回的所有数据点与所有簇中心距离信息的列表
    for i in range(len(dataset)):
        one_point_distance_list = []#一个点和所有簇之间距离列表
        for j in range(len(center_point_list)):
            one_point_for_one_center_distance_list =[]
            for k in range(characteristic_number):
                one_feature_distance = dataset[i][k] - center_point_list[j][k]
                one_point_for_one_center_distance_list.append(one_feature_distance**2)
            one_point_distance_list.append(math.sqrt(sum(one_point_for_one_center_distance_list)))
        distance_list.append(one_point_distance_list)

    return distance_list

分类

接下来是用于分类的函数,把每个点进行分类

def Classify(distance_list):
    classify_result_list = []
    ori_list = distance_list
    sorted_list = []#排序后的距离列表,目的是找出一个点距离其最近的簇中心
    for i in range(len(distance_list)):
        sorted_data = sorted(distance_list[i])
        sorted_list.append(sorted_data)

    for i in range(len(ori_list)):

        classify_result = ori_list[i].index(sorted_list[i][0])
        classify_result_list.append(classify_result)#所有数据点分类结果列表

    return classify_result_list

更新中心点

根据分类结果更新新的类型中心点

def Update(dataset,classify_result_list,last_center):#迭代函数,输入数据集,分类结果集,以及迭代的中心点
    characteristic_number = len(dataset[0])
    center_num = len(last_center)
    cnt_sum = [0]*k#建立一个存储每一个分类的种群数量信息的集合
    update_center_sum = [[0]*characteristic_number for i in range(center_num)]
    update_center = [[0]*characteristic_number for i in range(center_num)]#最终更新中心点结果

    for i in range(len(classify_result_list)):
        for j in range(characteristic_number):
            update_center_sum[classify_result_list[i]][j] += dataset[i][j]#计算出每个类别的数据和
        cnt_sum[classify_result_list[i]] += 1
    for i in range(len(update_center_sum)):
        for j in range(characteristic_number):
            update_center[i][j] = update_center_sum[i][j]/cnt_sum[i]

    change = []#每次迭代的改变量,作为迭代结束的标志
    for i in range(center_num):
        update_distance_list = []
        for j in range(characteristic_number):
            one_feature_distance = last_center[i][j] - update_center[i][j]
            update_distance_list.append(one_feature_distance**2)
        change.append(math.sqrt(sum(update_distance_list)))
    return change,update_center

迭代

这些准备好之后,放入迭代函数

def interation(dataset,center):#输入数据集和中心点初值
    change = [10]#每一次迭代的改变值,赋了一个不为零的值作为初值
    change_list = []#每次迭代变化值计数
    update_center = []#迭代中心点列表
    classify_result_list = []
    cnt = 0#迭代次数计数

    while (sum(change) != 0):#当中心点位置不在改变停止迭代
        if (cnt == 0):
            distance_list = Instance_Caculate(dataset, center)
            classify_result_list = Classify(distance_list)
            change, update_center = Update(dataset, classify_result_list, center)
        else:
            distance_list = Instance_Caculate(dataset, update_center)
            classify_result_list = Classify(distance_list)
            change, update_center = Update(dataset, classify_result_list, update_center)
        change_list.append(change)
        cnt += 1
        print(change)
    return change_list,classify_result_list,update_center,cnt#返回每次迭代改变值统计列表,新的分类结果列表,新的中心点,以及迭代次数

可视化部分

到这里算法部分的函数就结束了,接下来是可视化部分,这里我分为了两张图,一个是分类结果的散点图,另一个是迭代过程的折线图

我将最后得到的结果存储到一个三维列表中,第一维度就是每个点的不同特征,第二个维度是不同的点,第三个维度的索引就是分类类别,也就是类别0到类别n,为了后续显示出区别

def Result_Organizations(dataset,classify_result_list):
    organizations = [[] for i in range(k)]

    for i in range(len(dataset)):
        organizations[classify_result_list[i]].append(dataset[i])
    return organizations

汉字显示函数

def Chinese_Define ():
    plt.rcParams['font.family'] = 'SimHei'
    plt.rcParams['axes.unicode_minus']=False

随机颜色函数

def randomcolor():
    colorArr = ['1','2','3','4','5','6','7','8','9','A','B','C','D','E','F']
    color = ""
    for i in range(6):
        color += colorArr[random.randint(0,14)]
    return "#"+color

分类结果可视化

结果的可视化,把数据先存入x,y集合中再显示会比整个数据集迭代显示运行起来快很多

def Result_Reveal(dataset,classify_result_list,organizations,update_center):#分类结果显示,输入数据集,分类集合(这两个可以不用,我是方便后续改)数据分类结果,以及最终中心点集

    color_list = []#用于记录下分类的颜色,后续与迭代过程可视化颜色对齐
    axe1 = plt.subplot(121)
    for i in range(k):
        x = []
        y = []
        for j in range(len(organizations[i])):
            x.append(organizations[i][j][0])
            y.append(organizations[i][j][1])
        color = randomcolor()
        axe1.scatter(x, y, marker='o', color=color, s=8)
        color_list.append(color)
    x = []
    y = []
    for i in range(len(update_center)):
        x.append(update_center[i][0])
        y.append(update_center[i][1])
    axe1.scatter(x, y, marker='x', color='red', s=15)
    return color_list

迭代过程可视化

接下来是迭代过程的显示

def Update_Feature_Reveal(change_list,cnt,color_list):#输入迭代中每轮改变值的统计列表,迭代次数,以及对应分类结果的颜色列表
    change_list_reshape = [[] for i in range(len(change_list[0]))]
    for i in range(len(change_list)):
        for j in range(len(change_list[0])):
            change_list_reshape[j].append(change_list[i][j])
    ax2 = plt.subplot(122)
    for i in range(len(change_list[0])):
        ax2.plot(np.arange(1,cnt+1,1),change_list_reshape[i],c = color_list[i],
                             marker = 'D',label = f'中心{i}变化值')
    ax2.legend()

把plt.show()打成了个函数

def Show():
    plt.show()

PCA降维

多维数据下的降维处理,用到了pca函数直接处理

def Pca_Transform(organizations,update_center):
    pca = PCA(n_components=2)
    organizations_pca = [[] for i in range(k)]
    update_center_pca = []

    for i in range(k):
        organizations_pca[i] = pca.fit_transform(organizations[i])
    update_center_pca = pca.fit_transform(update_center)

    return organizations_pca,update_center_pca

主函数

这里改变k值更改分类数量,我用的这个数据集有7000左右个数据

if __name__ =='__main__':
    Chinese_Define()
    dataset = Read_Pca_Transformed_Dataset()
    k = 11

    center =[]

    for i in range(k):
        index = random.randint(0,len(dataset))
        center.append(dataset[index])

    change_list,classify_result_list,update_center,cnt = interation(dataset,center)
    organizations = Result_Organizations(dataset,classify_result_list)
    organizations_pca, update_center_pca = Pca_Transform(organizations, update_center)#pca降维的数据接收

    plt.figure()
    color_list = Result_Reveal(dataset,classify_result_list,organizations,update_center)#这里如果是二维以上数据改为上两行的organizations_pca, update_center_pca
    Update_Feature_Reveal(change_list,cnt,color_list)
    Show()

运行结果展示

我发现多维数据分类后再降维显示会很乱,这里就以二维数据展示

 

 

 

完整代码

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import math
import numpy as np
import random

def Chinese_Define ():
    plt.rcParams['font.family'] = 'SimHei'
    plt.rcParams['axes.unicode_minus']=False

def Read_Dataset():
    file_path = r'E:\pycharm\classify\Dataset.xlsx'
    data = pd.read_excel(file_path, header=1)

    dataset_frame = data.iloc[:,222:235]#用iloc读取文件
    dataset = []
    for i in range(len(dataset_frame.iloc[:,0])):#遍历列长
        dataset.append(dataset_frame.iloc[i,:].tolist())#将每一行数据存入一个list,最终存入二维数组dataset
    return dataset

def Read_Pca_Transformed_Dataset():
    file_path = 'E:\pycharm\pca_write\Dataset_pca_transformed.xlsx'

    dataset_frame= pd.read_excel(file_path, header=0)
    dataset = []
    for i in range(len(dataset_frame.iloc[:, 0])):
        dataset.append(dataset_frame.iloc[i, :].tolist())
    return dataset


def randomcolor():
    colorArr = ['1','2','3','4','5','6','7','8','9','A','B','C','D','E','F']
    color = ""
    for i in range(6):
        color += colorArr[random.randint(0,14)]
    return "#"+color

def Instance_Caculate(dataset,center_point_list):
    characteristic_number = len(dataset[0])#数据特征数量
    distance_list = []#最终返回的所有数据点与所有簇中心距离信息的列表
    for i in range(len(dataset)):
        one_point_distance_list = []#一个点和所有簇之间距离列表
        for j in range(len(center_point_list)):
            one_point_for_one_center_distance_list =[]
            for k in range(characteristic_number):
                one_feature_distance = dataset[i][k] - center_point_list[j][k]
                one_point_for_one_center_distance_list.append(one_feature_distance**2)
            one_point_distance_list.append(math.sqrt(sum(one_point_for_one_center_distance_list)))
        distance_list.append(one_point_distance_list)

    return distance_list

def Classify(distance_list):
    classify_result_list = []
    ori_list = distance_list
    sorted_list = []#排序后的距离列表,目的是找出一个点距离其最近的簇中心
    for i in range(len(distance_list)):
        sorted_data = sorted(distance_list[i])
        sorted_list.append(sorted_data)

    for i in range(len(ori_list)):

        classify_result = ori_list[i].index(sorted_list[i][0])
        classify_result_list.append(classify_result)#所有数据点分类结果列表

    return classify_result_list

def Update(dataset,classify_result_list,last_center):#迭代函数,输入数据集,分类结果集,以及迭代的中心点
    characteristic_number = len(dataset[0])
    center_num = len(last_center)
    cnt_sum = [0]*k#建立一个存储每一个分类的种群数量信息的集合
    update_center_sum = [[0]*characteristic_number for i in range(center_num)]
    update_center = [[0]*characteristic_number for i in range(center_num)]#最终更新中心点结果

    for i in range(len(classify_result_list)):
        for j in range(characteristic_number):
            update_center_sum[classify_result_list[i]][j] += dataset[i][j]#计算出每个类别的数据和
        cnt_sum[classify_result_list[i]] += 1
    for i in range(len(update_center_sum)):
        for j in range(characteristic_number):
            update_center[i][j] = update_center_sum[i][j]/cnt_sum[i]

    change = []#每次迭代的改变量,作为迭代结束的标志
    for i in range(center_num):
        update_distance_list = []
        for j in range(characteristic_number):
            one_feature_distance = last_center[i][j] - update_center[i][j]
            update_distance_list.append(one_feature_distance**2)
        change.append(math.sqrt(sum(update_distance_list)))
    return change,update_center

def interation(dataset,center):#输入数据集和中心点初值
    change = [10]#每一次迭代的改变值,赋了一个不为零的值作为初值
    change_list = []#每次迭代变化值计数
    update_center = []#迭代中心点列表
    classify_result_list = []
    cnt = 0#迭代次数计数

    while (sum(change) != 0):#当中心点位置不在改变停止迭代
        if (cnt == 0):
            distance_list = Instance_Caculate(dataset, center)
            classify_result_list = Classify(distance_list)
            change, update_center = Update(dataset, classify_result_list, center)
        else:
            distance_list = Instance_Caculate(dataset, update_center)
            classify_result_list = Classify(distance_list)
            change, update_center = Update(dataset, classify_result_list, update_center)
        change_list.append(change)
        cnt += 1
        print(change)
    return change_list,classify_result_list,update_center,cnt#返回每次迭代改变值统计列表,新的分类结果列表,新的中心点,以及迭代次数

def Show():
    plt.show()

def Result_Reveal(dataset,classify_result_list,organizations,update_center):#分类结果显示,输入数据集,分类集合(这两个可以不用,我是方便后续改)数据分类结果,以及最终中心点集

    color_list = []#用于记录下分类的颜色,后续与迭代过程可视化颜色对齐
    axe1 = plt.subplot(121)
    for i in range(k):
        x = []
        y = []
        for j in range(len(organizations[i])):
            x.append(organizations[i][j][0])
            y.append(organizations[i][j][1])
        color = randomcolor()
        axe1.scatter(x, y, marker='o', color=color, s=8)
        color_list.append(color)
    x = []
    y = []
    for i in range(len(update_center)):
        x.append(update_center[i][0])
        y.append(update_center[i][1])
    axe1.scatter(x, y, marker='x', color='red', s=15)
    return color_list

def Update_Feature_Reveal(change_list,cnt,color_list):#输入迭代中每轮改变值的统计列表,迭代次数,以及对应分类结果的颜色列表
    change_list_reshape = [[] for i in range(len(change_list[0]))]
    for i in range(len(change_list)):
        for j in range(len(change_list[0])):
            change_list_reshape[j].append(change_list[i][j])
    ax2 = plt.subplot(122)
    for i in range(len(change_list[0])):
        ax2.plot(np.arange(1,cnt+1,1),change_list_reshape[i],c = color_list[i],
                             marker = 'D',label = f'中心{i}变化值')
    ax2.legend()



def Result_Organizations(dataset,classify_result_list):
    organizations = [[] for i in range(k)]

    for i in range(len(dataset)):
        organizations[classify_result_list[i]].append(dataset[i])
    return organizations

def Pca_Transform(organizations,update_center):
    pca = PCA(n_components=2)
    organizations_pca = [[] for i in range(k)]
    update_center_pca = []

    for i in range(k):
        organizations_pca[i] = pca.fit_transform(organizations[i])
    update_center_pca = pca.fit_transform(update_center)

    return organizations_pca,update_center_pca

if __name__ =='__main__':
    Chinese_Define()
    dataset = Read_Pca_Transformed_Dataset()
    k = 11

    center =[]

    for i in range(k):
        index = random.randint(0,len(dataset))
        center.append(dataset[index])

    change_list,classify_result_list,update_center,cnt = interation(dataset,center)
    organizations = Result_Organizations(dataset,classify_result_list)
    organizations_pca, update_center_pca = Pca_Transform(organizations, update_center)#pca降维的数据接收

    plt.figure()
    color_list = Result_Reveal(dataset,classify_result_list,organizations,update_center)#这里如果是二维以上数据改为上两行的organizations_pca, update_center_pca
    Update_Feature_Reveal(change_list,cnt,color_list)
    Show()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值