k-means聚类分析算法

   日期:2020-07-18     浏览:95    评论:0    
核心提示:先放编完大框架,解决了小的语法错误后的代码:此处为了方便差错和验证,使用了西瓜书教材的数据进行实验:import math# list可以是【里面放tuple】D = [(0.697,0.460), (0.774, 0.376), (0.634, 0.264), (0.609, 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,

在学完python基本语法,学习西瓜书k-means聚类分析算法原理后,根据西瓜书算法原理及伪码(p203)上手编写代码,既是熟悉算法的过程,也是熟悉python编程的过程。此处为了方便差错和验证,使用了西瓜书教材的数据进行实验
前面先放初步编完大框架,记录一下调试代码的过程和遇到的困难以及解决方法。文末贴了调试完成后的最后可直接运行的代码及结果,需要的读者可以直接略过中间部分跳至文末。
初步解决了一些小的语法错误后,可以成功运行的代码:

import math
# 使用list里面存放tuple的方式来存放样本数据集
D = [(0.697,0.460), (0.774, 0.376), (0.634, 0.264), (0.609, 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.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)]
# 选取3个样本作为均值向量
u1 = [0.403, 0.237]
u2 = [0.343, 0.099]
u3 = [0.478, 0.437]
# 令两个簇C1、C2为空,定义簇为list类型
c1 = []
c2 = []
c3 = []
# 方法:计算样本xj与各均值向量的距离(此处使用闵可夫斯基距离,当n等于2时为欧式距离)
def getDistancein_x_u(xfirst, xsecond, ufirst, usecond):
    distancin_x_u = math.sqrt(pow(abs(xfirst - ufirst), 2)+pow(abs(xsecond - usecond), 2))
    return distancin_x_u
# 定义样本与均值向量的距离
distancein_x_u = 0.0
# 主函数
for x in range(1,100):
    # 每次循环时将c1和c2清空
    c1 = []
    c2 = []
    c3 = []
    for element in D:
        distancein_x_u1 = getDistancein_x_u(element[0], element[1], u1[0], u1[1])
        distancein_x_u2 = getDistancein_x_u(element[0], element[1], u2[0], u2[1])
        distancein_x_u3 = getDistancein_x_u(element[0], element[1], u3[0], u3[1])
        # 根据最近的均值向量确定此样本的簇标记,并将该样本划入相应的簇。此处应使用一个排序算法,再改吧,先跑成再说
        if distancein_x_u1 > distancein_x_u2 and distancein_x_u1 > distancein_x_u3:
            c1.append((element[0], element[1]))
        elif distancein_x_u2 > distancein_x_u1 and distancein_x_u2 > distancein_x_u3:
            c2.append((element[0], element[1]))
        else:
            c3.append((element[0], element[1]))
    #此处for循环执行完毕以后,c1和c2里都塞满了
    #先保存当前均值向量,以便后面对比:
    model_u1 = u1
    model_u2 = u2
    model_u3 = u3
    #从c1、C2中求出新的均值向量,此处先定义变量为float类型
    distance_sum_x_1 = 0.0
    distance_sum_y_1 = 0.0
    distance_sum_x_2 = 0.0
    distance_sum_y_2 = 0.0
    n=0
    for newelement1 in c1:
        distance_sum_x_1 = distance_sum_x_1 + newelement1[0]
        distance_sum_y_1 = distance_sum_y_1 + newelement1[1]
        n = n+1
    u1 = [distance_sum_x_1/n, distance_sum_y_1/n]
    n=0
    for newelement2 in c2:
        distance_sum_x_2 = distance_sum_x_2 + newelement2[0]
        distance_sum_y_2 = distance_sum_y_2 + newelement2[1]
        n = n+1
    u2 = [distance_sum_x_1/n, distance_sum_y_1/n]
    n=0
    for newelement3 in c3:
        distance_sum_x_3 = distance_sum_x_3 + newelement1[0]
        distance_sum_y_3 = distance_sum_y_3 + newelement1[1]
        n=n+1
    u3 = [distance_sum_x_1/n, distance_sum_y_1/n]
    if u1 == model_u1 and u2 == model_u2 and u3 == model_u3:
        print("分类第一组:")
        for element_final1 in c1:
            print('('+element_final1[0]+','+element_final1[1])
        print("分类第二组:")
        for element_final2 in c2:
            print('('+element_final2[0]+','+element_final2[1])
        print("分类第三组:")
        for element_final3 in c3:
            print('(' + element_final3[0] + ',' + element_final3[1])
    break
