Press "Enter" to skip to content

Jupyter Notebook使用Python做K-Means聚类分析

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

1,本Notebook背景介绍

 

此前介绍了一篇论文范例《 新冠肺炎疫情期间公众心理健康信息需求研究—以社会化问答平台“知乎”为例 》,该研究作者以社会化问答平台“知乎”为例,通过K-means聚类算法和LDA主题模型挖掘“知乎”平台下心理健康话题中的公众心理健康信息需求提问数据,并对这些数据进行分析和研究,探究新冠肺炎疫情期间公众心理健康信息需求特点及分布情况,为有关政府部门和相关运营商更好地了解这方面现状,提升服务水平提供参考和帮助。

 

该研究使用了K-means聚类算法,我们今天就在python中做K-Means聚类分析实验。

 

1.1,什幺是K-means聚类算法?

 

参看 一步步教你轻松学K-means聚类算法 ,摘录如下:

 

k-平均算法(英文:k-means clustering)源于信号处理中的一种向量量化方法,现在则更多地作为一种聚类分析方法流行于数据挖掘领域。k-平均聚类的目的是:把 n个点划分到k个聚类中,使得每个点都属于离他最近的均值(此即聚类中心)对应的聚类,以之作为聚类的标准。

 

K-Means 是发现给定数据集的 K 个簇的聚类算法, 之所以称之为 K-均值 是因为它可以发现 K 个不同的簇, 且每个簇的中心采用簇中所含值的均值计算而成.簇个数 K 是用户指定的, 每一个簇通过其质心(centroid), 即簇中所有点的中心来描述。

 

聚类与分类算法的最大区别在于, 分类的目标类别已知, 而聚类的目标类别是未知的。

 

注意:k-平均聚类与k-近邻是两种不同的算法,我们将在另外一个Jupyter Notebook中实验KNN算法。

 

1.2,K-means聚类算法步骤

 

k-means聚类算法步骤实质是EM算法的模型优化过程,具体步骤如下:

 

1)随机选择k个样本作为初始簇类的均值向量;

 

2)将每个样本数据集划分离它距离最近的簇;

 

3)根据每个样本所属的簇,更新簇类的均值向量;

 

4)重复(2)(3)步,当达到设置的迭代次数或簇类的均值向量不再改变时,模型构建完成,输出聚类算法结果。

1.3,K-means不适合的数据集

 

了解哪些数据集不适合K-means是有必要的,防止揪过来这个算法就往上硬套,参看 k-means聚类算法原理总结

 

1. 若样本数据是各向异性的,那幺k-means算法的效果较差

 

2. 当样本数据集是非凸数据集时,k-means聚类效果较差

 

3. 当训练数据集各个簇类的标准差不相等时,k-means聚类效果不好

 

4. 若各簇类的样本数相差比较大,聚类性能较差

 

5. 若数据维度很大时,运行时间很长,可以考虑先用pca降维

 

该文讲解好几个场景时都使用了散点图对数据进行观察,在执行聚类算法前这是必要的,还可以用一些描述性统计方法,对数据特点进行观察,也可以使用pca,通过变换,获得好的观察角度,得到有代表性的特征,才进行计算。

 

2,测试数据

 

本算法所用的数据集为 uci 上的聚类算法数据集3D Road Network。

 

为缩短每次实验运行的时间,取原始数据集的前1万行作为测试数据,数据文件为:data/raw/network2.txt

 

文件中的数据格式为如下(部分数据,用于示例):

 

144552912,9.3498486,56.7408757,17.0527715677876

 

144552912,9.3501884,56.7406785,17.614840244389

 

144552912,9.3505485,56.7405445,18.08353563951

 

144552912,9.3508058,56.7404845,18.2794652530352

 

144552912,9.3510534,56.7404863,18.4229736146099

 

144552912,9.3514747,56.7405022,19.1248885940143

 

144552912,9.3521273,56.7405585,19.5905926656897

 

144552912,9.3524201,56.7405974,19.6217636955693

 

144552912,9.3525839,56.740629,19.6599309194984

 

144552912,9.3527255,56.7406626,19.4906695590218

 

3,本notebook执行的运算

 

基于测试数据,在Jupyter Notebook中使用Python做K-Means聚类分析计算。

 

代码参考文献: K-means算法的python实现及可视化

 

4,第三方库

 

运行本Notebook,需安装numpy和matplotlib。如果没有安装,则需要打开一个Python的COMMAND窗口,运行如下的安装命令:

 

$ pip install numpy -i https:// pypi.tuna.tsinghua.edu.cn /simple/

 

$ pip install numpy -i https:// pypi.tuna.tsinghua.edu.cn /simple/

 

5,引入numpy库

 

NumPy是Python中科学计算的基础包。它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种例程,包括数学,逻辑,形状操作,排序,选择,I / O离散傅立叶变换,基本线性代数,基本统计运算,随机模拟等等。

 

NumPy包的核心是ndarray对象。这封装了同构数据类型的n维数组,许多操作在编译代码中执行以提高性能。

 

# coding:utf-8    
import numpy
from numpy import *

 

6,引入matplotlib库

 

matplotlib是一款命令式、较底层、可定制性强、图表资源丰富、简单易用、出版质量级别的python 2D绘图库。

 

matplotlib算是python绘图的元老级库,类似编程语言里的C语言。很多其它的python绘图库是基于matplotlib开发的,比如seaborn、ggplot、plotnine、holoviews、basemap等。

 

matplotlib可用于python脚本、python shell、jupyter notebook、web等。

 

最适合来运行matplotlib绘图的工具是jupyter notebook,本Notebook也是基于该工具做可视化实验:交互式操作,在浏览器上运行代码,能直接显示运行结果和图表,

 

import matplotlib.pyplot as plt

 

