K-Means聚类算法(读懂、理解、复现)——K-Means篇

        如果你需要只是需要源代码的及如何使用得到想要的结果图,可以看K-means的算法复现及使用即可,如果需要了解更多可以看完(个人学习笔记,若有不足请理解)


1 K-means算法复现及使用

1.1 复现源代码

# -*- coding: UTF-8 -*-
'''
@Project :StudyingZ_算法集合
@File    :K-meansZ.py
@IDE     :PyCharm 
@Author  :努力学习 Hallow word
@Date    :2024-03-23 14:43 
'''
import os
import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = 'Microsoft YaHei'
plt.rcParams['axes.unicode_minus'] = False


class Node():
    # 接收中心点和label
    def __init__(self, parent, label):
        self.parent = parent
        self.label = label

class K_meansZ():

    def __init__(self, filename: str, feature: list, k):
        self.feature = feature
        self.k = k
        try:
            self.df = pd.read_excel(filename)
        except:
            self.df = pd.read_csv(filename)
        self.data = self.df[self.feature]
        self.min_max_data = self.min_max()
        self.center = []
        self.epoch = 0
        self.label = {}

    def min_max(self):
        # 数据归一化
        min_values = self.data.min(axis=0)
        max_values = self.data.max(axis=0)
        # 归一化每列的数据
        normalized_data = (self.data - min_values) / (max_values - min_values)

        return normalized_data

    def recover(self, data):
        min_values = self.data.min(axis=0)
        max_values = self.data.max(axis=0)

        return ((data) * (max_values - min_values) + (min_values))

    def k_means(self, k: int, epoch: int, threshold_value: float, min_epoch):
        # 以k值取数据的前k个坐标作为初始中心点
        self.center = (self.min_max_data[:k])
        print('初始化中心点', self.center)
        points = np.array(self.min_max_data)
        epoch = epoch
        threshold_value = threshold_value

        start = Node(self.center, label=Node)

        while min_epoch < epoch:
            min_epoch += 1
            dic = {}
            ls = []
            new_center = []
            # 通过曼哈顿算法得到各点距离最近的中心点
            for point in points:
                distance = [self.Manhattan_distance(point, center) for center in np.array(start.parent)]
                min_distance_index = np.argmin(distance)
                ls.append(min_distance_index)

                if min_distance_index not in dic:
                    dic[min_distance_index] = [point]
                else:
                    dic[min_distance_index].append(point)

            # 重新计算中心点()
            for value in dic.values():
                new_center.append(sum(value) / len(value))

            # 收敛条件,当我们每个中心点与上一个中心点的曼哈顿距离小于阈值(threshold_value)时进行收敛并结束迭代
            if max([self.Manhattan_distance(point, center) for point, center in
                    zip(np.array(start.parent), new_center)]) <= threshold_value:
                start.label = dic
                start.parent = new_center
                self.epoch = min_epoch
                break

            start.label = dic
            start.parent = new_center

        # 复原所有的归一化的点
        recover_dic = {}
        for label, datas in start.label.items():
            recover_dic[label] = np.array([self.recover(data) for data in datas])

        start.label = recover_dic
        # 复原中心点
        recover_ls = []
        print(start.parent)
        for datas in start.parent:
            recover_ls.append(np.array(self.recover(datas)))
        start.parent = recover_ls
        self.epoch = min_epoch
        self.center = recover_ls
        self.label = start.label
        draw = Draw(start.parent, start.label,self.feature)
        # self.three_dimensional(start.par ent, start.label)

    def Manhattan_distance(self, point, center):
        # 采用曼哈顿方法计算每个点到中心点距离
        return np.sum(np.abs(point - center))


class Draw():
    def __init__(self,center,point,feature):

        self.center = center
        self.point = point
        self.feature = feature
        if len(self.feature) == 3:
            self.three_dimensional()
        elif len(self.feature) == 2:
            self.twe_dimensional()

    def three_dimensional(self):
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')

        color_label = ['#7FFFAA', '#FF00FF', '#0000CD', '#87CEFA', '#7FFFAA', '#00FF00', '#FFEFD5', '#FF4500',
                       '#228B22', '#48D1CC', '#FF EFD5']
        marker_label = ['*','>','p','<','D','o','s','v','^']
        for i in range(k):
            x = [x[0] for x in self.point[i]]
            y = [y[1] for y in self.point[i]]
            z = [z[2] for z in self.point[i]]

            ax.scatter(x, y, z, c=color_label[i], marker=marker_label[i], label='Class {}'.format(i))

        for j in range(k):
            center1 = self.center[j]
            ax.scatter(center1[0], center1[1], center1[2], c=color_label[j], marker='x', s=150,
                       label='Center {}'.format(j))
        # 设置坐标轴标签
        ax.set_xlabel(self.feature[0])
        ax.set_ylabel(self.feature[1])
        ax.set_zlabel(self.feature[2])

        # 显示图例
        ax.legend()

        # 显示图形
        plt.show()

    def twe_dimensional(self):
        fig = plt.figure()
        ax = fig.add_subplot(111)

        # 需要什么样的颜色自己更改,散点图中的颜色是从列表第一个开始取的
        color_label = ['#FFC0CB', '#FF00FF', '#0000CD', '#87CEFA', '#7FFFAA', '#00FF00', '#FFEFD5', '#FF4500',
                       '#228B22', '#48D1CC', '#FFEFD5']
        for i in range(k):
            x = [x[0] for x in self.point[i]]
            y = [y[1] for y in self.point[i]]

            ax.scatter(x, y, c=color_label[i], marker='o', label='Class {}'.format(i))

        for j in range(k):
            center1 = self.center[j]
            ax.scatter(center1[0], center1[1], c=color_label[j], marker='x', s=150,
                       label='Center {}'.format(j))
        # 设置坐标轴标签
        ax.set_xlabel(self.feature[0])
        ax.set_ylabel(self.feature[1])
        # 显示图例
        ax.legend()

        # 显示图形
        plt.show()


