聚类算法——ISODATA聚类算法

每篇一句:

Some things are not to see to insist, but insisted the will sees hope.

ISODATA聚类算法:

  • 迭代自组织的数据分析算法(iterative self-organizing data analysis techniques algorithm, ISODATA):

    • 算法特点:

      • 加入了试探性步骤,组成人机交互的结构;

      • 可以通过类的自动合并与分裂得到较合理的类别数。

    • 与K-均值算法的比较:

      • 相似:聚类中心的位置均通过样本均值的迭代运算决定。

      • 相异:K-均值算法的聚类中心个数不变;ISODATA的聚类中心个数变化。

  • 算法简介:

    基本思路:

    1. 选择初始值——包括若干聚类中心及一些指标。可在迭代运算过程中人为修改,据此将N个模式样本分配到各个聚类中心中心去。

    2. 按最近邻规则进行分类。

    3. 聚类后的处理:计算各类中的距离函数等指标,按照给定的要求,将前次获得的聚类进行分裂或合并处理,以获得新的聚类中心,即调整聚类中心的个数。
    4. 判断结果是否符合要求:
      • 符合:结束;
      • 不符合:回到(2)

    算法共分十四步:

    • 第一~六步:预选参数,进行初始分类。为合并和分裂准备必要的数据。

    • 第七步:决定下一步是进行合并还是分裂。

    • 第八~十步:分裂算法。

    • 第十一~十三步:合并算法。

    • 第十四步:决定算法是否结束。

  • 算法描述:

    设有N个模式样本X1,X2,… ,Xn。

    • 第一步:预选Nc个聚类中心{Z1,Z2,… ,ZNc},Nc也是聚类过程中实际的聚类中心个数。预选指标:

      • K:希望的聚类中心的数目。

      • θn:每个聚类中应具有的最少样本数。若少于 θn,则该类不能作为一个独立的聚类,应删去。

      • θs:一个聚类域中样本距离分布的标准差阈值。标准差向量的每一分量反映样本在特征空间的相应维上,与聚类中心的位置偏差(分散程度)。要求每一聚类内,其所有分量中的最大分量应小于 θs ,否则该类将被分裂为两类。

      • θc:两聚类中心之间的最小距离。若两类中心之间距离小于θc ,则合并为一类。

      • L:在一次迭代中允许合并的聚类中心的最大对数。

      • I:允许迭代的次数。

    • 第二步: 把N个样本按最近邻规则分配到Nc个聚类中。
        若

      || X- Zj || = min{|| X – Zi ||, i = 1,2,…,Nc}

        则

      X ∈ Sj

    • 第三步:若Sj中的样本数Nj<θn,则取消该类,并且Nc减去1。

    • 第四步: 修正各聚类中心值。

      《聚类算法——ISODATA聚类算法》

    • 第五步:计算Sj类的类内平均距离Dj。

      《聚类算法——ISODATA聚类算法》

    • 第六步:计算总体平均距离 Dj,即全部样本到各自聚类中心距离的平均距离。

      《聚类算法——ISODATA聚类算法》

    • 第七步:判决是进行分裂还是进行合并,决定迭代步骤等。

      • 如迭代已达I次(最后一次),置θc=0,跳到第十一步(合并)。

      • 若Nc <= K/2,即聚类中心小于或等于希望数的一半,进入第八步(分裂)。

      • 如果迭代的次数是偶数,或Nc>=2K,即聚类中心数目大于或等于希望数的两倍,则跳到第十一步(合并)。否则进入第八步(分裂)。
    • 第八步:计算每个聚类中样本距离的标准差向量。对Sj类有:

      《聚类算法——ISODATA聚类算法》

    • 第九步:求每个标准差向量的最大分量。σj的最大分量记为:σjmax,j = 1,2,…,Nc。

    • 第十步:在最大分量集{σjmax,j = 1,2,…,Nc}中,如有σjmax > θs,说明Sj类样本在对应方向上的标准差大于允许的值,此时,又满足以下两个条件之一:

      《聚类算法——ISODATA聚类算法》

      则将Zj分裂成两个新的聚类中心Zj+和Zj-,并且Nc加1。其中:

      《聚类算法——ISODATA聚类算法》

      若完成了分裂运算,迭代次数加1,跳回第二步;否则,继续。

    • 第十一步:计算所有聚类中心之间的距离。Si类和Sj类中心间的距离为

      Dij = || Zi – Zj ||  i = 1,2,…Nc-1 j = i+1,…,Nc

    • 第十二步:比较所有Dij与θc的值,将小于θc的Dij按升序排列

    {Di1j1, Di2j2, …, DiLjL}

    • 第十三步:如果将距离为Diljl的两类合并,得到新的聚类中心为:

      《聚类算法——ISODATA聚类算法》

      每合并一对,Nc减1。

    • 第十四步: 若是最后一次运算(迭代次数为I),算法结束。否则:

      • 需要由操作者修改输入参数时(试探性步骤),跳到第一步;
      • 输入参数不需改变时,跳到第二步。

      此时,选择两者之一,迭代次数加1,然后继续进行运算。