7,引入OS库

 

用于定位和读取data/raw/network2.txt测试数据集文件

 

import os

 

8,定义读取测试数据集文件的函数loadDataSet

 

后面会调用这个函数

 

def loadDataSet(fileName):    
    dataSet = []    
    f = open(os.path.join(os.getcwd(), '..\\..\\data\\raw\\',fileName))    
    for line in f.readlines():        
        curLine = line.strip().split(',')   # 这里","表示以文件中数据之间的分隔符","分割字符串        
        row = []        
        for item in curLine:            
            row.append(float(item))        
        dataSet.append(row)     
    return numpy.mat(dataSet)

 

9,定义求向量距离的函数distEclud

 

后面会调用这个函数

 

# 求向量距离
def distEclud(vecA, vecB):    
    return sqrt(sum(power(vecA - vecB, 2)))

 

10,定义选前k个点作为初始质心的函数initCent

 

K-Means聚类算法,需要在运行开始时选取k个点作为初始的质心

 

# 选前k个点作为初始质心
def initCent(dataSet, k):    
    data = []    
    for i in range(k):        
        data.append(dataSet[i].tolist())    
    a = array(data)    
    centroids = numpy.mat(a)    
    return centroids

 

11,定义K均值聚类算法实现的函数KMeans

 

# K均值聚类算法实现
def KMeans(dataSet, k, distMeas=distEclud):    
    m = numpy.shape(dataSet)[0] #数据集的行    
    clusterAssment = numpy.mat(numpy.zeros((m, 2)))    
    centroids = initCent(dataSet, k)    
    clusterChanged = True    
    while clusterChanged:        
        clusterChanged = False        
        for i in range(m): #遍历数据集中的每一行数据            
            minDist = inf            
            minIndex = -1            
            for j in range(k): #寻找最近质心                
                distJI = distMeas(centroids[j, :], dataSet[i, :])                
                if distJI < minDist: #更新最小距离和质心下标                    
                    minDist = distJI                    
                    minIndex = j            
            if clusterAssment[i, 0] != minIndex:                
                clusterChanged = True            
            clusterAssment[i, :] = minIndex, minDist**2 #记录最小距离质心下标,最小距离的平方        
        for cent in range(k): #更新质心位置            
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]] #获得距离同一个质心最近的所有点的下标,即同一簇的坐标            
            centroids[cent,:] = mean(ptsInClust, axis=0) #求同一簇的坐标平均值,axis=0表示按列求均值     
    return centroids, clusterAssment

 

12,定义取坐标的函数getXY

 

取数据的前两维特征作为该条数据的x , y 坐标

 

def getXY(dataSet):    
    import numpy as np    
    m = numpy.shape(dataSet)[0]  # 数据集的行    
    X = []    
    Y = []    
    for i in range(m):        
        X.append(dataSet[i,0])        
        Y.append(dataSet[i,1])    
    return np.array(X), np.array(Y)

 

13,定义数据可视化函数showCluster

 

该函数调用matplotlib.pyplot来画图

 

# 数据可视化
def showCluster(dataSet, k, clusterAssment, centroids):    
    fig = plt.figure(figsize=(15,5))   
    plt.title("K-means")    
    ax = fig.add_subplot(111)    
    data = []     
    for cent in range(k): #提取出每个簇的数据        
        ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]] #获得属于cent簇的数据        
        data.append(ptsInClust)     
    for cent, c, marker in zip( range(k), ['r', 'g', 'b', 'y'], ['^', 'o', '*', 's'] ): #画出数据点散点图        
        X, Y = getXY(data[cent])        
        ax.scatter(X, Y, s=80, c=c, marker=marker)     
    centroidsX, centroidsY = getXY(centroids)    
    ax.scatter(centroidsX, centroidsY, s=1000, c='black', marker='+', alpha=1)  # 画出质心点    
    ax.set_xlabel('X Label')    
    ax.set_ylabel('Y Label')
    plt.show()

 

14,K-Means聚类分析实验一

 

指定k=2, 也就是聚类分析的结果得到2个类

 

cluster_Num = 2    
data = loadDataSet("network2.txt")    
centroids, clusterAssment = KMeans(data, cluster_Num)    
showCluster(data, cluster_Num, clusterAssment, centroids)

15,K-Means聚类分析实验二

 

指定k=3, 也就是聚类分析的结果得到3个类

 

cluster_Num = 3      
data = loadDataSet("network2.txt")    
centroids, clusterAssment = KMeans(data, cluster_Num)    
showCluster(data, cluster_Num, clusterAssment, centroids)

16,怎样选择K的大小?

 

上面我们分别实验了K=2和K=3的情况,那幺怎样选择K的大小呢? 一般使用“手肘法”。

 

在K-means怎幺选K?一文中有描述:

 

手肘法本质上也是一种间接的观察法。这里需要一点K-Means的背景知识。当K-Means算法完成后,我们将得到K个聚类的中心点Mi, i=1,2,⋯,K。以及每个原始点所对应的聚类Ci,i=1,2,⋯,K。我们通常采用所有样本点到它所在的聚类的中心点的距离的和作为模型的度量,记为DK。

很显然K越大,距离和越小。但是我们注意到K=3是一个拐点,就像是我们的肘部一样,K=1到3下降很快,K=3之后趋于平稳。手肘法认为这个拐点就是最佳的K。

 

手肘法是一个经验方法,而且肉眼观察也因人而异,特别是遇到模棱两可的时候。相比于直接观察法,手肘法的一个优点是,适用于高维的样本数据。有时候人们也会把手肘法用于不同的度量上,如组内方差组间方差比。

 

具体的算法这里不在测试了, 有兴趣的同学可以自行进行实验, 有很多文章可以参考,比如这篇: Python:K-Means聚类分析

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注