头歌数据挖掘算法原理与实践:k-均值

第1关:什么是质心

根据提示,在右侧编辑器 Begin-End 部分补充代码,计算样本间距离 distance(x, y, p=2) 方法:

x:第一个样本的坐标
y:第二个样本的坐标
p:等于1时为曼哈顿距离,等于 2 时为欧氏距离
构造计算所有样本质心的方法 cal_Cmass(data):

data:数据样本
与将所有样本到质心距离按从小到大排序的方法 sorted_list(data,Cmass):

data:数据样本
Cmass:数据样本质心

import numpy as np

#计算样本间距离
def distance(x, y, p=2):
    '''
    input:x(ndarray):第一个样本的坐标
          y(ndarray):第二个样本的坐标
          p(int):等于1时为曼哈顿距离,等于2时为欧氏距离
    output:distance(float):x到y的距离      
    '''
    #********* Begin *********#    
    if p == 1:
        # 曼哈顿距离(Manhattan distance)
        return np.sum(np.abs(x - y))
    elif p == 2:
        # 欧氏距离(Euclidean distance)
        return np.sqrt(np.sum((x - y) ** 2))
    else:
        # 通用闵可夫斯基距离(Minkowski distance)
        return np.power(np.sum(np.power(np.abs(x - y), p)), 1/p)
    #********* End *********#

#计算质心
def cal_Cmass(data):
    '''
    input:data(ndarray):数据样本
    output:mass(ndarray):数据样本质心
    '''
    #********* Begin *********#
    # 质心是所有样本坐标的平均值
    Cmass = np.mean(data, axis=0)
    #********* End *********#
    return Cmass

#计算每个样本到质心的距离,并按照从小到大的顺序排列
def sorted_list(data, Cmass):
    '''
    input:data(ndarray):数据样本
          Cmass(ndarray):数据样本质心
    output:dis_list(list):排好序的样本到质心距离
    '''
    #********* Begin *********#
    # 创建空列表存储每个样本到质心的距离
    dis_list = []
    
    # 计算每个样本到质心的距离
    for i in range(len(data)):
        dis = distance(data[i], Cmass)
        dis_list.append(dis)
    
    # 按照从小到大的顺序排列
    dis_list.sort()
    #********* End *********#
    return dis_list

第二关:动手实现 k- 均值

根据提示,在右侧编辑器 Begin-End 部分补充代码,实现 kmeans 方法,其中距离设为欧氏距离。

import numpy as np

# 计算一个样本与数据集中所有样本的欧氏距离的平方
def euclidean_distance(one_sample, X):
    '''
    input:
        one_sample(ndarray):单个样本
        X(ndarray):所有样本
    output:
        distances(ndarray):单个样本到所有样本的欧氏距离平方
    '''
    #*********Begin*********#
    # 计算单个样本与所有样本的欧氏距离平方
    distances = np.sum((X - one_sample)**2, axis=1)
    #*********End*********#
    return distances

# 从所有样本中随机选取k个样本作为初始的聚类中心
def init_random_centroids(k, X):
    '''
    input:
        k(int):聚类簇的个数
        X(ndarray):所有样本
    output:
        centroids(ndarray):k个簇的聚类中心
    '''
    #*********Begin*********#
    # 获取样本数量和特征维度
    n_samples, n_features = X.shape
    # 创建k个聚类中心的空数组
    centroids = np.zeros((k, n_features))
    # 随机选择k个样本作为初始中心
    random_indices = np.random.choice(n_samples, k, replace=False)
    centroids = X[random_indices]
    #*********End*********#
    return centroids

# 返回距离该样本最近的一个中心索引
def _closest_centroid(sample, centroids):
    '''
    input:
        sample(ndarray):单个样本
        centroids(ndarray):k个簇的聚类中心
    output:
        closest_i(int):最近中心的索引
    '''
    #*********Begin*********#
    # 计算样本到各个中心的距离
    distances = euclidean_distance(sample, centroids)
    # 返回距离最小的中心索引
    closest_i = np.argmin(distances)
    #*********End*********#
    return closest_i