print("分类第一组:")
for element_final1 in c1:
    print('(', element_final1[0], ',', element_final1[1], ')')
print("分类第二组:")
for element_final2 in c2:
    print('(', element_final2[0], ',', element_final2[1], ')')
print("分类第三组:")
for element_final3 in c3:
    print('(' + element_final3[0] + ',' + element_final3[1])

1.遇到ZeroDivisionError: float division by zero在51行,被除数不能为0

 for newelement1 in c1:
        distance_sum_x_1 = distance_sum_x_1 + newelement1[0]
        distance_sum_y_1 = distance_sum_y_1 + newelement1[1]
        n = n+1
    u1 = [distance_sum_x_1/n, distance_sum_y_1/n]

第一个想法是此处c1可能在第一次分类结束后并未获得任何元素,那么newelement1中一个元素都没有,甚至无法遍历它,那么前面一定出了一些问题,发现在31行处大于小于号搞错了。
2.在测试不同的迭代次数算法的分类结果时,发现无论设置循环几次,结果都毫无变化,于是设立了一个参数用来监控算法的迭代次数,结果发现惊人的:

发现问题是这里的错误,很低级的错误

for x in range(1, 100):

x为空无法遍历x,还是对python的for循环不太熟悉,此处改为while循环:

while y<50:

依然只迭代了一次,看是不是第一轮便执行了两次前后对比均值向量的if语句直接break了
并没有。
问题出在这里

    if u1 == model_u1 and u2 == model_u2 and u3 == model_u3:
        print("分类第一组:")
        for element_final1 in c1:
            print('('+element_final1[0]+','+element_final1[1])
        print("分类第二组:")
        for element_final2 in c2:
            print('('+element_final2[0]+','+element_final2[1])
        print("分类第三组:")
        for element_final3 in c3:
            print('(' + element_final3[0] + ',' + element_final3[1])
        print("执行了if语句,直接跳出")
    break

break的位置错误,本来是要在if语句中break,现在直接在外层直接把本层循环跳出了
3.又遇到被除数为0的问题:
可以添加一个if-else判断来解决这个问题

    if n == 0:
        print("C1被除数不能为0")
    else:
        u1 = [distance_sum_x_1/n, distance_sum_y_1/n]

4.解决了上个错误后,分别测试1/2/3/4轮迭代结果,发现至第4轮后c2、c3簇完全没有了,全部到了c1簇里,和没有分一样。
单独测试了第一轮迭代没有问题,结果集和西瓜书一模一样

 # 测试第一轮后遍历一下c1、c2、c3:
    print('第一轮簇的测试')
    print('测试C1')
    for test1 in c1:
        print(test1[0],',',test1[1])
    print('测试C2')
    for test2 in c2:
        print(test2[0], ',', test2[1])
    print('测试C3')
    for test3 in c3:
        print(test3[0], ',', test3[1])


在计算均值向量处测试了计算出的新的均值向量:

    for newelement1 in c1:
        n = n + 1
        distance_sum_x_1 = distance_sum_x_1 + newelement1[0]
        distance_sum_y_1 = distance_sum_y_1 + newelement1[1]
    if n == 0:
        print("C1被除数不能为0")
    else:
        u1 = [distance_sum_x_1/n, distance_sum_y_1/n]
    # 输出一下当前轮的均值向量
    print('第', y, '轮均值向量1:', u1[0], u1[1])

测试结果发现均值向量计算出的结果u2、u3偏差到不知道哪里了!

是计算方法的问题,明天下午继续更新
晚上躺下看代码找到了错误,u1数据正确,但是u2,u3偏差很大,看计算新的均值向量的代码部分

    if n == 0:
        print("C1被除数不能为0")
    else:
        u2 = [distance_sum_x_1/n, distance_sum_y_1/n]

应该是x_1,和y_1,这边直接复制的,低级错误
改正以后再跑结果正确,与西瓜书测试结果相符。

