K-Means 聚类算法 Python实现

Posted 叫我小陈就好

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K-Means 聚类算法 Python实现相关的知识,希望对你有一定的参考价值。

聚类算法

        将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类。由聚类所生成的簇是一组数据对象的集合,这些对象与同一个簇中的对象彼此相似,与其他簇中的对象相异。“物以类聚,人以群分”,在自然科学和社会科学中,存在着大量的分类问题。聚类分析又称群分析,它是研究(样品或指标)分类问题的一种统计分析方法。聚类分析起源于分类学,但是聚类不等于分类。聚类与分类的不同在于,聚类所要求划分的类是未知的。聚类分析内容非常丰富,有系统聚类法、有序样品聚类法、动态聚类法、模糊聚类法图论聚类法、聚类预报法等。

(以上名词解释源自百度百科)

K-Means基本思想

  • 初始化中心点
  • 计算样本点与中心点之间的距离,将样本点归为最近的中心点的类中
  • 根据类划分,计算新的样本中心点
  • 重复操作直到中心点或类的归属不再发生变化

需要预设类的个数为K


代码解析

生成随机样本

import torch
import math
import matplotlib.pyplot as plt

# 利用torch的函数生成随机的样本点
X=torch.randn(2000)*100
y=torch.randn(2000)*100
# 一个长度为2000的向量,表示点的类别归属
C=torch.zeros(2000)

生成初始中心点

# 设置k-means的类别数
K = 5
CentPoint = []

for i in range(K):
    CentPoint.append([torch.randint(-100,100,(1,)).item(), 
                      torch.randint(-100,100,(1,)).item()])

K-Means算法

# 计算二维平面上点的距离
def dis(a,b):
    return math.sqrt((a[0]-b[0])*(a[0]-b[0])+(a[1]-b[1])*(a[1]-b[1]))

# K-Means
# 一般执行10次以内即可完成分类
for p in range(10):

    # NewPoint初始化为0
    NewPoint = [[0, 0] for i in range(K)]
    for i in range(len(X)):
        mDis=1e9
        mC=0
        for j in range(len(CentPoint)):
            cp=CentPoint[j]
            D = dis([X[i].item(), y[i].item()], cp)
            # print("distance:", D)
            if mDis>D:
                mDis=D
                mC=j
        C[i]=mC
        # print("mC",mC,C[i].item())
        NewPoint[mC][0]+=X[i].item()
        NewPoint[mC][1]+=y[i].item()

    # 更新中心点
    for i in range(K):
        CentPoint[i][0]=NewPoint[i][0]/2000
        CentPoint[i][1]=NewPoint[i][1]/2000

    # 输出中心点,观察变化过程
    print(CentPoint)

结果展示

cc=list(C)
# 按不同颜色来区分不同种类的点
for i in range(len(X)):
    if cc[i]==0:
        plt.plot(X[i].item(), y[i].item(), 'r.')
    elif cc[i]==1:
        plt.plot(X[i].item(), y[i].item(), 'g.')
    elif cc[i]==2:
        plt.plot(X[i].item(), y[i].item(), 'b.')
    elif cc[i]==3:
        plt.plot(X[i].item(), y[i].item(), color='pink', marker='.')
    elif cc[i]==4:
        plt.plot(X[i].item(), y[i].item(), color='orange', marker='.')

# 样本聚类的中心点
for CP in CentPoint:
    plt.plot(CP[0], CP[1], color='black', marker='X')

可以观察出来,由于这组随机样本的生成是基于二维正态分布的,用K-Means来分析聚类,五个中心点的位置十分接近于二维正态分布的中心。


完整代码

import torch
import math
import matplotlib.pyplot as plt

def dis(a,b):
    return math.sqrt((a[0]-b[0])*(a[0]-b[0])+(a[1]-b[1])*(a[1]-b[1]))


X=torch.randn(2000)*100
y=torch.randn(2000)*100
C=torch.zeros(2000)

K = 5
CentPoint = []

for i in range(K):
    CentPoint.append([torch.randint(-100,100,(1,)).item(),
                      torch.randint(-100,100,(1,)).item()])

