当前课程知识点:R语言数据分析 > 下部:博术 > 第13章 方以类聚、物以群分 > 13.2 划分方法
大家好
欢迎来到《R语言数据分析》课程
今天和大家交流一下
聚类分析里面一个非常经典的算法
基于划分的kmeans方法
基于划分的方法
应该是聚类分析里面最基本 最简单
应该也说是最经典的方法之一
它的最基本思路是
就是发现用户指定个数的簇
我们可以用形式化的表述方法来重新描述一下
就给定m个对象
也就是说我们这个数据框包含m行
对吧
这么一个数据集
以及要生成的簇数
因为是生成用户指定的个数
这个k是用户指定的
并不是这个算法自己生成的
这个时候其实我的输入至少得有
至少得有数据集 还得有这个K
所谓划分的算法是
就是把这个数据对象分成k个簇
用户说要分成k个簇
那好我们现在分成什么
C1 C2一直到Ck
k个簇
使得i和j
分别都是大于等于1
小于等于K的
那好
Cj它也就说每一簇
它所包含的数据对象应该都是
都是这个数据集的一个真子集
并且每一簇的交集都是都是空集
当然我们这里面其实它还有一个就是
[所有]簇的并集它应该就是这个数据集本身就是D
这个我们需要一个目标函数来评估
这个划分的质量
那具体怎么评估
其实和我们前面讲的
聚类分析的目的应该是一致
就是什么
强内聚松耦合
同一个簇之间
同一个簇里面的对象
它应该相对离比较近
比较相似
那不同的簇之间
它应该离得比较远
不怎么相似
这么一个过程
那好
我们看一看具体目标函数我们怎么定义
我们可以选用这个目标函数
就是误差的平方和
就是SSE
后面其实我们在R里面实现的时候
也会经常看到这个SS
就是这个误差平方和
它具体什么意思
比如说我们现在要把这个数据集
这个数据对象分成k份
那好
我的每一份我都找一个质心
找一个中心点
我就令它为Ci
为Ci那毫无疑问
这个Ci是个
假如我这个数据总共包含N列的话
这应该也是
包含N个元素的一个向量
和这个X一样的
就是一行包含N列
那好
这里面也是包含N个数据点
那我就计算这两个向量之间的距离
计算这两个数据点之间的距离就dist
这个距离计算方法
当我们前面讲了
比如说我可以用这个欧式距离
这个距离的平方
我把它求出来
我就视为误差平方和
然后再对
每一个类我都做这么一个操作
每个类它相应的对象都算一算
和我这个类中心的距离加起来
加完之后再把这个K个类再加起来
这就视为什么
视为误差平方和
我们的目标是什么
要让这个尽量紧凑
对不对
让它尽量紧凑
尽量独立
那就尽量要让那个误差平方和尽量小
这个目标函数我们有了
我们有这么一个量化的指标
来要想达到这么一个目标
让它尽量小
但是
假如我要枚举或者是通过蛮力的方法
遍历所有可能划分的话
那这就是一个NP问题
我们可以考虑下
比如即便我这个K等于2
我就说
把这个数据对象m个数据对象分成两份的话
那这么其实有好多划分的方法
什么意思
至少我可以把它分成两份
一个什么m等于m1加m2
也就是说我这个数据
数据集本身是m对象M行
那我现在分成m1 m2这么两份
K等于2分成两份
那第一份里面
第一个数据集里面有m1个对象
第二个数据集里面有m2个对象
那我划分的时候
我至少从m里面抽取m1个
但这个时候其实m1有多少个取法
我从1到多少
从1到m减1都可以
对不对
所以这个时候其实它整个这个要遍历的话
它其实整个这个过程非常复杂
而且你遍历完之后
还得对每一个什么每一个簇进行这么一个计算
所以计算量非常非常大
这个时候通过蛮力的方法是不可取的
对吧
当然我们也是没必要的
这个时候我们可以采用一些方法
一些算法
比如这个我们接下来介绍
这个k-Means这个算法
其实可以避免这么一个问题
好我们看看
k-Means方法是如何
通过一个启发式的方法
来实现我们这个目标函数它的一个优化的
它算法原理其实相对还是比较简单
它输入包含这个首先是k
你这个数据究竟想分成多少份
当然还有我这个数据本身
包含M个对象的数据集
那输出就是将我这个D分成了k份
对不对
也就是k个簇的集合
它具体过程我们看看总共包含这么五个步骤
首先是从D里面随机的选择K个对象
作为初始的簇中心
其实也就是说我们这包含M行的这个数据框
我从里面随机选择多少
选出k行作为我这个簇的中心
作为簇的中心
然后进行迭代
注意了 开始进行迭代了
迭代过程是怎么样
我根据我这个离簇中心的远近
因为你现在总共有k个中心
对不对
相当于我在这个m个点里面选了k个点
那好
那另外m-k个点
我就分别来找
我究竟离哪一个点最近
就是这个k点里面哪个点最近
离哪个点最近的话
我就将我自己怎么样分配到
相应的那个簇里面去
也就说你这刚开始选定了这个簇中心
就代表这个簇了
那好
我离你这个中心越近
那我就把我自己归到这里面去
那我就把我自己归到这里面去
一旦归完之后
那我整个这个所有的m个点我都分成了K份了
然后对每一个份我要更新这个簇的均值
也就说我这个中心是不是真的中心
我就划分完之后
每一个点都找好自己的位置之后
我看看每一份里面
把每一份每一个簇的中心
我再重新计算一下
是不是和原来这个中心是一致的
假如是一致的话
那好
那我这个时候就不用再更新了
就不用再Repeat的了
假如是有变化的话怎么办
我再重新迭代
又根据什么
因为我这个时候更新完之后
新的类中心也算出来了
对不对
新的中心来了之后
我再把每一个点
每个点再重新做一下归位
我看一看离新的k个点
新的k个簇中心
离谁最近
那好
我就又把每个点都分到相应的簇里面去
分完之后再把分完了之后的
每个簇的点我再重新计算一下簇中心
和刚才这个簇中心是不是一致的
假如不一致
我要继续迭代
就这么一个过程
当然这样说的时候可能有点拗口
它已经有一点这个相对有点抽象
咱们看一看这个具体过程
我们通过一个图来示意它
我们同样是以
这么一个二维的这个一个数据空间来展示它
比如说我们现在有好多个点
那好
首先 我在这个m个点里面随机选择k个点
比如说我们现在想把它分成三份的话
我就随机选了三个点
所以说其实还回到我们之前的一个说法
叫有生于无
当我不知道从何下手的时候怎么办
我先随机选
先随机选
选完之后那好
我现在把每一个点
和我这个三个点的距离我都计算一下
比如说我这下面这个点
我离什么离我这个点近那好
我就把它归成这一类
然后这个点的话
比如这个点离这边近
这一部分又归一类
上面这个点离谁近
离这个近
把这部分归为一类
这样归成了三类之后
大家看一下
归成三类之后
我们再看看归成三类之后的这些点
它的中心是不是真的就是刚才这三个加号
我们发现不是
为什么呢
我们重新把这个中心计算之后发现这三个部分
分别的对中心在这个位置在这个位置
变了
变了怎么办
我进入下一轮迭代
进入下一轮迭代怎么样
我又重新把
每一个点
和新的三个簇中心的距离我先计算一下
我看我究竟哪个点
哪个点
离哪个簇中心最近
那我也重新分一次
又重新分一次
接着进入下一轮迭代
不断迭代迭代完之后
最后我就相对稳定了这一部分的类中心
你基本再重新计算的时候发现
和这部分是一样的是一致的
那我就再停止迭代了
就不再变化的时候
我就停止迭代
那我们这是通过一个图的方式来展示了一下
我们所谓的那个k-Means
k均值的这么一个迭代的过程
它避免了我们这种野蛮的遍历
野蛮的枚举和一个搜索的过程
再来看另外一张图
刚才这个图里面我们其实没有划这个边界
对吧
那好
比如说我现在还是以这么一个
二维的这个数据空间为例
当然这个数据就比较有名了
叫iris
这个我们看
看这么一个二维空间里面
刚开始的时候
这边总共应该是150个点
我刚开始随机选了三个点
那好
选完之后
150个点里面每一个点都怎么样
都和这三个点做一个比较
我们就发现它边界什么样的
就凡是在这个区域的应该都划为这一类
凡是在这个区域的都划为这一类
是不是
反正到上面这个区域的
都应该是另外这个中心所属的类
对吧
好
画完之后这相当于
假如是我们把这三个点比作领导的话
我先看看这些群众向领导靠齐的时候
每个群众我都知道我自己所属的位置
我属于哪个群体
归谁领导了
但是集中完了之后
我还得再讲点民主
什么意思
我这边每个点都已经标完了
它属于这个类
那我再看看这些类
它的本质上的中心是不是真的就这一点
不是因为我们这些点中心可能在这个位置
这一点的中心可能在这个位置
那这一点的中心可能往这个地方一点
对不对
那好
你刚开始是集中了一下
然后我的民主一下
然后再重新什么
我再重新集中一下
再民主一下
假如我们做这种比喻的话
就是民主集中 民主集中
最后达到效果是什么
就民主和集中的意见是一致的时候
那好
那我这个k-Means的算法就终止了
咱们看一看具体的边界变化的过程
可以看得出来
我们这里面民主集中的过程在不断的迭代
迭到最后的时候
民主集中意见一致了
我们这个分类的过程也就完了
k-Means过程完成了
以上就是我们通过算法语言
我们通过这个图形以及一个比喻
简单的阐述了一下这个k-Means
K均值它这种聚类方法的一个最基本的原理
咱们看一下在R里面如何实现的
在R里面的话
有这么几个函数是跟这个k-Means相关的
用得比较多
首先是这个最基本的
这个stats这个包里面有个k-Means
我们顾名思义毫无疑问就是
K均值的这么一个算法的一个实现
还有一个fpc那个包里面也有相应的函数
比如kmeansruns()都可以
咱们看看具体这个函数如何使用的
我们依旧是以我们这个问题情境为例
这个成绩表cjb
拿到所有的学生的九门课的成绩
我根据这九门课的成绩
我看看能不能聚一下类
聚成两类之后
是不是和我这个文理分科确实是一致的
证明在这个九维的数据空间里面
它的自然结构确实呈现出了这么两簇
一群可能大部分是文科生
一群大部分是理科生
我们首先是先把这个成绩表里面的
语文一直到生物九门课先选取出来
取完之后
直接交给这个kmeans()这个函数就可以了
我们讲了这个kmeans这个算法
在前面描述的时候也发现
它至少得包含两个参数
一个是数据集本身
另外一个就是k
k的话这个kmeans这个函数里面
它不是一个参数叫K
是叫centers
其实也是中心点的个数
类中心的个数其实就是k
当然这个kmeans里面
还可以设置其他一些参数
比如说我迭代的次数
有可能说一直迭代一直迭代
受我这个计算时间限制
那我可以设置一个最大的迭代次数也可以
当然那些参数一般都不需要设置
最基本上就这两个一个数据集
另外一个是k的需要指定一下
建完之后我们交给imodel的这个变量
我们看看imodel的话它其实是一个列表
看看它列表组成部分
这边总共有这么九个组成部分
比如说cluster
centers
totss
咱们看看每一个组成部分究竟是什么意思
第一个组成部分
因为它既然是一个列表的话
毫无疑问我可以通过这个美元符号
把它给析取出来
提取这个imodel的第一个组成部分
就是cluster
毫无疑问这边是多少
它其实和我们的数据集的条数是一模一样的
就是每一个数据点
我现在都已经归成某一类了
因为我这个k为2的话
毫无疑问
这个类别要不就是1要不就是2
那好
这里面其实2和1就分别代表
我这些数据点这些数据对象它所属的类
也就是说cluster其实就是归类的结果
归类的结果
咱们来看这个这个centers
我们讲了聚类分析的话
它其实最终的结果是
这个聚的类以及这个类的特征
类的中心的话其实是类的非常重要的特征
就是这两个点群
这两个簇它分别具有什么特点
我先可以看看中心点
它的位置什么样的
我们来看一下
比如说我们这里面是九维的一个数据空间
它相应的
类中心的话
也是一个包含九个元素的一个向量
那好 我们可以看一下
比如说
第一类和第二类作比较的话
比如数学
生物等等
可以看得出来第二类明显是高于第一类
其实我们可以直观地认识一下
就是像这个类中心不一样的话
毫无疑问我们认为
假如我们要把
这两类要和这个文理分科对应起来的话
那我应该是第二类应该是对应到这个理科
第一类对应到文科
因为我们从前面的
数据可视化的时候已经发现了
理科生那些相应的课程成绩是相对高一点的
类中心的话
它在很大程度上代表这个类本身的特征
咱们看下一个组成部分
totss
这个看起来是一个缩写
还有后面这个withinss
还有这个tot.withinss
还有这个betweenss
这跟betweenness没什么关系
它只是betweenss
其实我们来看一下
这个totss减掉这个tot.withinss
等于betweenss
是不是
这个是一个什么意思
这块其实是
我把所有的数据点当成只作为一类的时候
它的误差的平方和是多少
就作为一类的时候
就是每一个点和这个(总体)类中心
因为现在类中心的话相当于
相当于每一个维度上的平均值
每个维度上平均值就作我整个这个大类
整个这个700多个点的一个类中心的
每个点和我这个类 类中心的距离的平方和
加起来多少加起来就是这个值
那这个withinss因为它有两类是不是
有两个点群
那每个点群的话也是一样
当前这个点群里面
每一个点
和我这个点群的中心之间的距离的平方
它们的和就是分别是这两个值
那毫无疑问下面这个值就是他们俩的和相加
是不是
然后这个所谓的betweenss什么意思
那就是所谓的总的误差
总的距离减掉这个组内的这个误差
组内的距离就等于这个组间的了
当我们在看的时候
其实还是心里还是不放心的
我们假如能用代码写完之后
跟它的实现的结果是一致的话
这个时候我们可能才能真的比较脚踏实地
我们看一下
假如我们自己写代码怎么实现的
我们首先得确定
比如说对这个总的这个
距离来讲 或者总的
误差的平方和来讲 怎么算
我先得算
先找这个中心在哪
也就是说对于每一个scores
这么九个科目
每一个科目多少求一个平均值
这个平均值就是我这个类中心
就是700多个数据点的一个中心
那好 我一旦找这个类中心之后
我下一步要做的事情就是每一个点
都和这个类中心我取一个距离
所谓的距离怎么求
我把每一个点的
每一个点不同的分量
和我这个类中心的相应的分量
做一个什么差值
先减掉它
减掉它之后怎么办
因为我是用欧式距离算的话
就是平方
平方之后再怎么样
不需要开根号了
为什么
因为我是距离的平方
再求和对不对
距离的平方再求和
这其实就是我们所谓的总的误差的
总体误差平方和
这是我们那个具体的
用不同的点减掉这个类中心
当然
我们这边不同的点其实是什么
就是不同的行对不对
所以我采用这个apply这个函数
对这个scores进行操作
因为是按行进行操作的话
毫无疑问
第二个参数
应该设置为1
然后把每一个点
和它的距离的平方和都求出来之后
我再什么
再把所有点的这个误差平方和再进行相加
之后就得到我们所谓的total_SS
其实和前面的结果应该是完全一致的
当然同学们感兴趣也可以自己写代码
实现下每一类它相应的
误差平方和
这是我们关于这个模型返回结果的
一个最基本解释
相应的不同的组成部分
咱们再看一看
假如我现在模型一旦建完之后
它究竟把我们这个九维的数据空间不同的点
画成一个什么样的样子了
画成哪两个点群
当我们在九维数据空间里面
我们是难以想象的
而且我们现在也没有办法来突破三维
变成九维之后来看它
那这时候可能有一个办法就是什么
我先把他映射到二维的空间里面来
映射二维空间里面之后
我再看看这个距离的远近
呈现一个什么样的形态
哪些点是比较近一点
哪些点是离的远一点的
当然从高维到低维的映射有好多方法
比如说有主成分分析法
有这个多维标度分析等等
咱们看一看
我们可以调用这个包
就是factoextra这么一个包
里面有一个专门用来可视化这个cluster
就是我这个聚类结果的这么一个函数
这个fviz的话是这个包里面的一个前缀
是表示专门来做可视化的
同样是我们把我们刚才建好这个模型
imodel作为一个参数
同时我把这个数据也交给它
咱们看看具体结果什么样的
这个时候其实是什么是
第一主成分
第二主成分对不对
然后在这个数据点里面映射完之后
从九维映射到二维之后
我们可以看一下
确实它是把数据分成了这两类
不同的数据归属为不同的类
也就说通过这么一个映射的方法
我们看一下大概的数据
它在高维空间里面
它大概所呈现的一个形态
虽然我们现在能看得比较清楚
模型也拿到了
图形也拿到了
但同学们肯定会说
你这是因为你事先已经知道这个类
这么多同学应该分成文理分科分成两类
所以你才把这个K设为2
后面的结果
才看起来好像相对比较漂亮点
但是我们对一个实际问题来讲
因为无监督学习的话
这个标签是不知道的
究竟多少个标签
每个标签应该打成什么样子
我是不知道的
那怎么办
这个时候我们可以看一看
通过一些评估的方法
通过一些评估指标
比如说我们前面讲的这个轮廓系数
我就可以来评估 来确定
我对不同的K值
它轮廓系数究竟有多大
然后跟这轮廓系数来算
我来重新反推这个K应该取几才比较合适
这时候我们要借助这个包
fpc这个包这
这里面专门有一个kmeansruns()
什么意思呢
这里面我对不同的K进行遍历
遍历完之后
我看看
它相应的这个轮廓系数的平均值究竟多大
毫无疑问我是要找什么
找这个轮廓系数平均值最大的一个K
就视为我最佳的一个分类
这也反映了我这个九维数据空间里面
最自然的
一个所存在的结构
这点群所存在结构
我们一旦把这个scores这个数据
交给这个kmeansruns()的话
它返回一个结果
那好 我们看这结果
它就包含很多部分
总共包含这么11个部分
当然最后一部分就是
所谓的最佳的这个K值
当然前面这些比如包括
我们看到的这个cluster等等
它其实和我们前面看到
那个kmeans里面意思差不多的
咱们主要关注一下后面这个
这个K的选择以及这个所谓的评估的问题
通过轮廓系数来进行评估
毫无疑问
我们可以把kmeans_results
这个刚才返回的结果
kmeansruns()返回的结果这个列表
把其中bestk最好的K拿出来一看
它确实就是这样
确实恰恰就为2
和我们文理分科分的两类确实正好是一致的
那我们再看看
它相应的取值多少
当它为2的时候它的轮廓系数0.33
K等于3的时候0.24
为4的时候0.24等等等等一看
确实为2的时候轮廓系数是最大的
它的平均值最大
毫无疑问
那我这个最好的K应该就是2
当然
我们也可以将这个这个轮廓系数
每个点轮廓系数计算一下
然后我们再将它可视化展示一下
因为这里面只是算了一个平均值
就取不同的K的时候一个平均值
那好
我们现在看看
比如说我K取2和K取3的时候
每一个点上面它的轮廓系数是怎么变的
要计算这个轮廓系数的话
我们要用这个包叫cluster
然后这个包
我们在前面讲轮廓系数的时候
它至少在包含两个参数
第一个是我这个距离
每个点两两之间的距离把它算出来
第二个我得知道它相应的所属的标签
就是分完类之后
哪些点是一类的
哪些点是另外一类的
那好
第一个参数的话
我直接调用这个dist这个函数就可以得到
得到一个距离的一个矩阵
然后第二个参数的话
我先可以通过这个kmeans来先进行聚类
聚完类之后
我取出其中的相应所聚的类的标签
然后将这两个参数交给谁
交给我这个计算这个轮廓系数的函数
分别是这个cluster_idx2
然后scores_dist
然后我们就可以得到这个
每一个点的相应的轮廓系数
同样我们可以通过这个fviz为前缀的这个
就是咱们那个factoextra这个包里面的相应的一个
可视化的函数
对这个轮廓系数进行绘图
咱们看一下
当它为2的时候
当它为2的时候是一个什么样情况
这边其实分成两部分
我根据不同的颜色可以看得出来
这么多700多条那个记录就分成两部分的
分两部分
当然有一部分
第二个类里面有一部分轮廓系数为负
但总体上来讲
它平均值是多少 应该是0.33左右
假如我是K取3的时候
它轮廓系数多少
我们看一下
这边呢
就是k=3的时候每个点的轮廓系数
当然我们要确定这个k值的话
主要看什么 主要看这个
平均的轮廓系数
当然我们不会说要选K值的话
把k=2 k=3 k=4一直这样画下去
可以怎么办呢
我们可以把
k取不同的值的时候
把它轮廓系数的平均值都算出来
算出来之后
绘制在同一图里面
我们看看怎么实现的
这个时候我们还依然调用这个
还是调用这个factoextra
这么一个包里面的什么呢
fviz
nbclust
调用这个函数
这个里面我们用的是轮廓系数来确定不同的k
来优先它的k
这个时候k是从多少啊
从2
一直到20
我们看一下
这还是用的什么kmeans方法
就是基于这个kmeans方法的话
我们进行聚类
聚成不同类的话
我看看它的轮廓系数
平均值
它的一个变化的情况
具体图形我们看一下
这上可以看得出来
这时候K等于2的话
我们看一下具体的结果
横轴的话是什么
就我的K取不同的值
从1-2-3-4-5-6-7-8-9-10
一直到多少
到20
就我们前面设的kmax=20
纵轴呢 纵轴就是我们的
轮廓系数的平均值
轮廓系数的平均值
毫无疑问
我们特别希望是看到这种
类似这么一种
先
往上升
上升之后再下降
整个下降一个趋势
毫无疑问我们要找到一个最高点
找到最高点
这个时候最高点在哪
最高点在什么
2
K等于2的时候取到最高点
所以通过这种方法
我们可以优选K值就为2
这正好和我们什么
和我们这个问题本身这个情境是
相吻合的
通过聚类方法
发现也是聚为两类比较合适
我们很自然的想
它和文科 理科
文理分科是不是正好对应的
我们看一下和实际的
类标签做一个对应
看看它对比之后
看它结果怎么样
要和实际类标签做对比的话
毫无疑问我先把实际类标签先取出来
就是成绩表里面的文理分科
就是实际的
每一个同学的文科理科的选择情况
然后怎么样
我将我这个模型聚出来的类
对应到理科或者文科上面去
对应完之后怎么样
我调研Metrics
包里面的ce
这个计算分类错误率这个函数
我来对比一下
看看究竟分类错误率是多少
当然因为我聚类结果它是没有标签的
比如说1对应到理科
或者1对应到文科都有可能
所以我要取这二者里面最小的那一个
我们看一下
这个值多少 0.354
毫无疑问
我们在做聚类的时候
其实我是不知道类标签是多少的
我只是什么
看9维的数据空间
它自然的一个结构 是吧
就这700多个点在9维数据空间里面
根据它距离远近关系发现
分成两簇比较合适
而确实
我们发现它和什么
实际的文理分科它有一定的对应关系
也就是说
我们的九门课确实对文理分科是有影响的
从而也说明我们聚类
这个结果有一定的意义
本次课到此结束
谢谢大家
-第1章 气象万千、数以等观
--第1章 作业
-第2章 所谓学习、归类而已
--第2章 作业
-第3章 格言联璧话学习
--第3章 作业
-第4章 源于数学、归于工程
--第4章 作业
-讨论题
-第5章 工欲善其事、必先利其器
--第5章 作业
-第6章 基础编程——用别人的包和函数讲述自己的故事
--6.1 编程环境
--6.4 控制流
--第6章 作业
-第7章 数据对象——面向数据对象学习R语言
--第7章 作业
-第8章 人人都爱tidyverse
--第8章 作业
-第9章 最美不过数据框
--第9章 作业
-第10章 观数以形
--第10章 作业
-第11章 相随相伴、谓之关联
--11.1 导引
--第11章 作业
-第12章 既是世间法、自当有分别
--12.1 导引
--第12章 作业
-第13章 方以类聚、物以群分
--13.1 导引
--第13章 作业
-第14章 庐山烟雨浙江潮
--第14章 作业