Engee 文档
Notebook

聚类

聚类(或聚类分析)是将一组对象划分为称为 "群组 "的组。在每个群组中都应该有 "相似 "的对象,而不同群组中的对象应该尽可能不同。聚类与分类的主要区别在于,组的列表没有指定,而是在算法过程中确定的。

本示例将展示如何使用 K-means 方法(K-means)和模糊聚类方法(C-means)进行聚类。

连接必要的库:

In [ ]:
Pkg.add(["RDatasets", "Clustering"])
In [ ]:
using Plots
using Clustering 

为聚类分析加载数据集

In [ ]:
using RDatasets
iris = dataset("datasets", "iris");

将数据集中观察对象的属性定义为一个单独的变量:

In [ ]:
iris_species = iris[:,5];

在三维图形上可视化原始数据:

In [ ]:
plotlyjs()
setosaIndex = findall(x->x=="setosa", iris_species)
versicolorIndex = findall(x->x=="versicolor", iris_species)
virginicaIndex = findall(x->x=="virginica", iris_species)
scatter(Matrix(iris[setosaIndex, [:SepalLength]]), Matrix(iris[setosaIndex, [:SepalWidth]]), Matrix(iris[setosaIndex, [:PetalLength]]), color="red")
scatter!(Matrix(iris[versicolorIndex, [:SepalLength]]), Matrix(iris[versicolorIndex, [:SepalWidth]]), Matrix(iris[versicolorIndex, [:PetalLength]]), color="blue")
scatter!(Matrix(iris[virginicaIndex, [:SepalLength]]), Matrix(iris[virginicaIndex, [:SepalWidth]]), Matrix(iris[virginicaIndex, [:PetalLength]]), color="green")
Out[0]:

转换为二维图形,以便进一步比较聚类结果和原始数据:

In [ ]:
gr()
s1 = scatter(Matrix(iris[setosaIndex, [:SepalLength]]), Matrix(iris[setosaIndex, [:SepalWidth]]), color="red", markersize = 5, label="setosa")
s2 = scatter!(Matrix(iris[versicolorIndex, [:SepalLength]]), Matrix(iris[versicolorIndex, [:SepalWidth]]), color="blue", markersize = 5, label="versicolor")
s3 = scatter!(Matrix(iris[virginicaIndex, [:SepalLength]]), Matrix(iris[virginicaIndex, [:SepalWidth]]), color="green", markersize = 5, label="virginica")
p1 = plot(s3)
Out[0]:

将数据转换成可接受的格式,以便用聚类方法进行处理:

