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

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

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

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

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

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

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

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

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

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

层次聚类的优点包括:

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

缺点则包括:

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

可视化效果图:

import numpy as np
import warnings
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram
from matplotlib.animation import FuncAnimation

warnings.filterwarnings("ignore")

# 定义F相似矩阵
matrix = np.array([[1, 0.4, 0.8, 0.5, 0.5],
                   [0.4, 1, 0.4, 0.4, 0.4],
                   [0.8, 0.4, 1, 0.5, 0.5],
                   [0.5, 0.4, 0.5, 1, 0.6],
                   [0.5, 0.4, 0.5, 0.6, 1]])

matrix = np.array([[1, 0.8, 0.6, 0.1, 0.2],
                   [0.8, 1, 0.8, 0.2, 0.85],
                   [0.6, 0.8, 1, 0, 0.9],
                   [0.1, 0.2, 0, 1, 0.1],
                   [0.2, 0.85, 0.9, 0.1, 1]])


# 平方法求传递闭包
def get_tR(r_matrix):
    rows = r_matrix.shape[0]  # 矩阵的行数
    cols = r_matrix.shape[1]  # 矩阵的列数
    min_list = []  # 存储每次比较的最小值
    new_mat = np.zeros((rows, cols), dtype='float')  # 初始化新矩阵
    for m in range(rows):
        for n in range(cols):
            min_list = []  # 清空最小值列表
            now_row = r_matrix[m]  # 当前行
            for k in range(len(now_row)):
                # 先取小,再取大
                min_cell = min(r_matrix[m][k], r_matrix[:, n][k])  # 先取小
                min_list.append(min_cell)  # 添加到最小值列表
            new_mat[m][n] = max(min_list)  # 再取大,并赋值给新矩阵
    return new_mat


# 求传递闭包矩阵T
t_r_matrix = matrix  # 初始化T为F相似矩阵
i = 0  # 记录迭代次数
while True:
    t_r_matrix = get_tR(t_r_matrix)  # 对T进行平方法求传递闭包
    if (t_r_matrix == matrix).all():  # 如果T不变,则停止迭代
        break
    else:  # 否则更新T为新矩阵,并继续迭代
        matrix = t_r_matrix
        i = i + 1

# 求lambda值列表L
lambda_list = []  # 初始化L为空列表
for i in range(t_r_matrix.shape[0]):
    for j in range(t_r_matrix.shape[1]):
        lambda_list.append(t_r_matrix[i][j])  # 提取T中的所有元素到L中

lambda_list = list(set(lambda_list))  # 去重
lambda_list.sort()  # 排序

# 403
# 按lambda截集进行动态聚类
def lambda_clustering(t_r_matrix):
    rows = t_r_matrix.shape[0]  # 矩阵的行数
    cols = t_r_matrix.shape[1]  # 矩阵的列数
    result = []  # 返回的结果,存储每个lambda值对应的聚类结果和类别数目
    # aa=[]
    for i in range(len(lambda_list)):  # 遍历每个lambda值
        i = len(lambda_list) - i - 1
        temp_matrix = np.zeros((rows, cols), dtype='float')  # 初始化一个0-1矩阵
        class_list = []  # 存储当前lambda值的聚类结果
        mark_list = []  # 存储当前lambda值已经被分组的样本

        for m in range(rows):
            for n in range(cols):
                if t_r_matrix[m][n] >= lambda_list[i]:  # 如果T中的元素大于等于lambda值,则赋值为1
                    temp_matrix[m][n] = 1

                    # 对0-1矩阵进行行比较,得到聚类结果
        for m in range(rows):
            if (m + 1) in mark_list:  # 如果当前样本已经被分组,则跳过
                continue
            now_class = []  # 存储当前样本所在的类别
            now_class.append(m + 1)  # 添加当前样本到类别中
            mark_list.append(m + 1)  # 添加当前样本到已分组的样本中
            for n in range(m + 1, rows):
                if (temp_matrix[m] == temp_matrix[n]).all():  # 如果两行相等,则表示两个样本属于同一类别
                    now_class.append(n + 1)  # 添加另一个样本到类别中
                    mark_list.append(n + 1)  # 添加另一个样本到已分组的样本中
                    # aa.append(mark_list)
            class_list.append(now_class)  # 添加当前类别到聚类结果中
            # if mark_list not in aa:
            # print(f"mark_list:{mark_list}")
            # print(f"aa:{aa}")
        result.append([lambda_list[i], class_list, len(class_list)])  # 添加当前lambda值对应的聚类结果和类别数目到结果中

    return result


# 调用函数,得到动态聚类结果
result = lambda_clustering(t_r_matrix)
print(result)
# print(bb)
# 打印动态聚类结果
for item in result:
    print(f"lambda = {item[0]}, 聚类结果为 {item[1]}, 可分为 {item[2]} 类")

lambda_list2 = []
new_label = []
x_label = result[-1][1][0].copy()
len_class = len(result[0][1])
for i in result:
    if i != result[0]:
        lambda_list2.append(i[0])
v = 0.01

# 提取聚类结果中,聚类的顺序bb
group = [i[1] for i in result]
aa = [[i] for i in x_label]
bb = []
for i in group:
    for j in i:
        if j not in aa:
            bb.append(j)
            aa.append(j)

# 复制原始bb
bb2 = []
for i in bb:
    bb2.append([j for j in i])

# 将聚类后的元素序号做成一个字典
max_label = max(x_label) + 1
dict1 = {}
for i in range(len(bb)):
    dict1[i] = max_label
    max_label = max_label + 1

# 由bb构建一个Z矩阵
for i in range(len(bb)):
    for j in range(i):
        if bb[j][0] in bb[i]:
            for h in bb[j]:
                if h in bb[i]:
                    bb[i].remove(h)
            bb[i].append(dict1[j])
Z = []
for i, b, l in zip(bb, lambda_list2, bb2):
    guodu = []
    for j in i:
        guodu.append(float(j - 1))
    guodu.append(float('%.2f' % (1 - b)))
    guodu.append(float(len(l)))
    Z.append(guodu)


# 自定义颜色函数
def color_func(k, threshold, size):
    if Z[k - size][2] < threshold:
        return 'black'
    else:
        return 'w'


# 创建一个空白图形
fig, ax = plt.subplots()


# 定义更新函数
def update(frame):
    ax.clear()
    threshold = frame
    dendrogram(Z, labels=np.arange(1, len(x_label) + 1), orientation='bottom', above_threshold_color='w', color_threshold=threshold,
               link_color_func=lambda k: color_func(k, threshold, len(x_label)))
    for i in lambda_list2:
        if frame >= 1 - i:
            ax.plot([1, 60], [1 - i, 1 - i], linestyle='--', color='b', alpha=0.2)
    ax.set_yticklabels([1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2])
    ax.plot([1, 60], [frame, frame], linestyle='--', color='r', alpha=0.5)
    ax.set_xlabel('class')
    ax.set_ylabel('lambda')
    ax.set_title(f'Dynamic graph')


# 创建动画
thresholds = np.linspace(0, 1, 50)  # 生成0到1的阈值范围
ani = FuncAnimation(fig, update, frames=thresholds, repeat=False)

ani.save('dynamic_animation2.gif', writer='pillow')
plt.show()

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值