print(CentPoint)
for p in range(10):
    NewPoint = [[0, 0] for i in range(K)]
    for i in range(len(X)):
        mDis=1e9
        mC=0
        for j in range(len(CentPoint)):
            cp=CentPoint[j]
            D = dis([X[i].item(), y[i].item()], cp)
            if mDis>D:
                mDis=D
                mC=j
        C[i]=mC
        NewPoint[mC][0]+=X[i].item()
        NewPoint[mC][1]+=y[i].item()

    for i in range(K):
        CentPoint[i][0]=NewPoint[i][0]/2000
        CentPoint[i][1]=NewPoint[i][1]/2000
    print(CentPoint)

cc=list(C)
for i in range(len(X)):
    if cc[i]==0:
        plt.plot(X[i].item(), y[i].item(), 'r.')
    elif cc[i]==1:
        plt.plot(X[i].item(), y[i].item(), 'g.')
    elif cc[i]==2:
        plt.plot(X[i].item(), y[i].item(), 'b.')
    elif cc[i]==3:
        plt.plot(X[i].item(), y[i].item(), color='pink', marker='.')
    elif cc[i]==4:
        plt.plot(X[i].item(), y[i].item(), color='orange', marker='.')

for CP in CentPoint:
    plt.plot(CP[0], CP[1], color='black', marker='X')

plt.show()

K-Means聚类算法与Python实现

在数据挖掘中,K-Means算法从一个目标集中创建多个组,每个组的成员都是比较相似的。这是个想要探索一个数据集时比较流行的聚类分析技术,也可以说是一种最简单的聚类算法了。


算法实现过程:

k-means 算法的工作过程说明如下:

首先从含n个数据对象的数据集中任意选择k个对象作为初始聚类中心;

对于所剩下其它对象,则根据它们与这些聚类中心的相似度,分别将它们分配给与其最相似的(聚类中心所代表的)聚类;

然后再计算每个所获新聚类的聚类中心(该聚类中所有对象的均值);

不断重复这一过程直到标准测度函数开始收敛为止。

一般都采用均方差作为标准测度函数。

k个聚类有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。


伪代码说明表示流程如下:

选择k个点作为起始质心(一般随机选择)

当任意一个点的簇分配结果发生改变时

对数据集中的每个数据点

对每个质心

计算质心与数据点之间的距离

将数据点分配到距其最近的簇

对每一个簇,计算簇中所有点的均值并将均值作为质心


算法优缺点:

优点:容易实现,最大好处就是这个了

缺点:

1)k值的选择是用户指定的,不同的k得到的结果会有挺大的不同

2)对k个初始质心的选择比较敏感,容易陷入局部最小值

3)存在局限性,非球状数据分布可能就搞不定了

4)数据较大时,效率较低,收敛速度慢


算法实现和设计:

代码实现如下


import numpy as np
import random
import codecs
import re
import matplotlib.pyplot as plt


# 计算欧氏距离
def calculate_distance(vec1, vec2):
return np.sqrt(np.sum(np.square(vec1 - vec2)))


# 载入数据测试数据集
def load_data_set(input_file):
input_date = codecs.open(input_file, 'r', 'utf-8').readlines()
data_set = list()
for line in input_date:
line = line.strip()
strList = re.split('[ ]+', line) # 去除多余的空格
       numList = list()
for item in strList:
num = float(item)
numList.append(num)
data_set.append(numList)
return data_set # data_set = [[], [], [], ...]


# 初始化k个质心,随机获取
def init_centroids(data_set, k):
return random.sample(data_set, k)


# 对每个属于data_set的item,计算item与centroid_list中k个质心的欧式距离,找出距离最小的
# 并将item加入相应的簇类中
def min_distance(data_set, centroid_list):
cluster_dict = dict() # 用dict来保存簇类结果
   for item in data_set:
vec1 = np.array(item) # 转换成array形式
       flag = 0  # 簇分类标记,记录与相应簇距离最近的那个簇
       min_dis = float("inf") # 初始化为最大值

       for i in range(len(centroid_list)):
