Python机器学习实验课K-means聚类代码和注释

数据集为鸢尾花数据集,特征有4个,类别有3类,分为训练集和测试集,进行K-means聚类,以学习K-means聚类算法。

import operator
import csv
import numpy as np
import random

names = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'] #类别
train_lables = []
test_lables = []

##数据集读取和划分
def loaddata(filename1, filename2, train_set=[], test_set=[]):

    with open(filename1, 'r') as f:
        lines = csv.reader(f)
        train_set = list(lines)#训练数据列表
        for i in range(len(train_set)):
            del(train_set[i][0])#删除每行的第0列,也就是标号
            #为训练数据添加标签
            if train_set[i][-1] == names[0]:
                train_lables.append(0)
            elif train_set[i][-1] == names[1]:
                train_lables.append(1)
            else:
                train_lables.append(2)
            del(train_set[i][-1])#删除原训练列表中的标签,因为已经有了标签列表
            for j in range(4): #因为有4个特征,把每一个特征都变成float类型
                train_set[i][j] = float(train_set[i][j])
            train_set[i] = np.array(train_set[i]) #把训练集的特征列表变成numpy数组

    with open(filename2, 'r') as f:#测试集同上
        lines = csv.reader(f)
        test_set = list(lines)
        for i in range(len(test_set)):
            del(test_set[i][0])
            if test_set[i][-1] == names[0]:
                test_lables.append(0)
            elif test_set[i][-1] == names[1]:
                test_lables.append(1)
            else:
                test_lables.append(2)
            del(test_set[i][-1])
            for j in range(4):
                test_set[i][j] = float(test_set[i][j])
            test_set[i] = np.array(test_set[i])


    return train_set, test_set 

#计算聚类中心坐标和相应的数据,参数:现在的聚类中心数据,要聚成的类别数
def get_core(core, k):
    newcore = [] #新的聚类中心坐标
    newcoredata = [] #聚类中心对应的数据
    for i in range(k): #初始化,每个聚类中心都还没对应数据
        newcoredata.append([])

    for i in range(len(train_set)):
        distance = []
        for j in range(k):
            distance.append(np.linalg.norm(train_set[i] - core[j], ord=None, axis=None, keepdims=False))
            #np.linalg.norm:求范数,参数1:矩阵,4;参数2:范数类型,ord=None是默认情况,求整个矩阵元素平方和再开根号;
            #参数3:axis=None表示矩阵范数;参数4:keepdims=False,表示不保持矩阵的二维特性
            #这里就是计算一个训练样本到每个聚类中心的距离
        newcoredata[distance.index(min(distance))].append(i)#选择最小的距离的索引,也就定位了是哪个聚类中心,将此样本的索引加入到该聚类中心对应的样本索引列表中

    for i in range(k):
        temp = np.zeros(4)
        for j in newcoredata[i]: #最终得到的temp是每个类的总距离
            temp += train_set[j]
        newcore.append(temp / len(newcoredata[i]))#每个,类的平均距离

    return newcore, newcoredata

#kmeans计算
def kmeans(k):

    core = [] #聚类中心的索引号

    while len(core) < 3: #当聚类中心还不到3个时,
        x = random.randint(0, len(train_set)) #随机选取训练集中的一个样本,
        if x not in core:#如果选的这个样本与之前选的不重复,就把它添加到聚类中心列表
            core.append(x)

    now_core = [train_set[i] for i in core] #记录聚类中心本身的内容(坐标)
    print(now_core)#打印结果:[array([5.1, 3.5, 1.4, 0.2]), array([7.7, 3. , 6.1, 2.3]), array([5.3, 3.7, 1.5, 0.2])]

    for i in range(10):#kmeans聚类迭代10次
        new_core, newcoredata = get_core(now_core, k) #计算新的聚类中心的坐标和相应的数据

        print(new_core, newcoredata)
        #打印新的聚类中心的坐标和对应的数据(样本索引号)
        #[array([4.85384615, 3.21153846, 1.49615385, 0.25      ]), array([6.43269231, 2.99230769, 5.15576923, 1.80192308]), array([5.44 , 3.43 , 2.345, 0.62 ])]
        #[[0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 14, 19, 20, 21, 22, 23, 24, 27, 29, 30, 31, 32, 33, 34, 37, 60], [38, 39, 41, 42, 43, 44, 46, 48, 49, 50, 51, 52, 53, 54, 56, 57, 61, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97], [8, 11, 12, 13, 15, 16, 17, 18, 25, 26, 28, 35, 36, 40, 45, 47, 55, 58, 59, 65]]

        dis = 0
        for i in range(k):
            dis += np.linalg.norm(new_core[i] - now_core[i], ord=None, axis=None, keepdims=False) #计算新的聚类中心和现在的聚类中心的距离
        if dis >= 0.1:#如果总距离>=0.1,更新聚类中心,继续迭代;否则迭代结束
            now_core = new_core
        else:
            break

    return now_core, newcoredata #返回聚类中心的坐标和每个聚类中心对应数据的索引

#判断聚类中的一个类是哪个具体的类别
def get_response(neighbors):
    class_vote = {}#空的字典
    for i in range(len(neighbors)):
        response = neighbors[i]
        #统计某个类别的样本数量
        if response not in class_vote:
            class_vote[response] = 1#计数
        else:
            class_vote[response] += 1#计数
    softed_vote = sorted(class_vote.items(), key=operator.itemgetter(1), reverse=True)#降序排序
    return softed_vote[0][0] #得出了对应样本数量最多的类别,所以这一簇就是这个类

#预测
def predection(test_data, core_label):

    result = []
    for i in range(len(test_data)):
        distance = []
        for j in range(k):
            distance.append(np.linalg.norm(test_data[i] - core[j], ord=None, axis=None, keepdims=False)) #测试样本和某个聚类中心的距离
        result.append(core_label[distance.index(min(distance))]) #距离哪个聚类中心距离最小则属于哪个类别

    return result#预测结果


if __name__ == "__main__":
    k = 3
    #数据集读取和划分
    train_set, test_set = loaddata('data/data42984/iris_train.csv', 'data/data42984/iris_test.csv') #得到训练集和测试集,均为numpy.array

    #kmeans计算
    core, data = kmeans(k)#得到聚类中心的坐标和每个聚类中心对应数据的索引

    core_label = []

    for i in range(k):
        temp = [train_lables[j] for j in data[i]] #得到的temp为一个聚类中心对应数据的标签
        core_label.append(get_response(temp))#get_response(temp)判断聚类中的一个类是哪个具体的类别,依次把具体的类别加入到core_label中。

    print(core_label) #打印:[0, 2, 1]

    #预测
    result = predection(test_set, core_label)

    print(test_lables) #打印:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
    print(result)      #打印:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2]


    acc_num = 0 #所有正确预测的数量
    for i in range(len(test_lables)):
        if result[i] == test_lables[i]:
            acc_num += 1

    print(acc_num/len(test_lables))#打印:0.8846153846153846

你可能感兴趣的:(python,机器学习)