【数学建模】数学建模算法学习其二(K-means聚类算法)

K-means聚类算法

简述

聚类是指将数据划分成多个组的任务,每一个组都叫做簇。聚类的目标就是要划分数据,使得每一个组里面的元素非常相似,但不同组里面的数据又非常不同,简单来说就是叫分类。我们通过聚类可以很方便地让我们对数据进行处理,把相似的数据分成一类,从而可以使得数据更加清晰。

K-means是聚类算法中最典型的一个,也是最简单、最常用的一个算法之一。这个算法主要的作用是将相似的样本自动归到一个类别中。通过设定合理的 K K K值,能够决定不一样的聚类效果。

K-means算法原理与理解

基本原理

假定给定数据样本 X X X,包含了 n n n个对象 X = { X 1 , X 2 , X 3 , . . . , X n } X=\{X_1,X_2,X_3,...,X_n\} X={X1,X2,X3,...,Xn},其中每个对象都具有m个维度的属性。Kmeans算法的目标是将n个对象依据对象间的相似性聚集到指定的 k k k个类簇中,且每个对象到类簇中心距离最小。

算法流程

在这里插入图片描述

主要思想:在给定 k k k值和 k k k个初始类簇中心点的情况下,把每个点分到离最近的类簇中心点所代表的类簇中,所有点分配完毕之后,根据一个类簇内所有点的到类簇中心的距离的平均值,然后多次迭代进行点的分配和类簇中心点的更新。
那么,我们首先初始化 k k k个聚类中心 { C 1 , C 2 , C 3 , . . . , C k } , 1 < j ≤ n \{C_1,C_2,C_3,...,C_k\},1<j\leq n {C1,C2,C3,...,Ck},1<jn。然后计算每一个对象到每一个聚类中心的欧氏距离
d i s ( X i , C j ) = ∑ t = 1 m ( X i t − C j t ) 2 dis(X_i,C_j)=\sqrt{\sum^m_{t=1}(X_{it}-C_{jt})^2} dis(Xi,Cj)=t=1m(XitCjt)2

符号说明范围
X i X_i Xi表示第 i i i个对象 1 ≤ i ≤ n 1\leq i\leq n 1in
C j C_j Cj表示第 j j j个聚类中心 1 ≤ j ≤ k 1\leq j\leq k 1jk
X i t X_{it} Xit表示第 i i i对象的第 t t t个属性 1 ≤ t ≤ m 1\leq t\leq m 1tm
C j t C_{jt} Cjt表示第 j j j个聚类中心的第 t t t个属性 1 ≤ t ≤ m 1\leq t\leq m 1tm

算法步骤

输入:样本集D,簇的数目k,最大迭代次数N;

输出:簇划分(k个簇,使平方误差最小);

算法步骤:

(1)为每个聚类选择一个初始聚类中心;

(2)将样本集按照最小距离原则分配到最邻近聚类;

(3)使用每个聚类的样本均值更新聚类中心;

(4)重复步骤(2)、(3),直到聚类中心不再发生变化;

(5)输出最终的聚类中心和k个簇划分;

K-Means算法优缺点

  • 优点
    (1)原理易懂、易于实现;
    (2)当簇间的区别较明显时,聚类效果较好;

  • 缺点
    (1)当样本集规模大时,收敛速度会变慢;
    (2)对孤立点数据敏感,少量噪声就会对平均值造成较大影响;
    (3)k的取值十分关键,对不同数据集,k选择没有参考性,需要大量实验

代码实现

# -*- coding: utf-8 -*-
#author: w61

import random
import math
import matplotlib.pyplot as plt