# 将所有样本进行归类,归类规则就是将该样本归类到与其最近的中心
def create_clusters(k, centroids, X):
    '''
    input:
        k(int):聚类簇的个数
        centroids(ndarray):k个簇的聚类中心
        X(ndarray):所有样本
    output:
        clusters(list):列表中有k个元素,每个元素保存相同簇的样本的索引
    '''
    #*********Begin*********#
    # 初始化k个空列表,用于存储样本索引
    clusters = [[] for _ in range(k)]
    
    # 遍历每个样本,将其分配到最近的中心所在的簇
    for i, sample in enumerate(X):
        # 找到离样本最近的中心
        centroid_idx = _closest_centroid(sample, centroids)
        # 将样本索引添加到对应簇
        clusters[centroid_idx].append(i)
    #*********End*********#
    return clusters

# 对中心进行更新
def update_centroids(k, clusters, X):
    '''
    input:
        k(int):聚类簇的个数
        clusters(list):每个簇包含的样本索引列表
        X(ndarray):所有样本
    output:
        centroids(ndarray):k个簇的聚类中心
    '''
    #*********Begin*********#
    # 获取特征维度
    n_features = X.shape[1]
    # 初始化聚类中心数组
    centroids = np.zeros((k, n_features))
    
    # 计算每个簇的新中心(平均值)
    for i in range(k):
        # 如果簇为空,保留原中心或随机选择一个样本作为中心
        if len(clusters[i]) == 0:
            # 选择一个随机样本作为中心
            centroids[i] = X[np.random.randint(0, X.shape[0])]
        else:
            # 计算簇内所有样本的平均值作为新中心
            cluster_samples = X[clusters[i]]
            centroids[i] = np.mean(cluster_samples, axis=0)
    #*********End*********#
    return centroids

# 将所有样本进行归类,其所在的类别的索引就是其类别标签
def get_cluster_labels(clusters, X):
    '''
    input:
        clusters(list):列表中有k个元素,每个元素保存相同簇的样本的索引
        X(ndarray):所有样本
    output:
        y_pred(ndarray):所有样本的类别标签
    '''
    #*********Begin*********#
    # 初始化标签数组,大小为样本数量
    n_samples = X.shape[0]
    y_pred = np.zeros(n_samples)
    
    # 为每个簇的样本分配标签
    for cluster_idx, cluster in enumerate(clusters):
        for sample_idx in cluster:
            y_pred[sample_idx] = cluster_idx
    #*********End*********#
    return y_pred

# 对整个数据集X进行Kmeans聚类,返回其聚类的标签
def predict(k, X, max_iterations, varepsilon):
    '''
    input:
        k(int):聚类簇的个数
        X(ndarray):所有样本
        max_iterations(int):最大训练轮数
        varepsilon(float):最小误差阈值
    output:
        y_pred(ndarray):所有样本的类别标签
    '''
    #*********Begin*********#
    # 从所有样本中随机选取k样本作为初始的聚类中心
    centroids = init_random_centroids(k, X)
    
    # 迭代,直到算法收敛(上一次的聚类中心和这一次的聚类中心几乎重合)或者达到最大迭代次数
    for _ in range(max_iterations):
        # 将所有进行归类,归类规则就是将该样本归类到与其最近的中心
        clusters = create_clusters(k, centroids, X)
        
        # 保存当前的聚类中心,用于后续比较是否收敛
        prev_centroids = centroids.copy()
        
        # 计算新的聚类中心
        centroids = update_centroids(k, clusters, X)
        
        # 如果聚类中心几乎没有变化,说明算法已经收敛,退出迭代
        diff = np.sum((centroids - prev_centroids)**2)
        if diff < varepsilon:
            break
    
    # 根据最终的簇获取标签
    y_pred = get_cluster_labels(clusters, X)
    #*********End*********#
    return y_pred

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值