1. 聚类是针对给定的样本,依据它们属性的相似度或距离,将其归并到若干个“类”或“簇”的数据分析问题。一个类是样本的一个子集。直观上,相似的样本聚集在同类,不相似的样本分散在不同类。

聚类和分类最大的不同在于:分类的目标是事先已知的,而聚类则不一样,聚类事先不知道目标变量是什么,类别没有像分类那样被预先定义出来。

  1. 距离或相似度度量在聚类中起着重要作用。

常用的距离度量有闵可夫斯基距离,包括欧氏距离、曼哈顿距离、切比雪夫距离、以及马哈拉诺比斯距离。常用的相似度度量有相关系数、夹角余弦。 用距离度量相似度时,距离越小表示样本越相似;用相关系数时,相关系数越大表示样本越相似。

  1. 类是样本的子集,比如有如下基本定义: 用k均值聚类 - 图1表示类或簇,用k均值聚类 - 图2,k均值聚类 - 图3;等表示类中的样本,用k均值聚类 - 图4表示样本k均值聚类 - 图5与样本k均值聚类 - 图6之间的距离。如果对任意的k均值聚类 - 图7,有k均值聚类 - 图8则称k均值聚类 - 图9为一个类或簇。

描述类的特征的指标有中心、直径、散布矩阵、协方差矩阵。

  1. 聚类过程中用到类与类之间的距离也称为连接类与类之间的距离包括最短距离、最长距离、中心距离、平均距离。


  1. 层次聚类假设类别之间存在层次结构,将样本聚到层次化的类中层次聚类又有聚合或自下而上、分裂或自上而下两种方法。

聚合聚类开始将每个样本各自分到一个类;之后将相距最近的两类合并,建立一个新的类,重复此操作直到满足停止条件;得到层次化的类别。分裂聚类开始将所有样本分到一个类;之后将已有类中相距最远的样本分到两个新的类,重复此操作直到满足停止条件;得到层次化的类别。
聚合聚类需要预先确定下面三个要素:
(1)距离或相似度; (2)合并规则; (3)停止条件。
根据这些概念的不同组合,就可以得到不同的聚类方法。

  1. k均值聚类 - 图10均值聚类是常用的聚类算法,有以下特点。基于划分的聚类方法;类别数k事先指定;以欧氏距离平方表示样本之间的距离或相似度,以中心或样本的均值表示类别;以样本和其所属类的中心之间的距离的总和为优化的目标函数;得到的类别是平坦的、非层次化的;算法是迭代算法,不能保证得到全局最优。

k均值聚类 - 图11均值聚类算法,首先选择k个类的中心,将样本分到与中心最近的类中,得到一个聚类结果;然后计算每个类的样本的均值,作为类的新的中心;重复以上步骤,直到收敛为止。