最后粘出最后运行代码,读者可以直接复制运行:

import math
# 使用list里面存放tuple的方式来存放样本数据集
D = [(0.697, 0.460), (0.774, 0.376), (0.634, 0.264), (0.609, 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.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)]
# 选取3个样本作为均值向量
u1 = [0.403, 0.237]
u2 = [0.343, 0.099]
u3 = [0.478, 0.437]
# 令两个簇C1、C2为空,定义簇为list类型
c1 = []
c2 = []
c3 = []
# 方法:计算样本xj与各均值向量的距离(此处使用闵可夫斯基距离,当n等于2时为欧式距离)
def getDistancein_x_u(xfirst, xsecond, ufirst, usecond):
    distancin_x_u = math.sqrt(pow(abs(xfirst - ufirst), 2)+pow(abs(xsecond - usecond), 2))
    return distancin_x_u
# 定义样本与均值向量的距离
distancein_x_u = 0.0
# 主函数
y = 0
while y < 20:
    # 每次循环时将c1和c2清空
    c1 = []
    c2 = []
    c3 = []
    for element in D:
        distancein_x_u1 = getDistancein_x_u(element[0], element[1], u1[0], u1[1])
        distancein_x_u2 = getDistancein_x_u(element[0], element[1], u2[0], u2[1])
        distancein_x_u3 = getDistancein_x_u(element[0], element[1], u3[0], u3[1])
        # 根据最近的均值向量确定此样本的簇标记,并将该样本划入相应的簇。此处应使用一个排序算法,再改吧,先跑成再说
        if distancein_x_u1 < distancein_x_u2 and distancein_x_u1 < distancein_x_u3:
            c1.append((element[0], element[1]))
        elif distancein_x_u2 < distancein_x_u1 and distancein_x_u2 < distancein_x_u3:
            c2.append((element[0], element[1]))
        else:
            c3.append((element[0], element[1]))
    # 此处for循环执行完毕以后,c1和c2里都塞满了
    # 先保存当前均值向量,以便后面对比:
    model_u1 = u1
    model_u2 = u2
    model_u3 = u3
    # 从c1、C2中求出新的均值向量,先定义新的均值向量变量为float类型
    distance_sum_x_1 = 0.0
    distance_sum_y_1 = 0.0
    distance_sum_x_2 = 0.0
    distance_sum_y_2 = 0.0
    distance_sum_x_3 = 0.0
    distance_sum_y_3 = 0.0
    n=0
    for newelement1 in c1:
        n = n + 1
        distance_sum_x_1 = distance_sum_x_1 + newelement1[0]
        distance_sum_y_1 = distance_sum_y_1 + newelement1[1]
    if n == 0:
        print("C1被除数不能为0")
    else:
        u1 = [distance_sum_x_1/n, distance_sum_y_1/n]
    n=0
    for newelement2 in c2:
        n = n + 1
        distance_sum_x_2 = distance_sum_x_2 + newelement2[0]
        distance_sum_y_2 = distance_sum_y_2 + newelement2[1]
    if n == 0:
        print("C1被除数不能为0")
    else:
        u2 = [distance_sum_x_2/n, distance_sum_y_2/n]
    n=0
    for newelement3 in c3:
        n = n + 1
        distance_sum_x_3 = distance_sum_x_3 + newelement3[0]
        distance_sum_y_3 = distance_sum_y_3 + newelement3[1]
    if n == 0:
        print("C1被除数不能为0")
    else:
        u3 = [distance_sum_x_3/n, distance_sum_y_3/n]
    # 如果新求出的均值向量和上次的相同,那么下次分配簇时使用的与均值向量的距离算下和这次一样,就可以结束了
    if u1 == model_u1 and u2 == model_u2 and u3 == model_u3:
        print("分类第一组:")
        for element_final1 in c1:
            print('(', element_final1[0], ',', element_final1[1], ')')
        print("分类第二组:")
        for element_final2 in c2:
            print('(', element_final2[0], ',', element_final2[1], ')')
        print("分类第三组:")
        for element_final3 in c3:
            print('(', element_final3[0], ',', element_final3[1], ')')
        break
    y = y+1
 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服