一、实验目的

1.理解聚类的过程

2.理解并掌握K-均值算法的过程

3.理解PCA算法进行降维的原理和步骤

二、实验内容及要求:

1.实验数据:iris数据,一共150个数据,每个数据包含4个特征,假设样本类别未知,但已知类别数为3。

2.实验要求

1)采用PCA的方式将原始特征进行降维,要求降维后的特征能够保留原始特征80%以上的信息;

2)将降维后的新特征在新的特征空间画出样本点;

3)采用K-均值算法对降维后的特征进行分类,并画出分类后的样本点(以不同的颜色进行表示);要求分别采用两种不同的初始代表点的确定方法,比较分类结果的不同;并与样本的真实类别进行比较,计算错误率。

4)对实验结果进行分析,分析初始代表点的不同对分类结果的影响。

5)使用python实现。

三、实验代码

# coding:utf-8
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA


def pplt(df, test_point=[0, 0], flag=0):
    fig = plt.figure()
    ax = fig.gca(projection='3d')

    ax.scatter(df[:45, 0], df[:45, 1], df[:45, 2], c='r', label="points 1")
    ax.scatter(df[45:90, 0], df[45:90, 1], df[45:90, 2], c='g', label="points 2")
    ax.scatter(df[90:135, 0], df[90:135, 1], df[90:135, 2], c='b', label="points 3")

    if flag == 1:
        ax.scatter(test_point[0], test_point[1], test_point[2], c='black', label='test_point')

    plt.legend()
    plt.show()




def pca(data, n_components):
    '''
    pca is O(D^3)
    :param data: (n_samples, n_features(D))    135*3
    :param n_dim: target dimensions
    :return: (n_samples, n_components)
    '''
    # 减去均值,也可以进行标准化处理(除以方差)
    data = data - np.mean(data, axis=0, keepdims=True)
    # print(data.shape)
    # 计算协方差矩阵
    # cov_matrix = 1 / m * np.matmul(data.T, data)
    cov_matrix = np.cov(data, rowvar = False)  # or
    # 计算协方差矩阵特征值及对于的特征向量
    eig_values, eig_vector = np.linalg.eig(cov_matrix) # 1*3  3*3
    # print(eig_values)
    # 对特征值进行排序,并取前n_components组
    indexs_ = np.argsort(-eig_values)[:n_components] # 将矩阵a按照axis(0列1行)排序,并返回排序后的下标  - 降序排

    picked_eig_values = eig_values[indexs_]
    picked_eig_vector = eig_vector[:, indexs_]  # 3*2
    # print(picked_eig_vector)
    data_ndim = np.matmul(data, picked_eig_vector)
    # print(data_ndim.shape)
    return data_ndim


# data 降维的矩阵(n_samples, n_features)
# n_dim 目标维度
# fit n_features >> n_samples, reduce cal


# 寻址每个样本其最近的中心点
def find_closest_centroids(X, centroids):
    m = X.shape[0]
    k = centroids.shape[0]
    idx = np.zeros(m)

    for i in range(m):
        min_dist = 1000000
        for j in range(k):
            # 计算每个训练样本和中心点的距离
            dist = np.sum((X[i, :] - centroids[j, :]) ** 2)
            if dist < min_dist:
                # 记录当前最短距离和其中心的索引值
                min_dist = dist
                idx[i] = j
    return idx


# 计算聚类中心
def compute_centroids(X, idx, k):
    m, n = X.shape
    centroids = np.zeros((k, n))

    for i in range(k):
        indices = np.where(idx == i) # X中归类到0 1 2 代表点的下标存入indices
        # print('indices',i,indices)
        # 计算下一个聚类中心,这里简单的将该类中心的所有数值求平均值作为新的类中心
        centroids[i, :] = (np.sum(X[indices, :], axis=1) / len(indices[0])) #.ravel() # 只返回原始数组的引用/视图
    # print(centroids.shape)
    return centroids


# 初始化聚类中心
def init_centroids(X, k):
    m, n = X.shape
    print('initial m,n,k',m,n,k)
    centroids = np.zeros((k, n))
    '''
    1)随机选择代表点 k个
    '''
    # 随机初始化 k 个 [0,m]的整数
    idx = np.random.randint(0, m, k)  # 0 - 150  步长k
    # print(centroids.shape)
    for i in range(k):
        centroids[i, :] = X[idx[i], :]
        # print(idx[i], i, centroids[i, :])
    '''
    2)用前c个样本点作为代表点
    '''
    # idx = np.random.randint(0, 50, 1)
    # centroids[0, :] = X[idx, :]
    # centroids[1, :] = X[idx+50, :]
    # centroids[2, :] = X[idx+100, :]
    print('centroids',centroids)
    return centroids