层次聚类


  1. 聚合(自下而上):聚合法开始将每个样本各自分裂到一个类,之后将相距最近的两类合并,建立一个新的类,重复次操作知道满足停止条件,得到层次化的类别。
  2. 分裂(自上而下): 分裂法开始将所有样本分到一个类,之后将已有类中相距最远的样本分到两个新的类,重复此操作直到满足停止条件,得到层次化的类别。 ```python import math import random import numpy as np from sklearn import datasets,cluster import matplotlib.pyplot as plt

iris = datasets.load_iris() gt = iris[‘target’];gt ‘’’ array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) ‘’’

iris[‘data’][:,:2].shape

(150, 2)

data = iris[‘data’][:,:2] x = data[:,0] y = data[:,1]

plt.scatter(x, y, color=’green’) plt.xlim(4, 8) plt.ylim(1, 5) plt.show()

  1. <a name="vtg8K"></a>
  2. ### 层次聚类(聚合法)
  3. ```python
  4. # 定义聚类数的节点
  5. class ClusterNode:
  6. def __init__(self, vec, left=None, right=None, distance=-1, id=None, count=1):
  7. """
  8. :param vec: 保存两个数据聚类后形成新的中心
  9. :param left: 左节点
  10. :param right: 右节点
  11. :param distance: 两个节点的距离
  12. :param id: 用来标记哪些节点是计算过的
  13. :param count: 这个节点的叶子节点个数
  14. """
  15. self.vec = vec
  16. self.left = left
  17. self.right = right
  18. self.distance = distance
  19. self.id = id
  20. self.count = count
  21. def euler_distance(point1: np.ndarray, point2: list) -> float:
  22. """
  23. 计算两点之间的欧拉距离,支持多维
  24. """
  25. distance = 0.0
  26. for a, b in zip(point1, point2):
  27. distance += math.pow(a - b, 2)
  28. return math.sqrt(distance)
  29. # 层次聚类(聚合法)
  30. class Hierarchical:
  31. def __init__(self, k):
  32. self.k = k
  33. self.labels = None
  34. def fit(self, x):
  35. # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标
  36. nodes = [ClusterNode(vec=v, id=i) for i, v in enumerate(x)]
  37. distances = {}
  38. point_num, feature_num = x.shape
  39. self.labels = [-1] * point_num
  40. currentclustid = -1
  41. while(len(nodes)) > self.k:
  42. min_dist = math.inf
  43. nodes_len = len(nodes)
  44. closest_part = None
  45. for i in range(nodes_len - 1):
  46. for j in range(i+1, nodes_len):
  47. d_key = (nodes[i].id, nodes[j].id)
  48. if d_key not in distances:
  49. distances[d_key] = euler_distance(nodes[i].vec, nodes[j].vec)
  50. d = distances[d_key]
  51. if d < min_dist:
  52. min_dist = d
  53. closest_part = (i, j)
  54. part1, part2 = closest_part
  55. node1, node2 = nodes[part1], nodes[part2]
  56. new_vec = [ (node1.vec[i] * node1.count + node2.vec[i] * node2.count ) /
  57. (node1.count + node2.count)
  58. for i in range(feature_num)]
  59. new_node = ClusterNode(vec=new_vec,
  60. left=node1,
  61. right=node2,
  62. distance=min_dist,
  63. id=currentclustid,
  64. count=node1.count + node2.count)
  65. currentclustid -= 1
  66. del nodes[part2], nodes[part1]
  67. nodes.append(new_node)
  68. self.nodes = nodes
  69. self.calc_label()
  70. def calc_label(self):
  71. """
  72. 调取聚类的结果
  73. """
  74. for i, node in enumerate(self.nodes):
  75. # 将节点的所有叶子节点都分类
  76. self.leaf_traversal(node, i)
  77. def leaf_traversal(self, node: ClusterNode, label):
  78. """
  79. 递归遍历叶子节点
  80. """
  81. if node.left == None and node.right == None:
  82. self.labels[node.id] = label
  83. if node.left:
  84. self.leaf_traversal(node.left, label)
  85. if node.right:
  86. self.leaf_traversal(node.right, label)
  87. # https://zhuanlan.zhihu.com/p/32438294
  88. my = Hierarchical(3)
  89. my.fit(data)
  90. labels = np.array(my.labels)
  91. print(labels)
  92. '''
  93. [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
  94. 2 2 2 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 2 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0
  95. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 2 0 0 0 1 0 0 1 2 1 0 1 0
  96. 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
  97. 0 0]
  98. '''
  99. # visualize result
  100. cat1 = data[np.where(labels==0)]
  101. cat2 = data[np.where(labels==1)]
  102. cat3 = data[np.where(labels==2)]
  103. plt.scatter(cat1[:,0], cat1[:,1], color='green')
  104. plt.scatter(cat2[:,0], cat2[:,1], color='red')
  105. plt.scatter(cat3[:,0], cat3[:,1], color='blue')
  106. plt.title('Hierarchical clustering with k=3')
  107. plt.xlim(4, 8)
  108. plt.ylim(1, 5)
  109. plt.show()

scikit-learn

  1. sk = cluster.AgglomerativeClustering(3)
  2. sk.fit(data)
  3. labels_ = sk.labels_
  4. print(labels_)
  5. '''
  6. [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  7. 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 2 0 2 0 1 0 1 1 0 2 0 2 0 2 2 2 2 0 0 2 0
  8. 0 0 0 0 0 2 2 2 2 0 2 0 0 2 2 2 2 0 2 1 2 2 2 0 1 2 0 2 0 0 0 0 1 0 0 0 0
  9. 0 0 2 2 0 0 0 0 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 2 0
  10. 0 0]
  11. '''
  12. # visualize result of sklearn
  13. cat1_ = data[np.where(labels_==0)]
  14. cat2_ = data[np.where(labels_==1)]
  15. cat3_ = data[np.where(labels_==2)]
  16. plt.scatter(cat1_[:,0], cat1_[:,1], color='green')
  17. plt.scatter(cat2_[:,0], cat2_[:,1], color='red')
  18. plt.scatter(cat3_[:,0], cat3_[:,1], color='blue')
  19. plt.title('Hierarchical clustering with k=3')
  20. plt.xlim(4, 8)
  21. plt.ylim(1, 5)
  22. plt.show()

k均值聚类


k均值聚类是基于中心的聚类方法,通过迭代,将样本分到k个类中,使得每个样本与其所属类的中心或均值最近,得到k个平坦的,非层次化的类别,构成对空间的划分。

K-means

  1. # kmeans
  2. class MyKmeans:
  3. def __init__(self, k, n=20):
  4. self.k = k
  5. self.n = n
  6. def fit(self, x, centers=None):
  7. # 第一步,随机选择 K 个点, 或者指定
  8. if centers is None:
  9. idx = np.random.randint(low=0, high=len(x), size=self.k)
  10. centers = x[idx]
  11. #print(centers)
  12. inters = 0
  13. while inters < self.n:
  14. #print(inters)
  15. #print(centers)
  16. points_set = {key: [] for key in range(self.k)}
  17. # 第二步,遍历所有点 P,将 P 放入最近的聚类中心的集合中
  18. for p in x:
  19. nearest_index = np.argmin(np.sum((centers - p) ** 2, axis=1) ** 0.5)
  20. points_set[nearest_index].append(p)
  21. # 第三步,遍历每一个点集,计算新的聚类中心
  22. for i_k in range(self.k):
  23. centers[i_k] = sum(points_set[i_k])/len(points_set[i_k])
  24. inters += 1
  25. return points_set, centers
  26. m = MyKmeans(3)
  27. points_set, centers = m.fit(data)
  28. centers
  29. '''
  30. array([[5.006 , 3.428 ],
  31. [6.81276596, 3.07446809],
  32. [5.77358491, 2.69245283]])
  33. '''
  34. # visualize result
  35. cat1 = np.asarray(points_set[0])
  36. cat2 = np.asarray(points_set[1])
  37. cat3 = np.asarray(points_set[2])
  38. for ix, p in enumerate(centers):
  39. plt.scatter(p[0], p[1], color='C{}'.format(ix), marker='^', edgecolor='black', s=256)
  40. plt.scatter(cat1_[:,0], cat1_[:,1], color='green')
  41. plt.scatter(cat2_[:,0], cat2_[:,1], color='red')
  42. plt.scatter(cat3_[:,0], cat3_[:,1], color='blue')
  43. plt.title('Hierarchical clustering with k=3')
  44. plt.xlim(4, 8)
  45. plt.ylim(1, 5)
  46. plt.show()

sklearn

  1. # using sklearn
  2. from sklearn.cluster import KMeans
  3. # n_clusters: 即我们的k值, max_iter: 最大的迭代次数
  4. kmeans = KMeans(n_clusters=3, max_iter=100).fit(data)
  5. gt_labels__ = kmeans.labels_
  6. centers__ = kmeans.cluster_centers_
  7. gt_labels__
  8. '''
  9. array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  10. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  11. 1, 1, 1, 1, 1, 1, 0, 0, 0, 2, 0, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0,
  12. 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2,
  13. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0,
  14. 0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0,
  15. 0, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2])
  16. '''
  17. centers__
  18. '''
  19. array([[6.81276596, 3.07446809],
  20. [5.006 , 3.428 ],
  21. [5.77358491, 2.69245283]])
  22. '''
  23. # visualize result
  24. cat1 = data[gt_labels__ == 0]
  25. cat2 = data[gt_labels__ == 1]
  26. cat3 = data[gt_labels__ == 2]
  27. for ix, p in enumerate(centers__):
  28. plt.scatter(p[0], p[1], color='C{}'.format(ix), marker='^', edgecolor='black', s=256)
  29. plt.scatter(cat1_[:,0], cat1_[:,1], color='green')
  30. plt.scatter(cat2_[:,0], cat2_[:,1], color='red')
  31. plt.scatter(cat3_[:,0], cat3_[:,1], color='blue')
  32. plt.title('kmeans using sklearn with k=3')
  33. plt.xlim(4, 8)
  34. plt.ylim(1, 5)
  35. plt.show()

寻找 K 值

  1. from sklearn.cluster import KMeans
  2. loss = []
  3. for i in range(1, 10):
  4. kmeans = KMeans(n_clusters=i, max_iter=100).fit(data)
  5. loss.append(kmeans.inertia_ / len(data) / 3) # 样本距离最近的聚类中心的距离总和,inertia值越小,说明聚类效果越好
  6. plt.title('K with loss')
  7. plt.plot(range(1, 10), loss)
  8. plt.show()

例 14.2

  1. X = [[0, 2], [0, 0], [1, 0], [5, 0], [5, 2]]
  2. np.asarray(X)
  3. '''
  4. array([[0, 2],
  5. [0, 0],
  6. [1, 0],
  7. [5, 0],
  8. [5, 2]])
  9. '''
  10. m = MyKmeans(2, 100)
  11. points_set, centers = m.fit(np.asarray(X))
  12. points_set
  13. '''
  14. {0: [array([0, 2]), array([0, 0]), array([1, 0])],
  15. 1: [array([5, 0]), array([5, 2])]}
  16. '''
  17. centers
  18. '''
  19. array([[0, 0], [5, 1]])
  20. '''
  21. #---------------------------------------
  22. kmeans = KMeans(n_clusters=2, max_iter=100).fit(np.asarray(X))
  23. kmeans.labels_
  24. '''
  25. array([0, 0, 0, 1, 1])
  26. '''
  27. kmeans.cluster_centers_
  28. '''
  29. array([[0.33333333, 0.66666667], [5. , 1. ]])
  30. '''