从0实现 k-means算法

4 篇文章 0 订阅
2 篇文章 0 订阅

k-means算法

基于划分的方法

​ 给定一个有n个数据对象的数据集D以及要生成的簇数k(k < n),划分方法D中对象分配到k个分组,每一个分组就代表一个聚类。然后用一个目标函数来评估划分的质量,使得簇内对象相似,而与其它簇内的对象相异。

特点

  • 很适合发现中小规模的数据集中的球状互斥的簇
  • 是基于距离
  • 可以用均值或中心点等代表簇中心

K-means简介

​ 由于具有出色的速度和良好的可扩展性,K-means聚类算法算得上是最著名的聚类方法。K-means算法是一个重复移动类中心点的过程,把类的中心点,也称重心(centroids),移动到其包含成员的平均位置,然后重新划分其内部成员

K-means算法相关概念

距离度量

曼哈顿距离

在曼哈顿街区要从一个十字路口开车到另一个十字路口,驾驶距离显然不是两点间的直线距离。这个实际驾驶距离就是“曼哈顿距离”,也称为“城市街区距离”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bvajegea-1639638028377)(D:\blog\Pic\k-means3.png)]

欧式距离

欧氏距离是一个通常采用的距离定义,指在m维空间中两个点之间的真实距离
(img-ZIlfYtpE-1639638028379)(D:\blog\Pic\k-means2.png)]

余弦相似度

余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。相比距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上。
余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HTz2Y7Vf-1639638028381)(D:\blog\Pic\k-means4.png)]

常见距离代码实现
import numpy as np
def distance(x,y,p=2):
    '''
    input:x(ndarray):第一个样本的坐标
          y(ndarray):第二个样本的坐标
          p(int):等于1时为曼哈顿距离,等于2时为欧氏距离
    output:distance(float):x到y的距离      
    ''' 
    if(p==1):
        return  sum(abs(x-y))
    elif(p==2):
        return (sum((x-y)**2))**(1/2)
计算一个样本与数据集中所有样本的欧氏距离的平方
def euclidean_distance(one_sample, X):
    one_sample = one_sample.reshape(1, -1)
    distances = np.power(np.tile(one_sample, (X.shape[0], 1)) - X, 2).sum(axis=1)
    return distances

质心与目标函数

质心

​ 质量中心的简称。质点系的质心是质点系质量分布的平均位置。
而质心可能随数据临近性度量不同和聚类目标不同而改变。

邻近性度量

通常对欧式空间的点使用欧几里得距离,对文档采用余弦相似性。
在此,我们采用欧式距离

目标函数

​ 对于邻近性度量为欧几里得距离的数据,我们通常采用误差的平方和(Sum of the Squared Error,SSE)作为度量聚类质量的目标函数。换言之,计算每个数据点的误差,即它到最近质心的欧式距离,然后计算误差的平方和。

K-means算法流程

python类初始化

class MyKmeans():
    """Kmeans聚类算法.
    Parameters:
    -----------
    k: int
        聚类的数目.
    max_iterations: int
        最大迭代次数. 
    varepsilon: float
        判断是否收敛, 如果上一次的所有k个聚类中心与本次的所有k个聚类中心的差都小于varepsilon, 
        则说明算法已经收敛
    """
    def __init__(self, k=5, max_iterations=300, varepsilon=0.0001, random_state=2):
        self.k = k
        self.max_iterations = max_iterations
        self.varepsilon = varepsilon
        np.random.seed(random_state)

选取初始质心

    # 从所有样本中随机选取self.k样本作为初始的聚类中心
    def init_random_centroids(self, X):
        n_samples, n_features = np.shape(X)
        centroids = np.zeros((self.k, n_features))
        for i in range(self.k):
            centroid = X[np.random.choice(range(n_samples))]
            centroids[i] = centroid
        return centroids

将每个对象分配到最相似的簇

    # 返回距离该样本最近的一个中心索引[0, self.k)
    def _closest_centroid(self, sample, centroids):
        distances = euclidean_distance(sample, centroids)
        closest_i = np.argmin(distances)
        return closest_i

    # 将所有样本进行归类,归类规则就是将该样本归类到与其最近的中心
    def create_clusters(self, centroids, X):
        n_samples = np.shape(X)[0]
        clusters = [[] for _ in range(self.k)]
        for sample_i, sample in enumerate(X):
            centroid_i = self._closest_centroid(sample, centroids)
            clusters[centroid_i].append(sample_i)
        return clusters