Python实现:

  • 解释说明见代码中注释
# coding=utf8

# initial_set为初始设置,依次代表如下:
# nc : 预选nc个聚类中心
# K:希望的聚类中心个数
# min_num:每个聚类中最少样本数
# s:聚类域中样本的标准差阈值
# c:两聚类中心之间的最短距离
# L:在一次迭代中允许合并的聚类中心的最大对数
# I:允许迭代的次数
# k:分裂系数

import math

from k_means_cluster import classify, get_newcluster
from max_min_cluster import get_distance


class IsoData(object):

    def __init__(self, initial_set, data):
        # initial_set 为初始设置集合,依次为nc,K,min_num,s,c,L,I,k
        self.nc = initial_set[0]
        self.K = initial_set[1]
        self.min_num = initial_set[2]
        self.s = initial_set[3]
        self.c = initial_set[4]
        self.L = initial_set[5]
        self.I = initial_set[6]
        self.k = initial_set[7]

        self.current_i = 1  # 目前迭代的次数
        self.data = data  # 数据集
        self.cluster_center = []  # 聚类中心list
        self.result = []  # 聚类结果
        self.inner_mean_distance = []  # 类内平均距离
        self.all_mean_distance = 0  # 全部样本的总体平均距离

        self.step1()

    def step1(self):

        # 预选nc个聚类中心

        for i in range(self.nc):
            self.cluster_center.append(self.data[i])
            self.result.append([self.data[i]])
        self.step2()

    def step2(self):

        # 最近邻规则分类

        # self.result = []
        self.result = classify(self.data, self.cluster_center)
        self.step3()

    def step3(self):

        # 判断Sj中样本数Nj是否小于min_num

        for i in range(len(self.result)):
            if len(self.result[i]) < self.min_num:
                self.result.pop(i)
                self.cluster_center.pop(i)
                self.nc -= 1
        self.step4()

    def step4(self):

        # 修正各聚类中心值

        self.cluster_center = get_newcluster(self.result)
        self.step5_6()

    def step5_6(self):

        # 计算类内平均距离以及全部样本的总体平均距离

        for i in range(len(self.result)):
            inner_dis = 0
            for j in range(len(self.result[i])):
                inner_dis += get_distance(self.result[i][j], self.cluster_center[i])
            self.all_mean_distance += inner_dis
            inner_dis = inner_dis / len(self.result[i])
            self.inner_mean_distance.append(inner_dis)
        self.all_mean_distance = self.all_mean_distance / len(self.data)
        self.step7()

    def step7(self):

        # 判断是进行分裂还是合并

        if self.current_i is self.I:
            self.c = 0
            self.step11_12_13_14()
        elif self.nc <= self.K / 2:
            self.step8_9_10()
        elif (self.current_i % 2 is 0) or self.nc >= 2 * self.K:
            self.step11_12_13_14()

    def step8_9_10(self):

        # 第八、九、十步:计算每个聚类中样本的标准差向量;求每个标准差向量的最大分量;
        # 在最大分量集中,根据条件判断下一步

        index_list = [0 for i in range(len(self.result))]  # 对应最大分量的分量序号
        max_standard_dev = [0 for i in range(len(self.result))]  # 最大分量集

        for i in range(len(self.result)):
            temp = 0
            # 记录标准差向量的最大分量及对应分量序号
            max_index = 0
            index = 0

            for j in range(len(self.data[0])):
                for k in range(len(self.result[i])):
                    temp += math.pow(self.result[i][k][j]- self.cluster_center[i][j], 2)
                temp = math.sqrt(temp / len(self.result[i]))
                if temp > max_index:
                    max_index = temp
                    index = j
                index_list[i] = index
                max_standard_dev[i] = max_index

        # 第十步:判断
        for i in range(len(max_standard_dev)):
            new_cluster1 = [0 for m in range(len(self.data[0]))]
            new_cluster2 = [0 for m in range(len(self.data[0]))]
            if max_standard_dev[i] > self.s and \
                    ((self.inner_mean_distance[i] > self.all_mean_distance and len(self.result[i]) > 2 * (self.min_num + 1)) or self.nc <= self.K / 2):
                for j in range(len(self.cluster_center[i])):
                    if j is not index_list[i]:
                        new_cluster1[j] = self.cluster_center[i][j]
                        new_cluster2[j] = self.cluster_center[i][j]
                    else:
                        new_cluster1[j] = self.cluster_center[i][index_list[i]] + self.k * max_standard_dev[i]
                        new_cluster2[j] = self.cluster_center[i][index_list[i]] - self.k * max_standard_dev[i]
                self.cluster_center.append(new_cluster1)
                self.cluster_center.append(new_cluster2)
                self.cluster_center.pop(i)
                self.current_i += 1
                self.nc += 1
                self.step2()
            else:
                self.step11_12_13_14()

    def step11_12_13_14(self):

        # 第十一、十二步:计算所有聚类中心之间的距离,并将小于c的值存入list按升序排列
        # 储存小于c的距离以及对应聚类中心下标的map

        dis_index = {}
        for i in range(1, self.nc -1):
            for j in range(i+1, self.nc):
                indexes = [0, 0]  # 下标数组
                dis_temp = get_distance(self.cluster_center[i], self.cluster_center[j])
                if dis_temp < self.c:
                    indexes[0] = i
                    indexes[1] = j
                    dis_index[dis_temp] = indexes

        # 距离按升序排列后的list
        dis_list = dis_index.keys()
        # 第十三步:合并
        for i in range(self.L):
            # 即将合并的聚类的标号
            index = dis_index.get(dis_list[i])
            # 已经合并过的聚类的标号
            already_index = []
            # 判断是否合并过
            if not (index[0] in already_index  or index[1] in already_index):
                # 新聚类中心
                new_cluster = [0 for i in range(len(self.data[0]))]
                for j in range(len(new_cluster)):
                    new_cluster[j] = (len(self.result[index[0]]) * self.cluster_center[index[0]][j]
                                      + len(self.result[index[1]]) * self.cluster_center[index[1]][j]) \
                                     / (len(self.result[index[0]]) + len(self.result[index[j]]))
                self.cluster_center.append(new_cluster)
                self.cluster_center.pop(index[0])
                self.cluster_center.pop(index[1])
                already_index.append(index[0])
                already_index.append(index[1])
                self.nc -= 1

        # 第十四步:判断
        if self.current_i is not self.I:
            flag = raw_input("是否需要修改参数,输入“y”确认修改,“n”取消\n")
            if flag == "y":
                self.nc = int(raw_input("nc:"))
                self.K = int(raw_input("K:"))
                self.min_num = int(raw_input("min_num:"))
                self.s = float(raw_input("s:"))
                self.c = float(raw_input("c:"))
                self.L = int(raw_input("L:"))
                self.I = int(raw_input("I:"))
                self.k = float(raw_input("k:"))
                self.current_i += 1
                self.step1()
            else:
                self.current_i += 1
                self.step2()
# 测试ISODATA
# data = [[0, 0], [1, 1], [2, 2], [4, 3], [5, 3], [4, 4], [5, 4], [6, 5]]
# initial_set = [1, 2, 1, 1, 4, 0, 4, 0.5]
# 
# isodata = IsoData(initial_set, data)
# result = isodata.result
# 
# for i in range(len(result)):
# print "----------第" + str(i+1) + "个聚类----------"
# print result[i]

# 结果:
# C:\Python27\python.exe C:/Users/14344/Cluster_Algorithm/test.py
# 是否需要修改参数,输入“y”确认修改,“n”取消
# n
# ----------第1个聚类----------
# [[4, 3], [5, 3], [4, 4], [5, 4], [6, 5]]
# ----------第2个聚类----------
# [[0, 0], [1, 1], [2, 2]]
# 

最后:

本文简单的介绍了 聚类算法——ISODATA聚类算法 中的相关内容,以及相应的代码实现。此算法较为复杂,加之本人代码水平有限,所以算法的实现过程或多或少步骤复杂或存在缺陷,有问题的地方欢迎大家指出。

代码地址:聚类算法——ISODATA聚类算法

    原文作者:聚类算法
    原文地址: https://blog.csdn.net/Ha_hha/article/details/79412588
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