无监督学习——凝聚聚类

凝聚聚类指的是许多基于相同原则构建的聚类算法,这一原则是:算法首先声明每个点是自己的簇,然后合并两个最相似的簇,直到满足某种停止规则为止。scikit-learn中实现的停止规则是簇的个数,因此相似的簇被合并,直到仅剩下指定个数的簇。还有一些链接准则,规定如何度量”最相似的簇“。这种度量总是定义在两个现有的簇之间。

scikit-learn中实现了以下三种选项:
ward:默认选项。ward挑选两个簇来合并,使得所有簇中的方差增加最小。这通常会得到大小差不多相等的簇。
average:average链接将簇中所有点之间的平均距离最小的两个簇合并。
complete:complete链接(也称为最大链接)将簇中点之间最大距离最小的两个簇合并。

ward适用于大多数数据集,在我们的例子中将使用它。如果簇中的成员个数非常不同(比如其中一个比其他所有都大得多),那么average或complete可能效果更好。

接下来,我通过一个实际例子来说明这个算法。

实际操作

1.数据来源

Student Marks:https://www.kaggle.com/shub99/student-marks

在这里插入图片描述

这是一个简单的二维数据集,通过学生的学期成绩来判断这名学生最后可不可以升级(毕业)。该数据集包含100条数据记录,共有三个特征维度,分别是期中成绩(MID-SEM-MARKS),期末成绩(END-SEM-MARKS)以及升级结果(“0”:失败;“1”:成功)。

2.数据处理

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import winreg
###################
real_address = winreg.OpenKey(winreg.HKEY_CURRENT_USER,r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',)
file_address=winreg.QueryValueEx(real_address, "Desktop")[0]
file_address+='\\'
file_origin=file_address+"\\源数据-分析\\marks.txt"###https://www.kaggle.com/shub99/student-marks
marks=pd.read_csv(file_origin,sep='\t',header=None)
#设立桌面绝对路径,读取源数据文件,这样将数据直接下载到桌面上就可以了,省得还要去找
###################

老规矩,上来先依次导入建模需要的各个模块,并读取文件。

需要注意的是,这份数据集的文件格式是txt,所以用pd.read_csv读取出来的数据是这个样子的:

在这里插入图片描述

所以接下来还需要用方法split来依据特定字符“**,**”进行划分,示例代码如下所示:

在这里插入图片描述

同时,每条数据记录都是以字符串(string)的格式存储的,因此我们还需要用格式转换方法astype(“float”)来将原本的字符串格式(string)强制转换成浮点型(float),最终代码如下:

1
2
3
4
marks["期中成绩"]=marks[0].str.split(",").str[0].astype("float")
marks["期末成绩"]=marks[0].str.split(",").str[1].astype("float")
marks["是否通过考试"]=marks[0].str.split(",").str[2]
del marks[0]

在这里插入图片描述

3.使用凝聚聚类

我们看一下凝聚聚类对这个数据集的效果如何。要注意,由于算法的工作原理,凝聚算法不能对新数据点作出预测。因此AgglomerativeClustering没有predict方法。为了构造模型并得到训练集上簇的成员关系,可以改用fit_predict方法,代码如下所示:

1
2
3
4
from sklearn.cluster import AgglomerativeClustering
agg=AgglomerativeClustering(n_clusters=2)
X=marks.iloc[:,:-1].values####values方法很有用,专门用于将dataframe格式的某几列转化成相应维度的矩阵数组
assignment=agg.fit_predict(X)

PS:推荐大家记一下values方法,这个方法可以把dataframe格式的数据完美转化成相应维度的矩阵数据(array),非常好用。

接下来,我们将聚类结果可视化:

1
2
3
4
5
6
7
8
import mglearn
import seaborn as sns
from matplotlib import pyplot as plt
plt.style.use("fivethirtyeight")
sns.set_style({'font.sans-serif':['SimHei','Arial']})
mglearn.discrete_scatter(X[:,0],X[:,1],assignment)
plt.xlabel("期中成绩")
plt.ylabel("期末成绩")

在这里插入图片描述

可以看到,算法完成了数据集的聚类。但是上面的图片并没有体现出分类的准确性,所以接下来我们通过对比源数据集中的特征分类来评估算法的精度:

在这里插入图片描述

这里主要是通过统计已知结果和聚类结果的差值,来评估算法精度。所以最后,我们可以知道算法的精度为83%。

4.层次聚类与树状图

凝聚聚类生成了所谓的层次聚类。聚类过程迭代进行,每个点都从一个单点簇变为属于最终的某个簇。每个中间步骤都提供了数据的一种聚类(簇的个数也不相同)。有时候,同时查看所有可能的聚类是有帮助的。所以接下来,我们考虑用一种工具来将层次聚类可视化。

不幸的是,目前scikit-learn没有绘制这种图像的功能。但可以利用SciPy轻松生成树状图。SciPy的聚类算法接口与scikit-learn的聚类算法稍有不同。SciPy提供了一个函数,接受数据数组X并计算出一个链接数组,它对层次聚类的相似度进行编码,然后我们可以将这个链接数组提供给SciPy的dendrogram函数来绘制树状图。

1
2
3
4
5
6
7
8
9
10
11
12
from scipy.cluster.hierarchy import dendrogram,ward
linkage_array=ward(X)###将ward聚类应用于数据数组X,返回一个数组,制定执行凝聚聚类是跨离的距离
dendrogram(linkage_array)###现在为包含簇之间距离的linkage_array绘制树状图

###开始画图
ax=plt.gca()

bounds=ax.get_xbound()
ax.plot(bounds)
ax.plot(bounds)
plt.xlabel("样本")
plt.ylabel("簇之间的距离")

在这里插入图片描述

树状图在底部显示数据点。然后以这些点(表示单点簇)作为叶节点绘制一棵树,每合并两个簇就添加一个新的父节点。

从下往上看,每层的数据点两两合并,依次类推,直到在顶层生成两个分支。这对应于算法中两个最大的簇。

树状图的y轴不仅说明凝聚算法中两个簇何时合并,每个分支的长度还表示被合并的簇之间的距离。同时我们还可以看到,最大的三个簇(绿色,红色,兰色)在合并成两个簇的过程中跨越了相对较远的距离。

不幸的是,凝聚聚类依然无法分析图形复杂的数据集,如下图所示:

在这里插入图片描述

但是,DBSCAN可以解决这个问题。碍于篇幅限制,下一篇文章我再来说一说这个算法。

有很多地方做的不是很好,欢迎网友来提出建议,也希望可以遇到些朋友来一起交流讨论。