聚类

聚类和分类的区别:

分类其实是从特定的数据中挖掘模式,作出判断的过程。比如Gmail邮箱里有垃圾邮件分类器,一开始的时候可能什么都不过滤,在日常使用过程中,我人工对于每一封邮件点选“垃圾”或“不是垃圾”,过一段时间,Gmail就体现出一定的智能,能够自动过滤掉一些垃圾邮件了。这是因为在点选的过程中,其实是给每一条邮件打了一个“标签”,这个标签只有两个值,要么是“垃圾”,要么“不是垃圾”,Gmail就会不断研究哪些特点的邮件是垃圾,哪些特点的不是垃圾,形成一些判别的模式,这样当一封信的邮件到来,就可以自动把邮件分到“垃圾”和“不是垃圾”这两个我们人工设定的分类的其中一个。 聚类的的目的也是把数据分类,但是事先我是不知道如何去分的,完全是算法自己来判断各条数据之间的相似性,相似的就放在一起。在聚类的结论出来之前,我完全不知道每一类有什么特点,一定要根据聚类的结果通过人的经验来分析,看看聚成的这一类大概有什么特点。

聚类问题

所谓聚类问题,就是给定一个元素集合D,其中每个元素具有n个可观察属性,使用某种算法将D划分成k个子集,要求每个子集内部的元素之间相异度尽可能低,而不同子集的元素相异度尽可能高。其中每个子集叫做一个簇。

K-means聚类算法

聚类算法属于非监督学习的一种,聚类算法有很多种,大概有几十种聚类算法。 K-means是聚类算法中的最常用的一种,它运算速度快,但是只能应用于连续的数据。 K-means的算法的计算过程如下: 1,需要一个k值 来指定我们首先希望簇的个数(或者说首先定多少个中心点) 2,从数据集中随机选择k个数据点作为质心 3,计算每个点离质心的距离,选择最近的质心成一簇 4,每个簇算出新的质心 5,如果新的质心与老质心的距离小于某个阈值,则认为聚类达到期望 6,重复3-5步骤

"距离"的概念

K-means中的距离,是个相似度或者相异度的概念,根据数据集的特征(比如标量、向量、枚举分类等)决定如何计算距离。 常用的距离有: 1,欧几里德距离: 对于数值型、标量的数据,就应该用欧氏距离。 欧式距离公式 选择欧几里德距离计算距离,如果数据集的单位不一致,就会有问题。 所以要进行数据的标准化将数据按比例缩放,使之落入一个小的特定区间。去除数据的单位限制,将其转化纯数值型数据 标准化方法最常用的有两种: min-max标准化(离差标准化):对原始数据进行线性变换,是结果落到【0,1】区间,转换方法为 X'=(X-min)/(max-min),其中max为样本数据最大值,min为样本数据最小值。 z-score标准化(标准差标准化):处理后的数据符合标准正态分布(均值为0,方差为1),转换公式:X减去均值,再除以标准差 2,余弦相似度(向量夹角): 对于向量型的数据,使用标量型的距离公式就不合适了,一种流行的方法是计算两个向量的向量夹角。 我们高中学过 两个向量的余弦是两个向量的点乘除以两个向量模长的乘积。 (两个向量的点乘也可以用向量矩阵X的转秩乘向量矩阵Y来表示) 这里要注意,余弦度量度量的不是两者的相异度,而是相似度,所以我们应该使用他的反函数,即acos。 3,Jaccard: 就是交集除以并集 |S ∩ T|/|S ∪ T| 还有一些其他的距离计算方式:切比雪夫距离、曼哈顿距离、马哈拉诺比斯距离、调整后的余弦相似度等等

demo

from collections import defaultdict
from random import uniform
# from math import acos
from numpy import *



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.
    """
    dimensions = len(points[0])

    new_center = []

    for dimension in xrange(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.
    """
    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.
    """
    assignments = []
    for point in data_points:
        shortest = 10  # positive infinity
        shortest_index = 0
        for i in xrange(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):
    """
    """
    # d
    dimensions = len(a)
    _sum = 0
    for dimension in xrange(dimensions):
        difference_sq = (a[dimension] - b[dimension]) ** 2
        _sum += difference_sq
    return sqrt(_sum)

    # acos
    # difference_sq = dot(a,b)/(linalg.norm(a)*linalg.norm(b))
    # return  acos(difference_sq)



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.
    """
    centers = []
    dimensions = len(data_set[0])
    min_max = defaultdict(int)

    for point in data_set:
        for i in xrange(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 xrange(k):
        rand_point = []
        for i in xrange(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)
    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 assignments