k-means聚类算法-代码阅读

前提:下了一个9年前的kmeans算法的代码仓库,整体写的清晰精巧。值得写一些代码阅读笔记:
是用python2写的,导致:xrange报unresolved reference
python2中有,range()返回的是一个list对象,而xrange()返回的是一个生成器对象。
python3中没有,取消了xrange(),并且和range()函数合并为range(),所以在python3中调用xrange会出现Unresolved reference ‘xrange’ ,也就是说,在Python3中xrange没有定义。全部改掉了。
Python的collections模块
这个模块实现了特定目标的容器,以提供Python标准内建容器 dict , list , set , 和 tuple 的替代选择,包括:namedtuple(),duque,ChainMap,Counter,OrderedDict,defaultdict,UserDict,UserList,UserString.Python文档:https://docs.python.org/zh-cn/3/library/collections.html
from collections import defaultdict
​class collections.defaultdict(default_factory=None, /[, …])
返回一个新的类似字典的对象。 defaultdict 是内置 dict 类的子类。 它重载了一个方法并添加了一个可写的实例变量。 其余的功能与 dict 类相同因而不在此文档中写明。
本对象包含一个名为 default_factory 的属性,构造时,第一个参数用于为该属性提供初始值,默认为 None。所有其他参数(包括关键字参数)都相当于传递给 dict 的构造函数。
random模块
该模块实现了各种分布的伪随机数生成器。对于整数,从范围中有统一的选择;对于序列,存在随机元素的统一选择、用于生成列表的随机排列的函数、以及用于随机抽样而无需替换的函数。
在实数轴上,有计算均匀、高斯(正态)、对数正态、负指数、伽马和贝塔分布的函数。为了生成角度分布,可以使用von Mises分布。
几乎所有模块函数都依赖于基本函数random(),它在半开放区间[0.0,1.0)内均匀生成随机浮点数。Python使用Mersenne Twister作为核心生成器。它产生53位精度浮点数,周期为2**19937-1,其在C中的底层实现既快又线程安全。
random.seed(a=None,version=2)
random.getstate()
random.setstate(state)
random.randbytes()
整数用函数
random.randrange(stop)
random.randrange(start, stop[,step])
random.randint(a,b)
还有序列用函数、实值分布、替代生成器。
​if min_key not in min_max or val < min_max[min_key]:min_max[min_key] = val
利用keys方法获取键,根据键修改值。

以下附上代码:

from collections import defaultdict
from random import uniform
from math import sqrt



def point_avg(points):
    """
    Accepts a list of points, each with the same number of dimensions.
    NB. points can have more dimensions than 2
    
    Returns a new point which is the center of all the points.
    接受一个列表中的点,每个点有相同数量的维度,维度数量可以多于2。
    返回一个新的点,这个点是所有输入点的中心。
    """
    dimensions = len(points[0])

    new_center = []

    for dimension in range(dimensions):
        dim_sum = 0  # dimension sum
        for p in points:
            dim_sum += p[dimension]

        # average of each dimension
        # 计算每个维度的中心
        new_center.append(dim_sum / float(len(points)))

    return new_center


def update_centers(data_set, assignments):
    """
    Accepts a dataset and a list of assignments; the indexes 
    of both lists correspond to each other.

    Compute the center for each of the assigned groups.

    Return `k` centers where `k` is the number of unique assignments.
    接受一个数据集和一个列表,这个列表的元素索引与数据集样本一一对应,表示样本所分配的类别。
    计算每个指定类别的点的中心,返回k个中心,其中k是不同类别的数量。
    """
    new_means = defaultdict(list)
    centers = []
    for assignment, point in zip(assignments, data_set):
        new_means[assignment].append(point)
        
    for points in new_means.itervalues():
        centers.append(point_avg(points))

    return centers


def assign_points(data_points, centers):
    """
    Given a data set and a list of points betweeen other points,
    assign each point to an index that corresponds to the index
    of the center point on it's proximity to that point. 
    Return a an array of indexes of centers that correspond to
    an index in the data set; that is, if there are N points
    in `data_set` the list we return will have N elements. Also
    If there are Y points in `centers` there will be Y unique
    possible values within the returned list.
    输入一个数据集和一个?,将点分配到不同的索引(类别)中,索引对应着那个点最接近的中心点。
    返回一个索引的数组,对应着数据集中样本的对应的类别索引。如果有N个点在数据集中,
    我们返回的数组就有N个元素。如果有Y个中心点,也就会有Y个不同的可能的值在返回的数组中。
    """
    assignments = []
    for point in data_points:
        shortest = ()  # positive infinity
        shortest_index = 0
        for i in range(len(centers)):
            val = distance(point, centers[i])
            if val < shortest:
                shortest = val
                shortest_index = i
        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)


def generate_k(data_set, k):
    """
    Given `data_set`, which is an array of arrays,
    find the minimum and maximum for each coordinate, a range.
    Generate `k` random points between the ranges.
    Return an array of the random points within the ranges.
    输入数据集,它是数组的数组。找到每个坐标的最小值和最大值,一个范围,在该范围内产生k个随机点。
    返回该范围内随机点的数组。
    """
    centers = []
    dimensions = len(data_set[0])
    min_max = defaultdict(int)

    for point in data_set:
        for i in range(dimensions):
            val = point[i]
            min_key = 'min_%d' % i #命名中
            max_key = 'max_%d' % i
            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

    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]
            
            rand_point.append(uniform(min_val, max_val))

        centers.append(rand_point)

    return centers


def k_means(dataset, k):
    k_points = generate_k(dataset, k)#产生k个中心点
    assignments = assign_points(dataset, k_points)#分配类别
    old_assignments = None
    while assignments != old_assignments:#只要不收敛,就执行:
        new_centers = update_centers(dataset, assignments)#更新中心点
        old_assignments = assignments
        assignments = assign_points(dataset, new_centers)#更新分类
    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],
#     ]
# print k_means(points, 3)

你可能感兴趣的:(知识碎片,算法,kmeans,聚类)