本文概述
在本教程中, 你将学习在R中的数据集上执行分层聚类。更具体地说, 你将了解:
- 什么是群集, 何时使用群集及其类型。
- 如何预处理你的数据。
- 层次聚类算法的工作详细。
- 如何执行聚类分析。
- 与k均值的比较。
:target:before { content:””; display:block; height:150px; margin:-150px 0 0; } h3 {font-weight:normal; margin-top:.5em} h4 { font-weight:lighter }
顾名思义, 群集算法将一组数据点分组为子集或群集。该算法的目标是创建在内部一致但在外部彼此明显不同的集群。换句话说, 集群中的实体应尽可能相似, 而一个集群中的实体应与另一集群中的实体尽可能不相似。
广义上讲, 有两种基于算法结构和操作对数据点进行聚类的方法, 即聚集和分裂。
- 集结:集结方法从在一个不同的(单个)群集中的每个观察开始, 然后连续将群集合并在一起, 直到满足停止条件为止。
- 除法:除法从一个群集中的所有模式开始, 然后进行拆分, 直到满足停止标准为止。
在本教程中, 你将专注于聚集或自下而上的方法, 在该方法中, 你将每个数据点作为自己的群集, 然后根据一些相似性度量来组合群集。这个想法也很容易适用于分裂方法。
群集之间的相似性通常是根据诸如两个群集之间的欧式距离之类的不相似性度量来计算的。因此, 两个群集之间的距离越大, 效果越好。
你可以考虑使用许多距离度量标准来计算差异度量, 并且选择取决于数据集中的数据类型。例如, 如果你的数据集中有连续的数值, 则可以使用欧几里德距离, 如果数据是二进制的, 则可以考虑Jaccard距离(在应用一键编码后处理分类数据以进行聚类时很有用)。其他距离度量包括曼哈顿, 明可夫斯基, 堪培拉等。
集群的预处理操作
开始之前, 你需要注意以下几点。
- 缩放比例
你必须规范化要素值的比例, 以便从聚类过程开始。这是因为每个观察值的特征值都表示为n维空间中的坐标(n是特征数), 然后计算了这些坐标之间的距离。如果未对这些坐标进行归一化, 则可能导致错误的结果。
例如, 假设你有关于三个人的身高和体重的数据:A(6英尺, 75公斤), B(6英尺, 77公斤), C(8英尺, 75公斤)。如果在二维坐标系(高度和重量)中表示这些要素, 并计算它们之间的欧几里得距离, 则以下两对之间的距离为:
A-B:2个单元
A-C:2个单位
好吧, 距离度量告诉我们, A-B和A-C对相似, 但实际上它们显然不一样! A-B对比A-C更相似。因此, 重要的是首先缩放这些值, 然后计算距离。
标准化特征值的方法有多种, 你可以考虑通过应用以下转换来标准化[0, 1]之间的所有特征值(x(i))的整个比例(称为最小-最大归一化):
$ x(s)= x(i)-min(x)/(max(x)-min(x))$
你可以为此使用R的normalize()函数, 也可以编写自己的函数, 例如:
标准化<-function(x){(x-min(x))/(max(x)-min(x))}
其他类型的缩放比例可以通过以下转换来实现:
$ x(s)= x(i)-平均值(x)/ sd(x)$
其中sd(x)是特征值的标准偏差。这将确保你的特征值分布的平均值为0, 标准偏差为1。你可以通过R中的scale()函数来实现。
- 缺失价值估算
预先处理数据集中的丢失/空值/ inf值也很重要。处理这些值的方法有很多, 一种是删除它们或以均值, 中位数, 众数或使用一些高级回归技术来估算它们。 R有许多程序包和函数来处理缺失值的估算, 例如impute(), Amelia, Mice, Hmisc等。你可以在本教程中阅读有关Amelia的信息。
层次聚集集群中的关键操作是将两个最近的集群重复组合成一个更大的集群。首先需要回答三个关键问题:
- 你如何表示一个多点的集群?
- 你如何确定群集的”附近”?
- 什么时候停止组合集群?
希望在本教程结束时, 你将能够回答所有这些问题。在应用分层集群之前, 让我们看一下其工作原理:
- 首先计算每对观察点之间的距离, 然后将其存储在距离矩阵中。
- 然后将每个点放在自己的群集中。
- 然后, 它开始根据与距离矩阵的距离合并最接近的点对, 结果, 簇的数量减少了1。
- 然后, 它会重新计算新群集和旧群集之间的距离, 并将它们存储在新的距离矩阵中。
- 最后, 它重复步骤2和3, 直到所有群集合并到一个群集中为止。
为了确定聚类规则, 有几种方法可以测量聚类之间的距离, 这些方法通常称为”链接方法”。一些常见的链接方法是:
- 完全链接:计算合并之前群集之间的最大距离。
- 单链接:计算合并之前群集之间的最小距离。此链接可用于检测数据集中的高值, 这些值可能会离群, 因为它们将在最后合并。
- 平均链接:计算合并之前群集之间的平均距离。
- 质心链接:找到聚类1的质心和聚类2的质心, 然后在合并之前计算两者之间的距离。
链接方法的选择完全取决于你, 没有任何一种快速有效的方法可以始终为你带来良好的效果。不同的链接方法导致不同的集群。
树状图
在分层聚类中, 你将对象分类为类似于树状图的树状结构, 称为树状图。拆分或合并的距离(称为高度)显示在下面的树状图的y轴上。
在上图中, 首先将4和6合并为一个群集, 即群集1, 因为它们在距离上最接近, 之后是点1和2, 例如群集2。此后, 将5合并到同一群集1中, 然后合并3导致两个群集。最后, 将两个群集合并为一个群集, 这是群集过程停止的地方。
到现在为止, 你可能会很感兴趣的一个问题是, 你如何确定何时停止合并集群?好吧, 这取决于你对数据的领域知识。例如, 如果你基于某个球员在场上的位置将其聚类, 该位置将代表他们的距离以进行距离计算, 则你已经知道应该只以两个聚类结尾, 因为只能有两支球队参加足球比赛。
但是有时你也没有该信息。在这种情况下, 你可以利用树状图的结果来估计群集数。你用水平线将树形图树切割成一定高度, 该高度可以使该线在不与合并点相交的情况下上下移动最大距离。在上述情况下, 它将在高度1.5和2.5之间, 如下所示:
如果按照所示进行切割, 则最终将只有两个簇。
请注意, 不必仅在此类位置进行切割, 你可以根据所需的群集数量选择任意点作为切割点。例如, 低于1.5且高于1的削减将为你提供3个群集。
请注意, 这不是决定簇数的硬性规定。你还可以考虑诸如剪影图, 肘形图之类的图, 或诸如Dunn指数, Hubert伽玛等之类的一些数字量度。这些图显示误差随簇数(k)的变化, 然后选择k的值, 其中误差最小。
衡量集群的优势
在任何无监督的学习任务中, 最重要的部分也许是结果分析。使用任何算法和任何参数集执行聚类后, 需要确保正确执行了聚类。但是, 你如何确定呢?
好吧, 有很多方法可以做到这一点, 也许最受欢迎的一种是邓恩指数。 Dunn指数是最小群集间距离与最大群集内直径之间的比率。簇的直径是其两个最远点之间的距离。为了使群集紧密分离, 你应该追求更高的邓恩氏指数。
现在, 你将运用获得的知识来解决现实世界中的问题。
你将对种子数据集应用分层聚类。该数据集包括对属于三种不同小麦品种的籽粒的几何特性的测量:卡玛, 罗莎和加拿大。它具有描述种子特性的变量, 例如面积, 周长, 不对称系数等。每种小麦都有70个观察值。你可以在此处找到有关数据集的详细信息。
首先使用read.csv()函数将数据集导入数据框。
请注意, 该文件没有任何标题, 并且以制表符分隔。为了保持结果的可重复性, 你需要使用set.seed()函数。
set.seed(786)
file_loc <- 'seeds.txt'
seeds_df <- read.csv(file_loc, sep = '\t', header = FALSE)
由于数据集没有任何列名, 因此你将从数据描述中给自己起列名。
feature_name <- c('area', 'perimeter', 'compactness', 'length.of.kernel', 'width.of.kernal', 'asymmetry.coefficient', 'length.of.kernel.groove', 'type.of.seed')
colnames(seeds_df) <- feature_name
建议收集有关数据集的一些基本有用信息, 例如数据集的维度, 数据类型和分布, NA数量等。你可以通过使用R中的str(), summary()和is.na()函数来做到这一点。
str(seeds_df)
summary(seeds_df)
any(is.na(seeds_df))
请注意, 此数据集的所有列均为数值。群集之前, 你需要清除此数据集中没有丢失的值。但是功能的比例不同, 你需要对其进行规范化。同样, 数据被标记了, 你已经有了关于哪个观测值属于哪个小麦品种的信息。
现在, 你将标签存储在单独的变量中, 并从数据集中排除type.of.seed列, 以便进行聚类。稍后, 你将使用true标签来检查你的聚类效果如何。
seeds_label <- seeds_df$type.of.seed
seeds_df$type.of.seed <- NULL
str(seeds_df)
你会注意到, 你已经从数据集中删除了true标签列。
现在, 你将使用R的scale()函数缩放所有列值。
seeds_df_sc <- as.data.frame(scale(seeds_df))
summary(seeds_df_sc)
请注意, 所有列的均值为0, 标准差为1。现在, 你已经对数据进行了预处理, 现在可以构建距离矩阵了。由于此处的所有值都是连续的数值, 因此你将使用欧几里德距离方法。
dist_mat <- dist(seeds_df_sc, method = 'euclidean')
此时, 你应该确定要使用的链接方法, 然后继续进行层次聚类。你可以尝试各种链接方法, 然后再决定哪种方法效果更好。在这里, 你将继续使用平均链接方法。
你将通过绘制将使用hclust()构建的层次集群对象来构建树状图。你可以通过method参数指定链接方法。
hclust_avg <- hclust(dist_mat, method = 'average')
plot(hclust_avg)
请注意, 树状图的构建方式, 每个数据点最终合并为一个单独的簇, 其高度(距离)显示在y轴上。
接下来, 你可以切割树状图以创建所需数量的群集。因为在这种情况下, 你已经知道只有三种类型的小麦, 所以你将选择簇数为k = 3, 或者如在树状图h = 3中所见, 你将获得三个簇。你将使用R的cutree()函数以hclust_avg作为一个参数, 将另一个参数设为h = 3或k = 3来切割树。
cut_avg <- cutree(hclust_avg, k = 3)
如果你在视觉上希望看到树状图上的聚类, 则可以使用R的abline()函数绘制切割线, 并使用rect.hclust()函数为树上的每个聚类叠加矩形区域, 如以下代码所示:
plot(hclust_avg)
rect.hclust(hclust_avg , k = 3, border = 2:6)
abline(h = 3, col = 'red')
现在, 你可以看到包含在三个不同颜色框中的三个群集。你还可以使用dendextend库中的color_branches()函数来可视化具有不同颜色分支的树。
请记住, 可以使用install.packages(‘package_name’, dependencies = TRUE)命令在R中安装软件包。
suppressPackageStartupMessages(library(dendextend))
avg_dend_obj <- as.dendrogram(hclust_avg)
avg_col_dend <- color_branches(avg_dend_obj, h = 3)
plot(avg_col_dend)
现在, 你将从dplyr包中将带有mutate()的列的结果附加到原始数据帧中的簇名称后面, 并从dplyr包中追加, 并使用count()函数计算为每个群集分配了多少观测值。
suppressPackageStartupMessages(library(dplyr))
seeds_df_cl <- mutate(seeds_df, cluster = cut_avg)
count(seeds_df_cl, cluster)
你将能够看到每个群集中分配了多少个观测值。请注意, 实际上, 从标记的数据中, 你可以对每种小麦进行70次观察。
通常基于你所做的聚类评估两个功能之间的趋势, 以便从数据聚类中提取更多有用的见解。作为练习, 你可以借助ggplot2软件包来分析小麦周长和面积群集之间的趋势。
suppressPackageStartupMessages(library(ggplot2))
ggplot(seeds_df_cl, aes(x=area, y = perimeter, color = factor(cluster))) + geom_point()
注意, 对于所有小麦品种, 其周长与面积之间似乎都存在线性关系。
由于你已经有了该数据集的真实标签, 因此你也可以考虑使用table()函数对你的聚类结果进行交叉检查。
table(seeds_df_cl$cluster, seeds_label)
如果查看生成的表, 你会清楚地看到三个包含55个或更多元素的组。总体而言, 你可以说你的簇充分代表了不同类型的种子, 因为最初你对每种小麦都有70个观测值。较大的组代表群集和实际类型之间的对应关系。
请注意, 在许多情况下, 你实际上并没有真正的标签。在这种情况下, 正如已经讨论过的, 你可以采取其他措施, 例如最大化Dunn指数。你可以使用clValid库中的dunn()函数来计算dunn的索引。同样, 你可以考虑通过制作训练集和测试集来对结果进行交叉验证, 就像在其他任何机器学习算法中所做的一样, 然后在具有真实标签的情况下进行聚类。
你可能已经听说过k-means聚类算法。如果没有, 请看一下本教程。两种算法之间有许多基本差异, 尽管在不同情况下任何一种算法的性能都比另一种更好。一些区别是:
- 使用的距离:层次聚类实际上可以处理任何距离度量, 而k均值依赖于欧几里得距离。
- 结果的稳定性:k均值在初始化时需要一个随机步骤, 如果重新运行该过程可能会产生不同的结果。在分层集群中情况并非如此。
- 簇数:虽然可以使用弯头图, 轮廓图等来计算k均值中正确的簇数, 但是层次化也可以使用所有这些簇, 但具有利用树状图进行相同操作的额外好处。
- 计算复杂度:K-means在计算上比分层聚类便宜, 并且可以在合理的时间范围内在大型数据集上运行, 这是k-means受欢迎的主要原因。
恭喜!你已经完成了本教程的结尾。你学习了如何预处理数据, 层次聚类的基础知识, 距离度量及其所使用的距离度量和链接方法, 以及在R中的用法。你还知道层次聚类与k-means算法有何不同。做得好!但是总会有更多的东西要学习。我建议你看看我们的R中无监督学习课程。