if __name__ == '__main__':

    # 改为自己的文件路径
    path = "./c.xlsx"

    k = 3
    min_epoch = 0
    epoch = 50

    Threshold_value = 0.0001
    #  自己excel或csv表的column_names列名,只支持二维和三维分类及只能是两列或三列数据
    feature = input("输入2-3个特征列").split(",")
    if len(feature) > 3:
        sys.exit("特征列过长 ,请选择2-3个的特征列用于聚类")

    print(feature)
    k_means = K_meansZ(path, feature, k)
    k_means.k_means(k, epoch=epoch, threshold_value=Threshold_value, min_epoch=min_epoch)

    print("收敛的中心点为1:{},2:{},3:{}".format(k_means.center[0], k_means.center[1], k_means.center[2]))

    print("1类有数据{} 2类数据有{}".format(len(k_means.label[0]),len(k_means.label[1])))

    print('迭代次数为{}时开始收敛'.format(k_means.epoch))

1.2 如何使用在自己的数据上

1. 将代码复制到本地后,直接看 if __name__ == '__main__' 函数,我们需要调整的参数都在这下面。将path变量的路径改为自己文件路径。k值为需要分的类别数量(按照自己想将数据分为及类),epoch为迭代次数,Threshold_value为收敛阈值,都是可以更改的。

2. 运行程序,会在控制台看到input的指令 “请输入2-3个特征列”,指输入你自己数据的列名并用英文逗号隔开。如下图(最少两列,最多三列):

我的数据集就如下表所示
身高体重年龄
15010018
17812321

输入完回车就可以得到聚类图片,若是三列得到的是三D图像,若输入两列得到的是二维图像。如下所示:

3.查看所有中心点值和每类有多少数据如下代码所示:

    print("收敛的中心点为1:{},2:{},3:{}".format(k_means.center[0], k_means.center[1], k_means.center[2]))

    print("1类有数据{} 2类数据有{}".format(len(k_means.label[0]),len(k_means.label[1])))

    print('迭代次数为{}时开始收敛'.format(k_means.epoch))

按照相应方法打印即可

 2 学习笔记

        以下是学习后自己理解写的一点笔记,字不好看,望理解

 

 3 K-means原理、程序复现过程

 3.1 k-means原理

        K-means聚类算法是一种硬聚类的无监督学习算法,我们通过设定类别数量及K的值来随机在我们数据集中找到K个初始化质心点(如何随机由自己设定),然后计算每一个数据到这K个质心点之间的距离,将其划分到离该点距离最近的质心点一类,将所有的点聚类完成后,通过该类别的数据重新计算新的质心点,并不断迭代聚类和新质心点的计算步骤,直到我们的质心点开始收敛。

3.2 程序复现过程

步骤一:对数据进行处理,因为我只做了二维和三维数据的聚类,所以这里我们处理数据是处理为(x,y)或 (x,y,z)的坐标点形式。

 步骤二:初始化质心点,我们可以从样本数据中数据抽取K个数据点作为质心点,也可以直接取数据的前K个数据作为质心点,这里如何取到更好的质心点可以自己想。本文是直接取数据的前K个值

步骤三: 计算聚类的公式选择,我们有多种可以计算两点距离的公式,本文选用了曼哈顿公式进行距离计算,曼哈顿公式如下:

\left | x1-x2 \right | + \left | y1 - y2 \right |

        通过计算得到所有点到K个质心点的距离,并将所有点划分到离自己距离最近的一类

 步骤四:分别对每一类的质心点进行更新,更新方法采用该类所有数据点的平均值计算方式如下:

x= (\sum_{i=1}^{n}xi)/n,y=(\sum_{i=1}^{n}yi)/n

        这里计算得到x和y的平均值作为新的质心点

 步骤五:计算到新的质心点后开始重复步骤三和步骤四的过程(不断迭代),直到质心点开始收敛(即我们当前得到的质心点与上一个质心点之间的距离小于我们设定阈值)

4 总结 

        首次学习并对算法进行复现,其中可能存在些许不足之处,若有误,望指正!

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

seeyou1228

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

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

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

打赏作者

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

抵扣说明:

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

余额充值