几种聚类算法在IRIS数据集上的应用(python)

分解聚类、C均值聚类及其改进、ISODATA应用在IRIS数据集的表现

  • IRIS数据集
  • 分解聚类
  • C均值聚类
  • ISODATA
  • K均值聚类

这个其实是之前的一次课程作业,要求是对鸢尾花数据集IRIS做聚类分析,课上讲了几种算法:系统,分解,c均值及其改进,ISODATA,这里分享一下自己的思路与代码。不过感觉提到聚类首先想到的是k均值、meanshift这些算法,可能课堂上讲的都是些最经典最基础的吧。原理方面不会讲太多,文中会给出核心代码。完整代码及数据集获取方式见文末。

IRIS数据集

引自百度百科。
Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。IRIS也称鸢尾花卉数据集,是一类多重变量分析的数据集。数据集包含150个数据样本,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。

IRIS以鸢尾花的特征作为数据来源,常用在分类操作中。该数据集由3种不同类型的鸢尾花的各50个样本数据构成。其中的一个种类与另外两个种类是线性可分离的,后两个种类是非线性可分离的。
几种聚类算法在IRIS数据集上的应用(python)_第1张图片
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个样本勉强在统计学习方法中用一用,复杂算法的优势在更大的数据集上才能体现出优势。

分解聚类

核心思想: 这个比较简单,就是说先把所有的样本归为一类,然后根据相似性逐渐分解为多类。通过目标函数评价当前分类的质量。
几种聚类算法在IRIS数据集上的应用(python)_第2张图片
几种聚类算法在IRIS数据集上的应用(python)_第3张图片

核心代码:

# 分解聚类
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-均值聚类是典型的一种动态聚类算法,其基本思想是在每次迭代时,每个样本归为与之距离最近的那个聚类中心所属的类,全部归完类之后,重新计算新的聚类中心。接下来重复前面的步骤,直到聚类中心不再变化,聚类完成。

核心代码:

# 改进的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

ISODATA

核心思想: 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

K均值聚类

还不会。

最后给出算准确率的代码:

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

完整的代码获取方式:

  1. 用积分换https://download.csdn.net/download/luodaxia_ttt/12569112
  2. 点赞收藏,然后评论区给邮箱,我看到的话会发邮件,不过可能比较慢。

你可能感兴趣的:(巨人的肩膀,聚类,算法,python,鸢尾花数据集)