如何把 ETE 3 系统发育分析代码迁移到 ETE 4
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
ETE 4 是一次破坏性 API 修订,不是简单的换导入名升级:包名从ete3变为ete4,Newick 选择的format=参数改为parser=,节点元数据从features改为props,iter_leaves()一类方法改为返回迭代器的leaves()。本文基于 scientific-agent-skills 仓库中 etetoolkit 技能的迁移参考文档(migration-ete3-to-ete4.md),目标版本为ETE 4.4.0(2025 年 9 月 3 日发布;ETE 4.0.0 与 4.1.1 于 2025 年 3 月 28 日发布,4.1.1 起 ETE 4 标记为 out of beta 并上架 PyPI)。按文操作后,你能把一套基于ete3的旧分析代码改写成在ete4上运行、且科学输出可核对的新代码。
准备:安装并确认 ETE 4.4.0 环境
在开始改代码之前,先按文档固定版本安装基础包:
uv pip install "ete4==4.4.0"如果旧项目确实还需要 ETE 3,文档建议只在必要时让两个 API 并存,并且不要在同一条代码路径里隐式混用两者——要明确命名兼容边界并分别测试。
安装完成后确认当前环境:
uv run --with "ete4==4.4.0" python -c "import ete4; print(ete4.__version__)"打印4.4.0说明隔离运行环境里的包版本符合迁移文档的验证基线。若工作流还涉及渲染,按需只加对应 extra:SmartView 静态 PNG 截图需要ete4[render-sm],Qt 树视图(矢量 PDF/SVG)需要ete4[treeview],命令形如uv pip install "ete4[treeview]==4.4.0"。
迁移第一步:机械检索 ETE 3 符号
文档给出了一个可直接用于搜索旧代码的模式清单。先在代码库中检索以下字符串,定位所有需要改写的调用点:
from ete3 TreeNode format= features= feature= attributes= attribute= add_feature add_features .features get_ascii get_tree_root get_common_ancestor get_leaves iter_leaves get_descendants iter_descendants get_leaf_names get_leaves_by_name convert_to_ultrametric topology_only is_leaf() is_root() & " ClusterTree TreeStyle NodeStyle命中之后,对照下面的逐项映射替换;替换完成后再检查 Newick 解析器、迭代器消费方式、属性导出语义、可视化布局这几类容易漏掉的点。
逐项替换 API 变更
导入与类名
# ETE 3 from ete3 import NCBITaxa, PhyloTree, Tree# ETE 4 from ete4 import GTDBTaxa, NCBITaxa, PhyloTree, TreeETE 3 中TreeNode和Tree是等价类,ETE 4 只用Tree。可视化相关导入也搬了家:
# ETE 3 from ete3 import NodeStyle, TextFace, TreeStyle # ETE 4 from ete4.treeview import NodeStyle, TextFace, TreeStyle如果代码用到 Web 可视化:
from ete4.smartview import Layout, PropFace, TextFace注意 SmartView 和 treeview 的TextFace是不同类型,两套布局字典与 face 互不兼容,迁移时不能交叉使用。
文件读取:路径字符串不再可靠
ETE 3 允许直接传路径:
tree = Tree("tree.nw", format=1)ETE 4 的约定是:Newick 文本用字符串,文件输入传打开的文件对象:
from pathlib import Path with Path("tree.nw").open(encoding="utf-8") as handle: tree = Tree(handle, parser=1)ETE 4.4.0 内部仍保留路径字符串启发式,但文档明确说依赖它与文档化契约冲突、会让输入行为变得含糊,所以迁移时应全部改成打开文件对象的方式。
带属性的新节点构造同样变了:
# ETE 3 tree = Tree(name="root", dist=0, support=1) # ETE 4 tree = Tree({"name": "root", "dist": 0, "support": 1})属性模型:features 到 props
ETE 3 要求 name、distance、support 有默认值;ETE 4 中这些属性可以缺席,便捷访问器可能返回None。读写元数据的方法名全部换掉:
# ETE 3 node.add_feature("habitat", "marine") node.add_features(group="case", score=0.8) print(node.features) # ETE 4 node.add_prop("habitat", "marine") node.add_props(group="case", score=0.8) print(node.props)参数重命名规律:feature/features、attribute/attributes、property/properties统一映射到prop/props。原来用hasattr(node, "x")判断自定义元数据的写法,改为:
if "x" in node.props: value = node.props["x"]查找、谓词与迭代器重命名
查找和谓词的对照:
| ETE 3 | ETE 4 |
|---|---|
tree & "A" | tree["A"] |
tree.get_tree_root() | tree.root |
node.is_leaf() | node.is_leaf |
node.is_root() | node.is_root |
tree.get_common_ancestor(a, b) | tree.common_ancestor(a, b) |
node.get_ancestors() | node.ancestors() |
tree.get_leaves_by_name("A") | tree.search_leaves_by_name("A") |
注意is_leaf、is_root在 ETE 4 中是属性,不带括号调用。
迭代器重命名:
| ETE 3 | ETE 4 |
|---|---|
get_leaves()/iter_leaves() | leaves() |
get_descendants()/iter_descendants() | descendants() |
get_edges()/iter_edges() | edges() |
get_leaf_names() | leaf_names() |
get_ancestors() | ancestors() |
一个容易踩的坑:ETE 4 的这些方法返回的是迭代器。len(tree.leaves())或对结果下标取值都会失败,必须先转成列表:
leaves = list(tree.leaves()) names = list(tree.leaf_names())按名查找两种实现下都返回第一个匹配;当节点名是身份标识时,要自行校验唯一性。
Newick 读写:format= 到 parser=
# ETE 3 tree = Tree(newick, format=1) newick = tree.write(format=1) # ETE 4 tree = Tree(newick, parser=1) newick = tree.write(parser=1)解析器也支持"name"、"support"这类命名别名。
ASCII 输出改为to_str():
# ETE 3 print(tree.get_ascii(show_internal=True)) # ETE 4 print(tree.to_str(show_internal=True, props=["name", "dist"]))扩展属性语义是反的,务必单独检查。ETE 3 里features=[]表示导出所有可用属性;ETE 4 中:
tree.write(props=[]) # 不导出任何扩展属性 tree.write(props=["species", "host"]) # 只导出选定属性 tree.write(props=None) # 导出全部可用属性文档强调对外输出时应使用显式的选定属性列表。另外write()要使用关键字参数:ETE 4 中它的第一个位置参数是outfile,不再是 ETE 3 的属性选择。
自定义格式化器的写法也变了:
# ETE 3 newick = tree.write( format=1, dist_formatter="%0.1f", name_formatter="TEST-%s", ) # ETE 4 from ete4.parser import newick parser = newick.make_parser( 1, dist="%0.1f", name="TEST-%s", ) text = tree.write(parser=parser)距离、拓扑与 Robinson-Foulds
| ETE 3 | ETE 4 |
|---|---|
A.get_distance(B) | tree.get_distance(A, B) |
topology_only=True | topological=True |
convert_to_ultrametric() | to_ultrametric() |
resolve_polytomy(recursive=True) | resolve_polytomy(descendants=True) |
中点外群的旧两步写法(get_midpoint_outgroup()+set_outgroup())仍然有效,ETE 4 另加了直接写法tree.set_midpoint_outgroup()。距离矩阵方面,ETE 4.4.0 新增distance_matrix(),文档说明它取代cophenetic_matrix(),新代码用前者。
robinson_foulds()的返回值从 5 个变成 7 个,只解包 5 个值的旧代码必须更新:
( rf, max_rf, common, edges_self, edges_other, discarded_self, discarded_other, ) = tree.robinson_foulds(other)参数名也从 feature 系改成了prop_t1、prop_t2系。
随机树生成
# ETE 3 tree.populate( size, names_library=names, random_branches=True, dist_range=(0, 1), ) # ETE 4 import random tree.populate( size, names=names, model="yule", dist_fn=random.random, support_fn=lambda: 1, )如果生成的拓扑或距离需要可复现,要自己设置随机种子。
PhyloTree 基因树分析的陷阱
PhyloTree的核心方法保留了,但要用 ETE 4 的属性和迭代器语法:
from ete4 import PhyloTree tree = PhyloTree( "((Hsa|g1,Ptr|g1),Mmu|g1);", sp_naming_function=lambda name: name.split("|", 1)[0], ) events = tree.get_descendant_evol_events(sos_thr=0.0) for leaf in tree.leaves(): print(leaf.name, leaf.species)三个具体要点:
- 物种感知方法要显式传入
sp_naming_function。文档指出当前源码的默认值是None,尽管旧文档描述过"自动取前三位"的规则,不能依赖它。 - 物种重叠事件检测要求 rooted 且完全二歧化的基因树。
- 不要把物种树传给
get_descendant_evol_events()——ETE 4.4.0 中它的签名只接受sos_thr。做比对时用:
reconciled_tree, events = gene_tree.reconcile(species_tree)事件检测后,读取node.props.get("evoltype")来检查节点事件类型,不要再依赖 ETE 3 的 feature 辅助方法。
分类数据库:存储位置与 GTDB
ETE 3 时代的示例通常指~/.etetoolkit/taxa.sqlite;ETE 4 把分类数据存到:
~/.local/share/ete/文档把当前文档中约 600 MB(NCBI)与 72 MB(GTDB)的数字定性为本地首次使用的占用估算,而不是压缩后的网络下载体积——归档大小随版本变化,解析后的 SQLite 和临时转换文件还需要额外空间,磁盘预算要留足。
ETE 4 新增了一等公民式的 GTDB 支持:
from ete4 import GTDBTaxaNCBI 数字 TaxID 与 GTDB 字符串标识符不可互换,混用会出错。
可视化迁移
SmartView 是 ETE 4 的首选路径:
from ete4 import Tree tree = Tree("((A,B),C);") tree.explore() tree.render_sm("tree.png")自定义 SmartView 布局:
from ete4.smartview import Layout, PropFace def draw_node(node): if node.is_leaf: return PropFace("name", position="right") layout = Layout("labels", draw_node=draw_node) tree.explore(layouts=[layout])保留 Qt treeview 的旧代码只需要改导入位置(from ete4.treeview import NodeStyle, TreeStyle),并按前面说明安装ete4[treeview]extra。文档给出的分工是:需要矢量 PDF/SVG 时 Qt treeview 仍是选项;SmartView 的render_sm()在 ETE 4.4.0 中产出的是 PNG 截图数据。
ClusterTree 在 ETE 4 中不存在
这是迁移中的"不支持项",旧代码如果依赖它必须移除或重做:
# ETE 3 from ete3 import ClusterTree# ETE 4.4.0 下的实际报错 ImportError: cannot import name 'ClusterTree' from 'ete4'文档明确不要把ClusterTree、linked matrix profiles、silhouette、Dunn 方法描述为 ETE 4 的能力:这些计算应改用受维护的聚类库,树拓扑展示用普通 ETETree即可。
ete4 compare命令行的已知问题
文档记录:ETE 4.4.0 自带的ete4 compare命令内部仍调用Tree(..., format=...),会因该关键字被移除而失败。替代方案是用Tree.robinson_foulds()、唯一标签场景下的Tree.compare(),或者本技能自带的scripts/tree_operations.py compare辅助命令。同时避免走Tree.compare(has_duplications=True)路径——上游源码把该分支标记为可能损坏。
一个完整的移植对照
文档给出的端到端示例,左边是典型 ETE 3 写法,右边是等价 ETE 4 代码:
# ETE 3 from ete3 import Tree tree = Tree("tree.nw", format=1) node = tree & "A" node.add_feature("group", "case") for leaf in tree.iter_leaves(): if leaf.is_leaf(): print(leaf.name) tree.write( outfile="out.nhx", format=1, features=["group"], )# ETE 4 from pathlib import Path from ete4 import Tree with Path("tree.nw").open(encoding="utf-8") as handle: tree = Tree(handle, parser=1) node = tree["A"] node.add_prop("group", "case") for leaf in tree.leaves(): if leaf.is_leaf: print(leaf.name) tree.write( outfile="out.nhx", parser=1, props=["group"], )这段示例覆盖了迁移中最高频的组合:文件对象读入、parser=、下标查找、add_prop、迭代器、属性导出列表。
验证迁移结果
文档给出了一段验证代码,用于确认环境版本和新语法都生效:
import ete4 from ete4 import Tree assert ete4.__version__ == "4.4.0" tree = Tree("((A:1,B:1)95:0.2,C:1);", parser="support") assert list(tree.leaf_names()) == ["A", "B", "C"] assert tree["A"].is_leaf round_trip = tree.write(parser="support", props=[]) assert round_trip == "((A:1,B:1)95:0.2,C:1);"其中字符串((A:1,B:1)95:0.2,C:1);同时作为输入和期望回写值,是文档示例里带支持值、分支长度的完整 Newick,parser="support"保证支持值95按支持值而非节点名解析;这段代码本身即是对解析器选择、props=[](不导出扩展属性)语义和迭代器用法的联合验证。
命令行侧可以用本技能目录skills/etetoolkit/下的脚本做二次核对。在该目录下执行(tree.nw换成你的实际树文件):
uv run --with "ete4==4.4.0" python scripts/tree_operations.py \ stats tree.nw --parser 1 uv run --with "ete4==4.4.0" python scripts/tree_operations.py \ ascii tree.nw --parser 1 --props name,dist uv run --with "ete4==4.4.0" python scripts/tree_operations.py \ compare tree_a.nw tree_b.nwcompare子命令内部走的是 ETE 4 的robinson_foulds()七元组解包,并会对无名称或重复的叶节点名直接报错拒绝,而不是静默产出部分结果,适合用来验证旧的两棵树比较流程改写后行为一致。
最后按文档的收尾要求执行:用带节点名、支持值、分支长度、NHX 属性、重复叶节点和多歧化的代表性树做测试,并且比较的是科学输出,而不只是代码能否跑通。
限制与后续
- 迁移基线是 ETE 4.4.0;文档中
sp_naming_function默认值、get_descendant_evol_events()签名等结论均针对 4.4.0,升级其他版本前需重新核对。 ClusterTree相关能力(聚类轮廓系数、Dunn 方法等)没有 ETE 4 等价物,必须换库实现。ete4 compare与Tree.compare(has_duplications=True)在 4.4.0 下不可用,树比较走robinson_foulds()或 tree_operations.py。- 分类数据库首次更新会占用可观磁盘空间,详见 taxonomy.md;解析器选择与核心 API 的完整对照见 api_reference.md。
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考