引自百度百科。
Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。IRIS也称鸢尾花卉数据集,是一类多重变量分析的数据集。数据集包含150个数据样本,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。
IRIS以鸢尾花的特征作为数据来源,常用在分类操作中。该数据集由3种不同类型的鸢尾花的各50个样本数据构成。其中的一个种类与另外两个种类是线性可分离的,后两个种类是非线性可分离的。
IRIS数据读取及简单处理:
# 读数据,构建列表[[],[],[]]
def loadIRISdata(filename):
data=[]
with open(filename,'r') as f:
reader=csv.reader(f)
result=list(reader)
for i in range(1,151):
data.append(list(map(float,result[i][1:5])))
return data
但是要说一下哦,这个数据集还是太小了,150个样本勉强在统计学习方法中用一用,复杂算法的优势在更大的数据集上才能体现出优势。
核心思想: 这个比较简单,就是说先把所有的样本归为一类,然后根据相似性逐渐分解为多类。通过目标函数评价当前分类的质量。
核心代码:
# 分解聚类
def decomposition_clustering(data, assignments):
Er_max = float('-inf') # 每次最大的目标函数值
while True:
# 第一次,先分成两大类
place = 0
tag = 0
# 每次循环 计算把 xi 归入 1 类时的 E
for i in range(150):
if assignments[i] == 0: # xi 属于 0 类
assignments[i] = 1 # 把 xi 先归到 1 类
average_1 = avg(data, 0, assignments)
if length(1, assignments) == 0: # 如果 1 类还是空的
average_2 = np.array([0, 0, 0, 0])
else:
average_2 = avg(data, 1, assignments) # 计算完当前两类的均值,接下来算目标函数值
Er = E(150, length(0, assignments), length(1, assignments), average_1, average_2)
if Er > Er_max: # 记录当前最大的E和对应位置
place = i
Er_max = Er
tag = 1 # E未到极值
print('max_1:', Er_max)
assignments[i] = 0 # xi 回到 0 类,进入下次循环
if tag == 1:
assignments[place] = 1 # 把 使得 E最大的那个样本归到 1 类
else:
break
不过这是分为两类的程序,分成三类的话还得把while循环再执行一遍。
核心思想: c-均值聚类是典型的一种动态聚类算法,其基本思想是在每次迭代时,每个样本归为与之距离最近的那个聚类中心所属的类,全部归完类之后,重新计算新的聚类中心。接下来重复前面的步骤,直到聚类中心不再变化,聚类完成。
核心代码:
# 改进的c均值聚类方法
def c_means_2_clustering(data,assignments,c0,c1,c2):
# D :每个样本与每个聚类中心的欧指距离
D=[]
D_sample1=[]
D_sample2=[]
D_sample3=[]
for j in range(150):
D_sample1.append(eularDist(data[j],c0))
D_sample2.append(eularDist(data[j],c1))
D_sample3.append(eularDist(data[j],c2))
D.append(D_sample1)
D.append(D_sample2)
D.append(D_sample3)
D=np.array(D)
assignments=D.argmin(0)# 样本离哪个聚类中心最近,就归为哪一类
c0=centerGet(data,0,assignments)# 计算新的聚类中心
c1=centerGet(data,1,assignments)
c2=centerGet(data,2,assignments)
J=errorSquare(data,assignments,c0,c1,c2)
print(J) # 初始的误差平方和
c=[c0,c1,c2]
while True:
# 对每个聚合中的每个样本 计算 pii pij
J_ori=J
for i in range(150):
J_old = J
pij=[]
tag=samples_of_class(assignments)
pii=tag[assignments[i]]/(tag[assignments[i]]-1)*(eularDist(data[i],c[assignments[i]]))**2
for j in range(3):
if j!=assignments[i]:
pij.append(tag[j]/(tag[j]+1)*(eularDist(data[i],c[j]))**2)
if j==assignments[i]:
pij.append(float('inf'))
if pii>min(pij):
print('第',i,'个要换成第',np.array(pij).argmin(),'类')
assignments[i] = np.array(pij).argmin()
c[0]=centerGet(data,0,assignments)
c[1]=centerGet(data,1,assignments)
c[2]=centerGet(data,2,assignments)
J=J-pii+min(pij)
# J = errorSquare(data,assignments,c[0],c[1],c[2])
print('当前',J)
if J>=J_old:
# continue
break
if J>=J_ori:
break
return assignments
核心思想: c-均值聚类比较简单,在数据量较少、类别数较少的情况下可以有很好的效果。动态聚类还有另一种方法——ISODATA,这是一种基于迭代自组织的数据分析方法,它的核心思想是不断地考虑每个类是否需要和其他类合并,或者是这一类要不要分裂成其他类,这样在迭代中探索聚类中心最合理的数量,再去将所有样本归到相应的类中。相比于c-均值聚类算法,ISODATA要复杂得多,因为它设计了一套机制来调整聚类中心数量,但也因此能够更好地对混合数据进行聚类,而不需要准确知道需要初始聚类中心数目。
ISODATA中涉及到几个先验知识,假设预期的聚类中心数目为 ,然后判断某一类中的样本数量是否足以构成一个类,需要一个阈值 ,若某一类的样本数少于这个阈值,则它就要合并到其他类中去;某个类中的各个样本分布应该是尽可能紧密的,也就是说样本距离分布的标准差应该也要小于一个阈值 才能形成类;再有两个类的聚类中心之间的距离应该大于一个阈值 ,否则两类靠太近,需要进行合并。
核心代码:
# 分裂操作
def class_divide(data,c,tag,assignments,K,D_inclass,D_all,theta_n,theta_s,theta_c):
# 算标准偏差向量
xigma=[]
xigma_index=[]
for i in range(len(c)):
xigma_i=0
for j in range(150):
if assignments[j]==i:
xigma_i+=(np.array(data[j])-np.array(c[i]))**2
xigma.append(max(np.sqrt(xigma_i/tag[i])))
xigma_index.append(np.array(np.sqrt(xigma_i/tag[i])).argmax())
for i in range(len(c)):
if xigma[i]>theta_s and (len(c)<=K/2 or (D_inclass[i]>D_all and tag[i]>(2*theta_n+2))):
c[i][xigma_index[i]]+=0.5*xigma[i]
c_new=c[i]
c_new[xigma_index[i]]-=xigma[i]
c.append(c_new)
else:
# print(c)
c = class_combine(c,tag,theta_c)
return c
# 合并操作
def class_combine(c,tag,theta_c):
Dij=[]
Dij_index=[]
if len(c)<2:
return c
if len(c)==2:
DD=eularDist(c[0],c[1])
if DD>theta_c:
return c
else:
c[0]=(tag[0]*np.array(c[0])+tag[1]*np.array(c[1]))/(tag[0]+tag[1])
del c[1]
return c
for i in range(len(c)-1):
for j in range(i+1,len(c)):
Dij.append(eularDist(c[i],c[j]))
Dij_index.append([i,j])
if min(Dij)>theta_c:
return c
min_index=Dij_index[np.array(Dij).argmin()]
# print(min_index)
c[min_index[0]]=(tag[min_index[0]]*np.array(c[min_index[0]])+tag[min_index[1]]*np.array(c[min_index[1]]))/(tag[min_index[0]]+tag[min_index[1]])
del c[min_index[1]]
return c
还不会。
最后给出算准确率的代码:
def acc(result):
sum = 0
all = 0
for i in range(50):
if result[i][0] == 0:
sum += 1
all += 1
for i in range(50):
if result[i + 50][0] == 1:
sum += 1
all += 1
for i in range(50):
if result[i + 100][0] == 2:
sum += 1
all += 1
print('正确聚类的结果数量:', sum, '总数:', all)
return sum, all
完整的代码获取方式: