当前位置:   article > 正文

模糊数学-层次聚类的艺术:动态聚类可视化与Python(附带全部代码)_层次聚类可视化

层次聚类可视化

由于做模糊数学代码实现的博主太少,导致大学生们面对作业痛苦不堪,现在我准备将我的作业开放给大家参考。如果你觉得这个博文还不错的话,请点点赞支持一下~

层次聚类(Hierarchical Clustering)是一种常用的数据分析方法,它通过计算数据点之间的相似度来构建一个层次结构的聚类树。在层次聚类中,数据被分为不同的层次,从而形成一个由细到粗的聚类结构。这种方法不需要预先指定聚类的数量,而是生成一棵树状图(称为树状图或层次图),通过树状图可以观察数据点之间的层次关系。

层次聚类主要有两种策略:

  1. 凝聚方法(Agglomerative):这是一种自底向上的方法,开始时每个数据点都被视为一个单独的聚类,然后算法逐步找到最相似的聚类对并将它们合并,这个过程一直持续到所有数据点都被合并到一个聚类中,或者达到某个终止条件。

  2. 分裂方法(Divisive):与凝聚方法相反,分裂方法是自顶向下的。最开始将所有数据点视为一个大的聚类,然后逐步将聚类分裂成更小的聚类,直到每个数据点都是一个单独的聚类,或者达到某个终止条件。

想象你要为一群人组织一场派对,但是你希望每个人都能找到志同道合的朋友。你手头有一份客人名单,但是你不知道他们之间的关系。

凝聚层次聚类中,你会这样开始:每个人最初都是自己一个小团体,就像每个人都独自站在派对的角落。然后,你开始观察,找出两个最有可能成为朋友的人,让他们一起聊天。如果他们聊得来,就把他们放在一起,形成一个小团体。接下来,你再找下一对可能的朋友,重复这个过程。随着时间的推移,这些小团体会逐渐合并成更大的团体,直到最后,可能所有的人都聚在一起,或者形成几个大团体,每个团体都有共同的兴趣和话题。

而在分裂层次聚类中,情况正好相反。你开始时把所有人都放在一个大团体里,就像是派对开始时大家都聚在一起。但是你注意到,不是所有人都在积极交谈。于是,你开始将那些不太参与的人分出去,让他们形成一个新的小团体。这个过程一直持续,直到每个人都找到了最适合自己的小团体。

在层次聚类中,聚类之间的距离可以通过多种方式来计算,例如最小距离、最大距离、平均距离等。选择不同的距离计算方法会影响聚类的结果。

层次聚类的优点包括:

  • 不需要预先指定聚类数目。
  • 能够发现数据的层次结构。
  • 可以处理非球形的聚类。

缺点则包括:

  • 计算复杂度较高,尤其是在数据量大的情况下。
  • 对噪声和异常值比较敏感。
  • 一旦合并或分裂,就不能调整。

