Scipyで階層クラスタリングを実行した時に、結果として出力されるデンドログラムをSeaviewのような系統樹ビューアで見れるようにしたいと思ったのでNewick形式での出力を試みた。
やり方を調べてみると、stackoverflowに今回の目的と同じことを質問している人がいて、参考になる実装コードが回答に提供されていた。
以降、上記サイトのコードを参考に実装例と出力結果を記載する。
コード実装例
クラスタリング結果のデンドログラムツリーは木構造ノードオブジェクト(ClusterNode)で表現される。下記の実装(dendrogram2newick)では、木構造のルートノードから再帰的に子ノードを取得して、各ノードに対応するデータを文字情報として連結していくことでNewick形式へ変換する。
import numpy as np
import scipy.cluster.hierarchy as hc
from scipy.cluster.hierarchy import ClusterNode
from typing import List
def dendrogram2newick(
node: ClusterNode, parent_dist: float, leaf_names: List[str], newick: str = ""
) -> str:
"""Convert scipy dendrogram tree to newick format tree
Args:
node (ClusterNode): Tree node
parent_dist (float): Parent distance
leaf_names (List[str]): Leaf names
newick (str): newick format string (Used in recursion)
Returns:
str: Newick format tree
"""
if node.is_leaf():
return f"{leaf_names[node.id]}:{(parent_dist - node.dist):.2f}{newick}"
else:
if len(newick) > 0:
newick = f"):{(parent_dist - node.dist):.2f}{newick}"
else:
newick = ");"
newick = dendrogram2newick(node.left, node.dist, leaf_names, newick)
newick = dendrogram2newick(node.right, node.dist, leaf_names, f",{newick}")
newick = f"({newick}"
return newick
# クラスタリング結果が明らかなテストデータ
num_list = [2, 5, 10, 3, 0, 6, 7, 9]
name_list = [f"num={num}" for num in num_list]
# クラスタリングしてデンドログラムツリーを取得
linkage = hc.linkage([[n] for n in num_list], method="ward")
tree = hc.to_tree(linkage)
# dendrogram2newickでNewick形式に変換
newick = dendrogram2newick(tree, tree.dist, name_list)
print(newick)
出力結果
((((num=6:1.00,num=5:1.00):0.73,num=7:1.73):3.69,(num=9:1.00,num=10:1.00):4.42):5.68,((num=3:1.00,num=2:1.00):1.89,num=0:2.89):8.22);