'''
    首先选择K个随机的点,称其为聚类中心
    对于数据集中的每一个数据,按照距离K个中心点的距离,将其与距离最近的中心点关联起来,与同一个中心点关联的所有点聚成一个类
    计算每一个组的平均值,将该组所关联的中心点移动到平均值的位置
    重复步骤2-3,直到中心点不再变化
    '''
# 实现 kmeans 算法
def run_k_means(X, initial_centroids, max_iters):
    '''
    :param X: 样本
    :param initial_centroids:聚类中心
    :param max_iters: 迭代次数
    :return:
    '''
    m, n = X.shape
    # 聚类中心的数目
    k = initial_centroids.shape[0]
    print('聚类中心的数目',k)
    idx = np.zeros(m)
    centroids = initial_centroids

    # print('centroids',centroids)
    for i in range(max_iters):
        idx = find_closest_centroids(X, centroids)
        # 聚类中心更新
        centroids = compute_centroids(X, idx, k)

    return idx, centroids
def defalt(real,pre,X):
    m= X.shape[0]
    right = 0
    for i in range(m):
        if real[i] == pre[i]+1:
            # print(real[i],pre[i])
            right += 1
    return right / m
if __name__ == "__main__":
    f_train1 = np.loadtxt("iris训练数据.txt", dtype=np.double, delimiter='\t', usecols=[0, 1, 2, 3, 4])  # 使用第123个特征
    f_train2 = np.loadtxt("iris测试数据.txt", dtype=np.double, delimiter='\t', usecols=[0, 1, 2, 3, 4])

    data = (np.r_[f_train1[0:25], f_train2[0:25], f_train1[25:50], f_train2[25:50], f_train1[50:75], f_train2[
                                                                                                        50:75]])  # 135* 4 里面np.r 是元组 转置后是矩阵
    lable = data[:, 0]

    # # print(test[0, :])
    # data = load_iris()
    # X = data.data
    # Y = data.target
    data_pca = pca(data[:, 1:5], 2)
    # data_2d1 = pca(X, 2)

    plt.figure(figsize=(8, 4))
    # plt.subplot(121)
    plt.title("after_PCA")
    plt.scatter(data_pca[:, 0], data_pca[:, 1], c=data[:, 0])
    # plt.scatter(data_2d1[:, 0], data_2d1[:, 1], c=Y)

    # sklearn_pca = PCA(n_components=2)
    # data_2d2 = sklearn_pca.fit_transform(train[:, 1:4])
    # data_2d2 = sklearn_pca.fit_transform(X)
    # plt.subplot(122)
    # plt.title("sklearn_PCA")
    # plt.scatter(data_2d2[:, 0], data_2d2[:, 1], c=train[:, 0])
    # plt.scatter(data_2d2[:, 0], data_2d2[:, 1], c=Y)
    # plt.show()

    X = data_pca
    initial_centroids = init_centroids(X, 3)
    # print(initial_centroids)
    # idx = find_closest_centroids(X, initial_centroids)
    # print(idx)

    # print(compute_centroids(X, idx, 3))

    idx, centroids = run_k_means(X, initial_centroids, 10)
    # print('idx',idx)

    right = defalt(lable,idx,X)
    print('正确率',right)
    # 可视化聚类结果
    cluster1 = X[np.where(idx == 0)[0], :]
    cluster2 = X[np.where(idx == 1)[0], :]
    cluster3 = X[np.where(idx == 2)[0], :]

    fig, ax = plt.subplots(figsize=(12, 8))
    ax.scatter(centroids[:, 0], centroids[:, 1], s=60, color='yellow', label='Point of Representation')
    ax.scatter(cluster1[:, 0], cluster1[:, 1], s=30, color='r', label='Cluster 1')
    ax.scatter(cluster2[:, 0], cluster2[:, 1], s=30, color='g', label='Cluster 2')
    ax.scatter(cluster3[:, 0], cluster3[:, 1], s=30, color='b', label='Cluster 3')
    ax.legend()
    plt.show()