每篇一句:
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的聚类中心个数变化。
算法简介:
基本思路:
选择初始值——包括若干聚类中心及一些指标。可在迭代运算过程中人为修改,据此将N个模式样本分配到各个聚类中心中心去。
按最近邻规则进行分类。
- 聚类后的处理:计算各类中的距离函数等指标,按照给定的要求,将前次获得的聚类进行分裂或合并处理,以获得新的聚类中心,即调整聚类中心的个数。
- 判断结果是否符合要求:
- 符合:结束;
- 不符合:回到(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。
第四步: 修正各聚类中心值。
第五步:计算Sj类的类内平均距离Dj。
第六步:计算总体平均距离 Dj,即全部样本到各自聚类中心距离的平均距离。
第七步:判决是进行分裂还是进行合并,决定迭代步骤等。
如迭代已达I次(最后一次),置θc=0,跳到第十一步(合并)。
若Nc <= K/2,即聚类中心小于或等于希望数的一半,进入第八步(分裂)。
- 如果迭代的次数是偶数,或Nc>=2K,即聚类中心数目大于或等于希望数的两倍,则跳到第十一步(合并)。否则进入第八步(分裂)。
第八步:计算每个聚类中样本距离的标准差向量。对Sj类有:
第九步:求每个标准差向量的最大分量。σj的最大分量记为:σjmax,j = 1,2,…,Nc。
第十步:在最大分量集{σjmax,j = 1,2,…,Nc}中,如有σjmax > θs,说明Sj类样本在对应方向上的标准差大于允许的值,此时,又满足以下两个条件之一:
则将Zj分裂成两个新的聚类中心Zj+和Zj-,并且Nc加1。其中:
若完成了分裂运算,迭代次数加1,跳回第二步;否则,继续。
第十一步:计算所有聚类中心之间的距离。Si类和Sj类中心间的距离为
Dij = || Zi – Zj || i = 1,2,…Nc-1 j = i+1,…,Nc
- 第十二步:比较所有Dij与θc的值,将小于θc的Dij按升序排列
{Di1j1, Di2j2, …, DiLjL}
第十三步:如果将距离为Diljl的两类合并,得到新的聚类中心为:
每合并一对,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聚类算法