k-means算法实现代码

本代码来自gihub地址。添加注释仅供理解。
这篇博客对k-means算法讲解比较详细。
K-means算法参考自《统计学习方法》

K-means算法:
  • 输入:n个样本的集合
  • 输出:样本集合的聚类
  1. 初始化,随机取 k 个样本点作为初始聚类中心
  2. 对样本进行聚类。对固定的类中心,计算样本到类中心距离,将每个样本指派到与其最近的中心的类中,构成聚类结果
  3. 计算新的类中心。对聚类结果计算样本的均值,做为新的类中心
  4. 如果迭代收敛或符合停止条件,停止迭代。否则,令 t = t + 1 ,返回 2步
算法实现:
# 小标注:无论是defaultdict还是{},a.get() 如果key不存在都返回None。
# 对于defaultdict,a['b'] key不存在返回默认的[];对于{},a['b'] key不存在返回KeyError
from collections import defaultdict
from random import uniform
from math import sqrt

# 输入点集列表(具有相同维度),返回该点集的中心。
def point_avg(points):
    dimensions = len(points[0]) # 维度
    new_center = [] # 中心集合
    # 计算所有点在每一维度分量的均值,获得中心点坐标
    for dimension in range(dimensions):
        dim_sum = 0  # 记录所有点坐标的当前维度总和
        for p in points:
            dim_sum += p[dimension] # 所有点求和
        new_center.append(dim_sum / float(len(points))) # 计算当前维度的均值,并插入到中心点坐标中
    return new_center

# 输入点集列表data_set和已有的分配assignments (他们相同索引的元素是对应的)
# 计算每一个簇的中心点,返回k个新的中心
def update_centers(data_set, assignments):
    new_means = defaultdict(list) # 初始化字典
    centers = [] # 初始化中心点集合
    # zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
    # zip()函数形成的列表为:[[type,(x,y)],[type,(x,y)],[type,(x,y)],……,[type,(x,y)]]
    for assignment, point in zip(assignments, data_set):
        new_means[assignment].append(point)
    # 计算每一类元素的中心点,并添加到centers列表中
    for points in new_means.values():
        centers.append(point_avg(points))
    # 返回中心点集合
    return centers

# 输入所有点的集合data_points以及选取的K个中心点的集合centers
# 通过计算将每个节点分配给一个距离最短的中心点,并输出一个列表,存储有每个节点对应的中心的索引。
# 输出列表形式为:[center_index, center_index, center_index, ……, center_index]
# 对应的点集合为:[point1, point2, point3, ……, pointN]
# 这里data_points和assignments的长度是相等的
def assign_points(data_points, centers):
    assignments = []
    # 遍历点集,获取每一个点距离最近的中心点,并存储其索引。
    for point in data_points:
        shortest = float('inf')  # 最短距离,先初始化为正无穷大
        shortest_index = 0 # 最短距离对应的索引
        for i in range(len(centers)):
            val = distance(point, centers[i]) # 计算当前节点和中心点的距离
            # 找到距离当前节点最近的中心点
            if val < shortest:
                shortest = val
                shortest_index = i
        # 将距离当前节点最近的中心点索引存储到assignments列表中(该列表和存储所有点的列表data_points的索引是对应的!)
        assignments.append(shortest_index)
    return assignments

# 输入两点,计算两点之间的距离并返回
def distance(a, b):
    dimensions = len(a) # 维度
    _sum = 0
    # 分别计算每一维度值的平方并递加
    for dimension in range(dimensions):
        difference_sq = (a[dimension] - b[dimension]) ** 2
        _sum += difference_sq
    # 对平方和开方并返回
    return sqrt(_sum)

# 在节点范围内,随机选取k个点作为聚类中心并返回这k个点
def generate_k(data_set, k):
    centers = [] # 中心列表
    dimensions = len(data_set[0]) # 维度
    min_max = defaultdict(int) # 初始化

    # 遍历全部节点
    for point in data_set:
        # 分别对不同维度进行计算,记录所有点每个维度的最大值和最小值
        for i in range(dimensions):
            val = point[i] # 节点第i维的值
            min_key = 'min_%d' % i # min_0,min_1,min_2,……,min_N
            max_key = 'max_%d' % i # max_0,max_1,max_2,……,max_N
            if min_key not in min_max or val < min_max[min_key]:
                min_max[min_key] = val
            if max_key not in min_max or val > min_max[max_key]:
                min_max[max_key] = val

    # k为函数接收到的参数,表示要分成k类
    for _k in range(k):
        rand_point = []
        # 提取每个维度的最大值和最小值,在最小和最大值的范围内随机生成一个值作为中点
        for i in range(dimensions):
            min_val = min_max['min_%d' % i]
            max_val = min_max['max_%d' % i]
            # uniform() 方法将随机生成下一个实数,它在 [x, y) 范围内
            rand_point.append(uniform(min_val, max_val))
        # 将生成的中点存储到中点列表中
        centers.append(rand_point)
    return centers

# 输入所有点集dataset以及聚类数目k,
def k_means(dataset, k):
    k_points = generate_k(dataset, k)  # 随机选出k个随机点作为中心
    assignments = assign_points(dataset, k_points) # 将所有点分配给距离最近的中心点
    print("initial assignments : %s" % (assignments))
    old_assignments = None
    # 迭代进行聚类,当两次聚类结果相同时,结束
    while assignments != old_assignments:
        new_centers = update_centers(dataset, assignments) # 根据当前的分配更新中心点
        old_assignments = assignments # 记录上一次分配结果
        assignments = assign_points(dataset, new_centers) # 将所有点分配给距离最近的中心点
    # 返回最终分类结果(一个存储对应中心点索引的队列)
    print("finial assignments : %s" % (assignments))
    return zip(assignments, dataset)


points = [
    [1, 2],
    [2, 1],
    [3, 1],
    [5, 4],
    [5, 5],
    [6, 5],
    [10, 8],
    [7, 9],
    [11, 5],
    [14, 9],
    [14, 14],
    ]
result = k_means(points, 3)
print (list(result))

运行结果:

在这里插入图片描述

你可能感兴趣的:(k-means算法实现代码)