最大最小聚类

机器学习
Published

November 24, 2018

我写的最大最小聚类.但是我写的时候只写了依据最小进行聚类 233

算法流程

  1. 随机找一个点为第一个聚类中心
  2. 找距离这个点最远的点为第二个聚类中心
  3. 遍历所有的欧式距离,以其中较小的距离为准,如果这个距离大于第一第二聚类点的\(\theta\)倍,那么这个点就是新的聚类点.重复直到没有新的聚类点.
  4. 分类即可

代码

import math
import matplotlib.pyplot as plt
import numpy as np


def createDataSet():
    """
    创建测试的数据集,里面的数值中具有连续值
    :return:
    """
    dataSet = [
        [0.697, 0.460], [0.774, 0.376], [0.634, 0.264],
        [0.608, 0.318], [0.556, 0.215],
        [0.403, 0.237], [0.481, 0.149], [0.437, 0.211],
        [0.666, 0.091], [0.243, 0.267],
        [0.245, 0.057], [0.343, 0.099], [0.639, 0.161],
        [0.657, 0.198], [0.360, 0.370],
        [0.593, 0.042], [0.719, 0.103], [0.359, 0.188],
        [0.339, 0.241], [0.282, 0.257],
        [0.748, 0.232], [0.714, 0.346], [0.483, 0.312],
        [0.478, 0.437], [0.525, 0.369],
        [0.751, 0.489], [0.532, 0.472], [0.473, 0.376],
        [0.725, 0.445], [0.446, 0.459],
    ]

    # 特征值列表

    labels = ['密度', '含糖率']

    # 特征对应的所有可能的情况
    labels_full = {}

    for i in range(len(labels)):
        labelList = [example[i] for example in dataSet]
        uniqueLabel = set(labelList)
        labels_full[labels[i]] = uniqueLabel

    return dataSet, labels, labels_full


# 计算两点之间的欧式距离
def one_one_dict(a: list, b: list)->float:
    return pow(a[0]-b[0], 2)+pow(a[1]-b[1], 2)


# 计算一个点与所有聚类中心的欧式距离
def one_n_dict(sample, centers: list)->np.ndarray:
    sample = np.array(sample)
    centers = np.array(centers)
    # 将sample复制k次 与 centers 矩阵维度相同
    samples = np.tile(sample, (centers.shape[0], 1))
    # print(samples)
    # 矩阵相减然后平方,对每一列求和 distances=[1 x k]
    distances = np.power(samples - centers, 2).sum(axis=1)
    return distances


# 计算n个点到一个中心点的欧式距离
def n_one_dist(X, c):
    c = np.array(c, ndmin=2)
    c_s = np.tile(c, (X.shape[0], 1))
    # 利用矩阵乘法求出欧式距离矩阵
    distances = np.power(X-c_s, 2).sum(axis=1)
    return distances


class Maxmin(object):
    def __init__(self, x, theta):
        self.x = x
        self.centers = []  # 聚类中心矩阵
        self.theta = theta  # 生成新聚类中心可能性

    # 确定第一个聚类中心
    def init_center(self, cent=None):
        """
        cent=None 时 随机生成初始聚类中心 否则指定点
        """
        if cent == None:
            cent_index = np.random.choice(np.shape(self.x)[0])
        else:
            cent_index = cent
        self.centers.append(self.x[cent_index])

    # 确定第二个聚类中心
    def second_center(self):
        dists = n_one_dist(self.x, self.centers[0])
        cent_index = np.argmax(dists)
        self.centers.append(self.x[cent_index])

    # 计算各个样本与中心点的距离,返回距离矩阵
    def calc_dist(self)->np.ndarray:
        dists = np.zeros((self.x.shape[0], len(self.centers)))
        for i in range(self.x.shape[0]):
            dist = one_n_dict(self.x[i],  self.centers)
            # 选距离中心点小的距离作为结果
            dists[i] = dist
        return dists

    # 生成新的聚类中心,返回是否成功生成
    def new_center(self, dists: np.ndarray)->bool:
        # 按列取最小值
        dists = np.min(dists, axis=1)
        # 最大距离大于两个中心点之间的距离就生成新中心点
        # 这里加pow是因为我前面算距离都没有开平方
        if pow(max(dists), 0.5) >\
                self.theta*pow(one_one_dict(self.centers[0], self.centers[1]), 0.5):
            self.centers.append(self.x[np.argmax(dists)])
            return True
        else:
            return False

    # 当没有新的聚类中心之后,生成对应类别
    def make_ylabel(self, dists: np.ndarray)->list:
        index = np.argmin(dists, axis=1)
        return index

    def fit(self, cent=None):
        """
        cent=None 时 随机生成初始聚类中心 否则指定点
        """
        # 得到第一个聚类中心
        self.init_center(cent)
        # 得到第二个聚类中心
        self.second_center()
        # 迭代计算新的聚类中心
        distances = self.calc_dist()
        while self.new_center(distances) == True:
            distances = self.calc_dist()
        # 根据距离获得label
        return self.make_ylabel(distances)


if __name__ == "__main__":
    # 书上的例子
    data = [[0, 0], [3, 8], [2, 2], [1, 1], [5, 3],
            [4, 8], [6, 3], [5, 4], [6, 4], [7, 5]]
    data = np.array(data)
    mm = Maxmin(data, 0.5)
    y = mm.fit()
    plt.scatter(x=data[:, 0], y=data[:, 1], c=y)
    plt.show()

    # 西瓜书中的数据
    data, _, _ = createDataSet()
    data = np.array(data)
    mm1 = Maxmin(data, 0.5)
    y = mm1.fit()
    plt.scatter(x=data[:, 0], y=data[:, 1], c=y)
    plt.show()

效果