vec2 = np.array(centroid_list[i])
distance = calculate_distance(vec1, vec2) # 计算相应的欧式距离
           if distance < min_dis:
min_dis = distance
flag = i # 循环结束时,flag保存的是与当前item距离最近的那个簇标记

       if flag not in cluster_dict.keys(): # 簇标记不存在,进行初始化
           cluster_dict[flag] = list()
cluster_dict[flag].append(item) # 加入相应的类别中
   return cluster_dict # 返回新的聚类结果


def get_centroids(cluster_dict):
# 得到k个质心
   centroid_list = list()
for cluster_key in cluster_dict.keys():
# 计算每列的均值,找到质心
       centroid = np.mean(np.array(cluster_dict[cluster_key]), axis=0)
centroid_list.append(centroid)
return np.array(centroid_list).tolist()


# 计算簇集合间的均方误差
# 将簇类中各个向量与质心的距离进行累加求和
def get_var(cluster_dict, centroid_list):
sum = 0.0
   for cluster_key in cluster_dict.keys():
vec1 = np.array(centroid_list[cluster_key])
distance = 0.0
       for item in cluster_dict[cluster_key]:
vec2 = np.array(item)
distance += calculate_distance(vec1, vec2)
sum += distance
return sum


# 展示聚类结果
def show_cluster(centroid_list, cluster_dict):
# 不同簇类的标记 'or' --> 'o'代表圆,'r'代表red,'b':blue
   color_mark = ['or', 'ob', 'og', 'ok', 'oy', 'ow']
# 质心标记 同上'd'代表棱形
   centroid_mark = ['dr', 'db', 'dg', 'dk', 'dy', 'dw']
for key in cluster_dict.keys():
# 画质心点
       plt.plot(centroid_list[key][0], centroid_list[key][1], centroid_mark[key], markersize=12)
for item in cluster_dict[key]:
# 画簇类下的点
           plt.plot(item[0], item[1], color_mark[key])
plt.show()


if __name__ == '__main__':
input_file = "testData.txt"
   dataSet = load_data_set(input_file)
# 初始化质心,设置k=4
   centroidList = init_centroids(dataSet, 4)
# 第一次聚类迭代
   clusterDict = min_distance(dataSet, centroidList)
# 获得均方误差值,通过新旧均方误差来获得迭代终止条件
   newVar = get_var(clusterDict, centroidList)
oldVar = -0.0001  # 旧均方误差值初始化
   print('***** 第1次迭代 *****')
print('簇类')
for key in clusterDict.keys():
print(key, ' --> ', clusterDict[key])
print('k个均值向量: ', centroidList)
print('平均均方误差: ', newVar)
show_cluster(centroidList, clusterDict) # 展示聚类结果

   k = 2
   while abs(newVar - oldVar) >= 0.0001: # 当连续两次聚类结果小于0.0001时,迭代结束
       centroidList = get_centroids(clusterDict) # 获得新的质心
       clusterDict = min_distance(dataSet, centroidList) # 新的聚类结果
       oldVar = newVar
newVar = get_var(clusterDict, centroidList)

print('***** 第%d次迭代 *****' % k)
print('簇类')
for key in clusterDict.keys():
print(key, ' --> ', clusterDict[key])
print('k个均值向量: ', centroidList)
print('平均均方误差: ', newVar)
show_cluster(centroidList, clusterDict) # 展示聚类结果
       k += 1

运行效果截图:(每次结果可能存在不同)

1.

2:

K-Means聚类算法与Python实现

3:

K-Means聚类算法与Python实现

4:

5:

对于如何提高聚类效果,这就是另一个话题啦,我们后面再讨论。


以上是关于K-Means 聚类算法 Python实现的主要内容,如果未能解决你的问题,请参考以下文章

数据挖掘-聚类分析(Python实现K-Means算法)

福利机器学习:Python实现聚类算法之K-Means

干货|机器学习:Python实现聚类算法之K-Means

动手写机器学习算法:K-Means聚类算法

机器学习笔记:K-means聚类算法的Python实现

聚类算法K-Means算法及其Python实现