可视化效果图:

  1. import numpy as np
  2. import warnings
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. from scipy.cluster.hierarchy import dendrogram
  6. from matplotlib.animation import FuncAnimation
  7. warnings.filterwarnings("ignore")
  8. # 定义F相似矩阵
  9. matrix = np.array([[1, 0.4, 0.8, 0.5, 0.5],
  10. [0.4, 1, 0.4, 0.4, 0.4],
  11. [0.8, 0.4, 1, 0.5, 0.5],
  12. [0.5, 0.4, 0.5, 1, 0.6],
  13. [0.5, 0.4, 0.5, 0.6, 1]])
  14. matrix = np.array([[1, 0.8, 0.6, 0.1, 0.2],
  15. [0.8, 1, 0.8, 0.2, 0.85],
  16. [0.6, 0.8, 1, 0, 0.9],
  17. [0.1, 0.2, 0, 1, 0.1],
  18. [0.2, 0.85, 0.9, 0.1, 1]])
  19. # 平方法求传递闭包
  20. def get_tR(r_matrix):
  21. rows = r_matrix.shape[0] # 矩阵的行数
  22. cols = r_matrix.shape[1] # 矩阵的列数
  23. min_list = [] # 存储每次比较的最小值
  24. new_mat = np.zeros((rows, cols), dtype='float') # 初始化新矩阵
  25. for m in range(rows):
  26. for n in range(cols):
  27. min_list = [] # 清空最小值列表
  28. now_row = r_matrix[m] # 当前行
  29. for k in range(len(now_row)):
  30. # 先取小,再取大
  31. min_cell = min(r_matrix[m][k], r_matrix[:, n][k]) # 先取小
  32. min_list.append(min_cell) # 添加到最小值列表
  33. new_mat[m][n] = max(min_list) # 再取大,并赋值给新矩阵
  34. return new_mat
  35. # 求传递闭包矩阵T
  36. t_r_matrix = matrix # 初始化T为F相似矩阵
  37. i = 0 # 记录迭代次数
  38. while True:
  39. t_r_matrix = get_tR(t_r_matrix) # 对T进行平方法求传递闭包
  40. if (t_r_matrix == matrix).all(): # 如果T不变,则停止迭代
  41. break
  42. else: # 否则更新T为新矩阵,并继续迭代
  43. matrix = t_r_matrix
  44. i = i + 1
  45. # 求lambda值列表L
  46. lambda_list = [] # 初始化L为空列表
  47. for i in range(t_r_matrix.shape[0]):
  48. for j in range(t_r_matrix.shape[1]):
  49. lambda_list.append(t_r_matrix[i][j]) # 提取T中的所有元素到L中
  50. lambda_list = list(set(lambda_list)) # 去重
  51. lambda_list.sort() # 排序
  52. # 403
  53. # 按lambda截集进行动态聚类
  54. def lambda_clustering(t_r_matrix):
  55. rows = t_r_matrix.shape[0] # 矩阵的行数
  56. cols = t_r_matrix.shape[1] # 矩阵的列数
  57. result = [] # 返回的结果,存储每个lambda值对应的聚类结果和类别数目
  58. # aa=[]
  59. for i in range(len(lambda_list)): # 遍历每个lambda值
  60. i = len(lambda_list) - i - 1
  61. temp_matrix = np.zeros((rows, cols), dtype='float') # 初始化一个0-1矩阵
  62. class_list = [] # 存储当前lambda值的聚类结果
  63. mark_list = [] # 存储当前lambda值已经被分组的样本
  64. for m in range(rows):
  65. for n in range(cols):
  66. if t_r_matrix[m][n] >= lambda_list[i]: # 如果T中的元素大于等于lambda值,则赋值为1
  67. temp_matrix[m][n] = 1
  68. # 对0-1矩阵进行行比较,得到聚类结果
  69. for m in range(rows):
  70. if (m + 1) in mark_list: # 如果当前样本已经被分组,则跳过
  71. continue
  72. now_class = [] # 存储当前样本所在的类别
  73. now_class.append(m + 1) # 添加当前样本到类别中
  74. mark_list.append(m + 1) # 添加当前样本到已分组的样本中
  75. for n in range(m + 1, rows):
  76. if (temp_matrix[m] == temp_matrix[n]).all(): # 如果两行相等,则表示两个样本属于同一类别
  77. now_class.append(n + 1) # 添加另一个样本到类别中
  78. mark_list.append(n + 1) # 添加另一个样本到已分组的样本中
  79. # aa.append(mark_list)
  80. class_list.append(now_class) # 添加当前类别到聚类结果中
  81. # if mark_list not in aa:
  82. # print(f"mark_list:{mark_list}")
  83. # print(f"aa:{aa}")
  84. result.append([lambda_list[i], class_list, len(class_list)]) # 添加当前lambda值对应的聚类结果和类别数目到结果中
  85. return result
  86. # 调用函数,得到动态聚类结果
  87. result = lambda_clustering(t_r_matrix)
  88. print(result)
  89. # print(bb)
  90. # 打印动态聚类结果
  91. for item in result:
  92. print(f"lambda = {item[0]}, 聚类结果为 {item[1]}, 可分为 {item[2]} 类")
  93. lambda_list2 = []
  94. new_label = []
  95. x_label = result[-1][1][0].copy()
  96. len_class = len(result[0][1])
  97. for i in result:
  98. if i != result[0]:
  99. lambda_list2.append(i[0])
  100. v = 0.01
  101. # 提取聚类结果中,聚类的顺序bb
  102. group = [i[1] for i in result]
  103. aa = [[i] for i in x_label]
  104. bb = []
  105. for i in group:
  106. for j in i:
  107. if j not in aa:
  108. bb.append(j)
  109. aa.append(j)
  110. # 复制原始bb
  111. bb2 = []
  112. for i in bb:
  113. bb2.append([j for j in i])
  114. # 将聚类后的元素序号做成一个字典
  115. max_label = max(x_label) + 1
  116. dict1 = {}
  117. for i in range(len(bb)):
  118. dict1[i] = max_label
  119. max_label = max_label + 1
  120. # 由bb构建一个Z矩阵
  121. for i in range(len(bb)):
  122. for j in range(i):
  123. if bb[j][0] in bb[i]:
  124. for h in bb[j]:
  125. if h in bb[i]:
  126. bb[i].remove(h)
  127. bb[i].append(dict1[j])
  128. Z = []
  129. for i, b, l in zip(bb, lambda_list2, bb2):
  130. guodu = []
  131. for j in i:
  132. guodu.append(float(j - 1))
  133. guodu.append(float('%.2f' % (1 - b)))
  134. guodu.append(float(len(l)))
  135. Z.append(guodu)
  136. # 自定义颜色函数
  137. def color_func(k, threshold, size):
  138. if Z[k - size][2] < threshold:
  139. return 'black'
  140. else:
  141. return 'w'
  142. # 创建一个空白图形
  143. fig, ax = plt.subplots()
  144. # 定义更新函数
  145. def update(frame):
  146. ax.clear()
  147. threshold = frame
  148. dendrogram(Z, labels=np.arange(1, len(x_label) + 1), orientation='bottom', above_threshold_color='w', color_threshold=threshold,
  149. link_color_func=lambda k: color_func(k, threshold, len(x_label)))
  150. for i in lambda_list2:
  151. if frame >= 1 - i:
  152. ax.plot([1, 60], [1 - i, 1 - i], linestyle='--', color='b', alpha=0.2)
  153. ax.set_yticklabels([1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2])
  154. ax.plot([1, 60], [frame, frame], linestyle='--', color='r', alpha=0.5)
  155. ax.set_xlabel('class')
  156. ax.set_ylabel('lambda')
  157. ax.set_title(f'Dynamic graph')
  158. # 创建动画
  159. thresholds = np.linspace(0, 1, 50) # 生成0到1的阈值范围
  160. ani = FuncAnimation(fig, update, frames=thresholds, repeat=False)
  161. ani.save('dynamic_animation2.gif', writer='pillow')
  162. plt.show()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/774615
推荐阅读
相关标签
  

闽ICP备14008679号