凝聚层次聚类按颜色阈值截断树状图转d3.tree适配JSON方法
问题描述
我通过如下方式生成树状图:
- 使用AgglomerativeClustering算法
- 调用
dendrogram(linkage_x, labels=all_encoded, color_threshold=2.2)方法
得到的运行结果如下图所示:
我希望将该树状图转换为JSON格式,以便后续使用d3.tree()完成可视化。
我需要的目标JSON结构需满足:保留深蓝色部分的层级结构,同时每个彩色聚类簇内的所有节点处于同一层级(无需逐层二分拆分直到叶节点)。
请问是否已有现成函数可实现上述需求?
目前我找到一段可构建完整树状图结构的代码,但不清楚如何修改才能无需穷尽复现完整树状图、实现按阈值截断的效果,参考代码如下:
def add_node(node, parent ): # 新建节点并挂载到父节点的子列表中 newNode = dict( node_id=node.id, children=[] ) parent["children"].append( newNode ) # 递归添加当前节点的子节点 if node.left: add_node( node.left, newNode ) if node.right: add_node( node.right, newNode )
实现方案
没有能直接输出该结构的现成封装函数,直接修改你现有的递归逻辑即可,核心就是在递归过程中增加距离阈值判断,到阈值就停止下钻。
具体逻辑:
- 递归遍历树节点时,先判断当前节点的合并距离是否小于你设置的
color_threshold=2.2 - 若小于阈值,说明当前节点就是图上单个彩色聚类簇的根节点,不需要再往下拆分二分结构,直接把该节点下所有原始样本叶节点收集起来,平级放到该节点的children列表中即可
- 若大于等于阈值,说明属于深蓝色的上层聚类结构,按照原有逻辑继续递归构建左右子节点即可
可直接复用的修改后代码如下:
# 工具函数:收集指定节点下所有原始叶节点 def collect_leaf_nodes(node, leaf_container): # scipy层次聚类规则:id小于总样本数的节点为原始样本叶节点 if node.id < total_sample_num: leaf_container.append({"node_id": node.id, "children": []}) return if node.left: collect_leaf_nodes(node.left, leaf_container) if node.right: collect_leaf_nodes(node.right, leaf_container) def add_node_truncated(node, parent, threshold=2.2): current_node = {"node_id": node.id, "children": []} parent["children"].append(current_node) # 达到截断阈值,停止下钻,直接平铺所有下属叶节点 if node.dist < threshold: collect_leaf_nodes(node, current_node["children"]) return # 未到阈值则继续递归构建上层层级结构 if node.left: add_node_truncated(node.left, current_node, threshold) if node.right: add_node_truncated(node.right, current_node, threshold) # 初始化根结构后调用即可 # root为linkage结果构建的聚类树根节点,total_sample_num为参与聚类的总样本量 result_json = {"node_id": "root", "children": []} add_node_truncated(root, result_json, threshold=2.2)
注意事项:
- 代码中判断用的
node.dist是聚类合并时的距离值,和你传入dendrogram的color_threshold使用完全一致的度量尺度,截断后的分块和你图上的彩色聚类簇完全对应 - 不需要额外依赖其他库,基于你现有的树结构就能跑,输出的结构可以直接喂给d3.tree使用
- 如果需要给节点附加距离、标签等属性,直接在构造
current_node字典时新增对应键值对即可,不影响递归逻辑
内容的提问来源于stack exchange,提问作者SFW
相关产品推荐
相关产品推荐