更新簇的质心

    # 对中心进行更新
    def update_centroids(self, clusters, X):
        n_features = np.shape(X)[1]
        centroids = np.zeros((self.k, n_features))
        for i, cluster in enumerate(clusters):
            centroid = np.mean(X[cluster], axis=0)
            centroids[i] = centroid
        return centroids

计算所属标签和质心

    # 将所有样本进行归类,其所在的类别的索引就是其类别标签
    def get_cluster_labels(self, clusters, X):
        y_pred = np.zeros(np.shape(X)[0])
        for cluster_i, cluster in enumerate(clusters):
            for sample_i in cluster:
                y_pred[sample_i] = cluster_i
        return y_pred

    # 对clusters,返回其聚类结果的质心
    def get_cluster_centroids(self, clusters, X):
        centroids = np.zeros((self.k,np.shape(X)[1]))
        for i, cluster in enumerate(clusters):
            centroid = np.mean(X[cluster], axis=0)
            centroids[i] = centroid
        return centroids

聚类

    # 对整个数据集X进行Kmeans聚类,返回其聚类的标签
    def predict(self, X):
                
        # 从所有样本中随机选取self.k样本作为初始的聚类中心
        centroids = self.init_random_centroids(X)
         
        # 迭代,直到算法收敛(上一次的聚类中心和这一次的聚类中心几乎重合)或者达到最大迭代次数
        for _ in range(self.max_iterations):
            # 将所有进行归类,归类规则就是将该样本归类到与其最近的中心
            clusters = self.create_clusters(centroids, X)
            former_centroids = centroids
             
            # 计算新的聚类中心
            centroids = self.update_centroids(clusters, X)
             
            # 如果聚类中心几乎没有变化,说明算法已经收敛,退出迭代
            diff = centroids - former_centroids
            if diff.any() < self.varepsilon:
                break
         
        return self.get_cluster_labels(clusters, X),self.get_cluster_centroids(clusters, X)

K-means实战

要求

数据中各个国家的疫情情况都不尽相同,有些甚至差异很大,因此我们想着先将78个国家进行聚类分析,由此来评判各个国家防疫的效果。

数据集

数据来源:https://github.com/datasets/covid-19

通过数据清洗和集成,部分数据如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g5sqLs8s-1639638028382)(D:\blog\Pic\k-means5.png)]

数据规约

累计确诊病例密度=累计确诊病例数/总人口;现有确诊病例密度=现有确诊病例数/总人口
故对于人口总数那一列属于冗余数据,故在进行k-means算法时,将这一列数据删除。

数据转换

数据规范化处理是数据挖掘的一项基本操作。现实中,数据中不同特征的量纲可能不一致,数值间的差别可能很大,不进行处理可能会影响到数据分析的结果,因此,需要对数据按照一定比例进行缩放,使之落在一个特定的区域,便于进行综合分析。

数据规范化可以有效地改进涉及距离度量的算法的精度和有效性。对于采用距离度量的分类算法和聚类算法,数据规范化的特别重要的步骤。

方法
  • 最小最大规范化
  • z-score规范化(零均值规范化)StandardScaler
  • 小数定标规范化
python中sklearn-preprocessing介绍

sklearn-preprocessing里有StandardScaler函数可以用来做零均值规范化

scaler = StandardScaler()
scaler.fit(data)
data = scaler.transform(data)

实战k-means算法代码

import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from My_KMeans import MyKmeans

df = pd.read_csv('Test\\77国家聚类.csv',header=0,index_col='国家')
data = df.iloc[:,:-1]

scaler = StandardScaler()
scaler.fit(data)
data = scaler.transform(data)

mykm = MyKmeans()
label,centroids = mykm.predict(data)
print(label)
result_centroid = scaler.inverse_transform(centroids)
# pd.DataFrame(result_centroid).to_csv('Test\\centroids.csv')
print(result_centroid)
# km = KMeans(5,random_state=2) #这里采用sklearn里的KMeans库(一样可以实现效果)
# res = km.fit(data)
# print(res.labels_+1)

# result = scaler.inverse_transform(res.cluster_centers_)
# print(result)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值