KMEANS 实现

  • Post author:
  • Post category:其他




KMEANS 的 python 实现

 记录一下上学期实现的k-means 算法



在这里插入图片描述



KMEANS 算法的基本思想


​KMEANS

的主要步骤思想是提前定义好聚类的数量,然后通过计算点到类中心点的欧式距离,不断更新聚类的中心点的坐标,直到前后两次迭代的所得到的所有点到中心点的欧式距离和相等则停止迭代。算法的步骤大致如下:

  1. ​ 准备好数据集,定义聚类数量
  2. ​ 准备初始的类中心点坐标
  3. ​ 将每个点归为距离最近的中心的那个类
  4. ​ 根据上一次得到的分类点的集合计算其中心点作为新的中心点,若前后得到的中心点的坐标都不变的话就停止迭代,否则返回第三步 (还增加了一种特殊情况的,即出现初始定义的类太多然后对应的一个类的中心点没有点被划分进去)
  5. ​ 得到分类后的点的标签



KMEANS 算法实现的主要代码

import copy
import json
import matplotlib.animation as animation
from sklearn.datasets import make_classification
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np

// 加载数据集
X = make_classification(n_samples=1000,n_features=2,n_redundant=0,n_informative=2,)
// 提前定义用于保存算法递归结束时的结果
result_centers = {"centers":[],"class_node_number":[],'classesIndexGroup':[]}

//对应上述步骤的  3
def classify(data,centers):
  """
      :param data:   数组格式的数据
      :param centers:   数据格式的数据  长度为设定的中心点数
      :return: classes {list} , sumDis {float}
  """
  length = centers.shape[0]     #获得聚类的中心数
  classes = [ [] for i in range(length)]
  classDataIndex = []
  sumDis = 0

  for i in range(data.shape[0]):
    per_data = data[i]
    diffMat = np.tile(per_data,(length,1)) - centers
    sqDiffMat =diffMat**2
    sqDisMat = sqDiffMat.sum(axis=1)   #求得到每个中心点的距离
    sorted_index = sqDisMat.argsort()   #获得排序后的索引
    classDataIndex.append(sorted_index[0])
    classes[sorted_index[0]].append(list(per_data))
    sumDis += sqDisMat[sorted_index[0]]
    sorted_index[0]
  draw(data,classDataIndex,centers)
  return classes,sumDis,classDataIndex

// 对应上述步骤的 4
def upCenters(classes):
  """
      :param:classes: 长度为设定的中点的数量  每个元素中包含了上次得到的每个类的数据 数据信息
      : return centers: 迭代更新后的中心点数据 ->  np.array
      根据上依次的聚类中心算出上次分类的每类的平均位置
  """
  centers = []
  for i in range(len(classes)):
      per_class = classes[i]
      per_class = np.array(per_class)
      if len(per_class) != 0:                               #可能出现一个聚类的中心不再有点的情况即分母出现零导致出错
          center = per_class.sum(axis=0) / len(per_class)   #转化为数组按照行来求和   算出上次分类中每类的中心
      elif len(per_class) == 0:
          continue
      centers.append(center)
  return np.array(centers)

// 算法执行的入口
def kmeans(data,centers,sumDis):
  """
      递归的寻找聚类的中心直到最后的总的距离不再变化
  """
  classes,new_sumDis, classDataIndex = classify(data,centers)
  # 停止递归条件
  if sumDis == new_sumDis:
      result_centers["centers"] = copy.deepcopy(centers) 
      result_centers["classesIndexGroup"] = copy.deepcopy(classDataIndex) 
      result_centers["class_node_number"] = copy.deepcopy(np.array([len(elem) for elem in classes]))
      # print("聚类停止 聚类中心数 类数",len(centers),len(classes))
      return centers
  new_centers = upCenters(classes)
  kmeans(data,new_centers,new_sumDis)

def draw(data,lables,centers=None):
  """
    用于绘制聚类后的数据
  """
  plt.clf()
  plt.scatter(data[:, 0], data[:, 1], s=3, c=lables)
  if centers is not None:
    plt.scatter(centers[:, 0], centers[:, 1], s=10, c='red')
  plt.pause(0.5)

def runKmeans(clauseterNum , data):
  kmeans(data, data[:clauseterNum], 0)

def useModel(data):
  """
    使用sklearn的KMeans模型进行聚类
  """
  clf = KMeans(n_clusters=5)
  clf.fit(data)
  print(clf.cluster_centers_)
  return clf.labels_

def main():
  data = X[0]
  # runKmeans(5,data)
  lables = useModel(data)
  draw(data,lables)

if __name__ == '__main__':
  plt.ion()
  main()
  plt.ioff()   #用于绘制动图的
  plt.show()



KMEANS 评价


优点

  • 可以适用于高高纬度的数据
  • 算法实现的原理比较简单
  • 算法所需要调节的参数比较少,只有对应的 K (聚类数量)
  • 算法的运行速度较快


缺点

  • 离群点对算法的效果影响较大(噪声点对算法结果的影响)
  • 初始的中心点的选择
  • 对于不是凸的数据集比较难收敛(改进:基于密度的聚类算法更加适合,比如DESCAN算法)



版权声明:本文为qq_52785898原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。