In [ ]:
features = collect(Matrix(iris[:, 1:4])')
Out[0]:
4×150 Matrix{Float64}:
 5.1  4.9  4.7  4.6  5.0  5.4  4.6  5.0  …  6.8  6.7  6.7  6.3  6.5  6.2  5.9
 3.5  3.0  3.2  3.1  3.6  3.9  3.4  3.4     3.2  3.3  3.0  2.5  3.0  3.4  3.0
 1.4  1.4  1.3  1.5  1.4  1.7  1.4  1.5     5.9  5.7  5.2  5.0  5.2  5.4  5.1
 0.2  0.2  0.2  0.2  0.2  0.4  0.3  0.2     2.3  2.5  2.3  1.9  2.0  2.3  1.8

K-means 方法

最流行的聚类方法之一是 K-means 法(K-means)。该方法的基本思想是迭代重复两个步骤:

1.将样本对象分配到聚类中; 2.重新计算聚类中心。

将聚类方法应用于原始数据:

In [ ]:
result_kmeans = kmeans(features, 3); #метод kmeans() принимает как аргументы набор данных и количество кластеров

使用点阵图直观显示解决方案:

In [ ]:
plotlyjs()
p2 = scatter(iris.SepalLength, iris.SepalWidth, 
        marker_z = result_kmeans.assignments, 
        color =:red, legend = false, markersize = 5)
Out[0]:

包含结果的变量(result_kmeans)中的赋值参数包含一列为每个观测值分配的聚类数。点阵图的颜色与特定聚类的数量相对应。

K-means 算法结果与原始数据的比较:

In [ ]:
plot(p1, p2)
Out[0]:

从图中可以看出,该算法的结果令人满意,观测值被聚类在与原始数据非常接近的聚类中。

模糊优化法

模糊聚类法可以看作是一种改进的 k-means 方法,其中对所考虑的集合中的每个元素都计算了其属于每个聚类的程度。

将聚类方法应用于原始数据:

In [ ]:
result_fuzzy = fuzzy_cmeans(features, 3, 2, maxiter=2000, display=:iter)
  Iters      center-change
----------------------------
      1       7.771477e+00
      2       4.739898e-01
      3       1.200676e+00
      4       6.755399e-01
      5       3.645266e-01
      6       2.000300e-01
      7       8.829159e-02
      8       4.021639e-02
      9       2.069955e-02
     10       1.182799e-02
     11       7.198234e-03
     12       4.523367e-03
     13       2.885398e-03
     14       1.852891e-03
     15       1.193257e-03
     16       7.693420e-04
Fuzzy C-means converged with 16 iterations (δ = 0.0007693420314963082)
Out[0]:
FuzzyCMeansResult: 3 clusters for 150 points in 4 dimensions (converged in 16 iterations)

根据观察对象的四个属性计算三个聚类的中心点:

In [ ]:
M = result_fuzzy.centers
Out[0]:
4×3 Matrix{Float64}:
 5.00396   5.88825  6.77419
 3.41412   2.76082  3.05214
 1.48276   4.36295  5.64575
 0.253522  1.3968   2.05315

计算观察对象的权重(属于某个群组的程度):

In [ ]:
memberships = result_fuzzy.weights
Out[0]:
150×3 Matrix{Float64}:
 0.996624    0.00230446   0.00107189
 0.975835    0.016663     0.00750243
 0.979814    0.0137683    0.00641748
 0.967404    0.0224829    0.0101134
 0.99447     0.0037622    0.00176788
 0.934536    0.0448343    0.0206299
 0.97948     0.0140129    0.00650756
 0.999547    0.000311776  0.000141288
 0.930335    0.0477519    0.0219135
 0.982709    0.0119456    0.00534496
 0.968026    0.0217691    0.0102053
 0.99213     0.00543676   0.0024328
 0.97062     0.020198     0.00918222
 ⋮                        
 0.0217694   0.748803     0.229428
 0.00347922  0.0288342    0.967687
 0.00507985  0.0376451    0.957275
 0.0153794   0.129099     0.855522
 0.0293134   0.614578     0.356108
 0.00527622  0.0338633    0.96086
 0.00971574  0.0631961    0.927088
 0.0112318   0.105921     0.882848
 0.0257965   0.506297     0.467907
 0.0120658   0.155488     0.832446
 0.0215546   0.188514     0.789932
 0.0269334   0.580575     0.392492

从 0 到 1 的数字表示与聚类中心的距离,其中 1 表示最大归属度(位于中心),0 表示与中心的最大距离。

模糊逻辑方法结果的可视化:

In [ ]:
p3 = scatter(iris.SepalLength, iris.SepalWidth, 
        marker_z = memberships[:,1], 
        legend = true, markersize = 5)

p4 = scatter(iris.SepalLength, iris.SepalWidth, 
        marker_z = memberships[:,3], 
        legend = false, markersize = 5)

p5 = scatter(iris.SepalLength, iris.SepalWidth, 
        marker_z = memberships[:,2], 
        legend = false, markersize = 5)

plot(p3, p4, p5, p1, legend = false)
Out[0]:

前三幅图显示了模糊逻辑法的结果,其中色标显示了属于某个群组的程度。

最后一张图显示的是初始(可靠)数据。

将鼠标光标首先指向原始数据图,然后指向使用该方法得到的点图,就可以比较结果,直观地评估该方法的准确性。

结论

在本例中,我们研究了两种最常用的聚类方法。用于实现这些方法的函数是从 Clustering.jl 库中调用的,该库还提供了许多其他更高级的方法。

应用这些方法得到的结果很好,但并不完美,需要更多的数据来提高算法的质量。