class Kmeans():
    def __init__(self, k, mode):
        self.datasets = []
        self.vector = 0                             #记录数据集的维数
        self.k = k                                      #聚类簇数
        self.C = [[] for i in range(k)]                  #簇
        self.mode = 0                               #初始化方式
        self.mean_vector = []                             #均值向量
        self.update_flag = 0                        #记录均值向量是否改变
        self.count = 0                              #记录循环次数
        self.max_count = 500                        #最大运行轮数

    def LoadData(self, file_name):
        """
        读取数据集
        :param file_name:数据集的路径
        :return:null
        """
        with open(file_name) as f:
            for contents in f:
                contents = contents.replace('\n','')
                content = contents.split(' ')
                self.datasets.append(content[1:])
        self.vector = len(self.datasets[0])

    def Inialize(self):
        """
        初始化均值向量
        :return:null
        """
        if self.mode == 0:
            means = random.sample(range(0,len(self.datasets)),self.k)
            for mean in means:
                self.mean_vector.append(self.datasets[mean])

    def getDistance(self, x, y):
        """
        计算两个向量(x和y)之间的距离
        :param x:
        :param y:
        :return: dis 距离
        """
        dis = 0
        for i in range(self.vector):
            dis += pow((float(x[i])-float(y[i])),2)
        dis = math.sqrt(dis)
        return dis

    def Clustering(self):
        """
        划分簇
        :return:null
        """
        self.C = [[] for m in range(self.k)]   #每一次都要先把C置零
        for j in range(0, len(self.datasets)):
            min_dis = 0
            min_index = 0
            for i in range(self.k):
                dis = self.GetDistance(self.datasets[j],self.mean_vector[i])
                if min_dis == 0 or dis < min_dis:
                    min_dis = dis
                    min_index = i
            self.C[min_index].append(j)

    def Update(self):
        """
        更新每个簇的均值向量
        :return:
        """
        self.update_flag = 0
        for i in range(self.k):
            means= []
            for h in range(self.vector):
                sum = 0
                for j in self.C[i]:
                    sum += float(self.datasets[j][h])
                if len(self.C[i]) == 0:
                    mean = 0
                else:
                    mean = sum / len(self.C[i])
                means.append(mean)
            if means == self.mean_vector[i]:
                continue
            else:
                self.mean_vector[i] = list(means)
                self.update_flag = 1

    def Plot(self):
        """
        绘图
        :return:
        """
        fig = plt.figure()
        ax = fig.add_subplot(111)
        colors = ['red','blue','green','black','pink','brown','gray']

        for i in range(self.k):
            for j in self.C[i]:
                x = float(self.datasets[j][0])
                y = float(self.datasets[j][1])
                plt.scatter(x,y,color = colors[i],marker='+')

        for i in range(self.k):
            for j in self.mean_vector:
                x = float(j[0])
                y = float(j[1])
                plt.scatter(x,y,color = 'cyan',marker='p')
        plt.show()

    def Process(self):
        """
        执行函数
        :return:
        """
        self.LoadData('data.txt')
        self.Inialize()
        while self.count <= self.max_count:
            self.Clustering()
            self.Update()
            if self.update_flag == 0:
                break
            self.count += 1
        print('k-means分类结果如下:')
        print(self.C)
        self.Plot()

if __name__ == "__main__":
    kmeans = Kmeans(5, 0)
    kmeans.Process()

测试所用的数据

1 0.697 0.460
2 0.774 0.376
3 0.634 0.264
4 0.608 0.318
5 0.556 0.215
6 0.403 0.237
7 0.481 0.149
8 0.437 0.211
9 0.666 0.091
10 0.243 0.267
11 0.245 0.057
12 0.343 0.099
13 0.639 0.161
14 0.657 0.198
15 0.360 0.370
16 0.593 0.042
17 0.719 0.103
18 0.359 0.188
19 0.339 0.241
20 0.282 0.257
21 0.748 0.232
22 0.714 0.346
23 0.483 0.312
24 0.478 0.437
25 0.525 0.369
26 0.751 0.489
27 0.532 0.472
28 0.473 0.376
29 0.725 0.445
30 0.446 0.459
  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Delv_Peter

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

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

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

打赏作者

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

抵扣说明:

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

余额充值