skills/etetoolkit/references/taxonomy.md
ETE 4.4.0 provides local SQLite-backed interfaces for:
NCBITaxaGTDBTaxaBoth can translate identifiers, retrieve ranks and lineages, find descendants,
construct minimal connecting topologies, and annotate PhyloTree objects.
The official tutorial's approximate 600 MB NCBI and 72 MB GTDB figures should be treated as local first-use footprint estimates, not compressed network download sizes. Archives vary by release and can be much smaller.
Parsed databases are stored under ~/.local/share/ete/ by default. Allow space
for the downloaded archive, parsed SQLite database, traversal cache, and
temporary conversion files. Do not create or refresh a database unexpectedly
in a constrained or offline job.
No API key or credential is required.
from ete4 import GTDBTaxa, NCBITaxa
ncbi = NCBITaxa(
dbfile=None,
taxdump_file=None,
memory=False,
update=True,
)
gtdb = GTDBTaxa(
dbfile=None,
taxdump_file=None,
memory=False,
)
Important controls:
dbfile: explicit parsed SQLite pathtaxdump_file: local taxonomy archive used to create/update a databasememory=True: load the database into memory for repeated queriesupdate=False on NCBITaxa: disable the constructor's schema-update pathWhen the database is absent, construction creates/downloads it. An existing
database is not refreshed to newer taxonomy content merely because
update=True; call update_taxonomy_database() explicitly when a content
refresh is intended.
For a reproducible or offline analysis, provide an explicit dbfile and use
the same file across runs.
Latest NCBI taxonomy:
from ete4 import NCBITaxa
ncbi = NCBITaxa(update=False)
ncbi.update_taxonomy_database()
Latest GTDB taxonomy:
from ete4 import GTDBTaxa
gtdb = GTDBTaxa()
gtdb.update_taxonomy_database()
From an already acquired local archive:
ncbi.update_taxonomy_database("taxdump.tar.gz")
gtdb.update_taxonomy_database("gtdb_taxdump.tar.gz")
For production provenance, record:
Do not replace a shared database in the middle of a multi-step analysis.
from ete4 import NCBITaxa
ncbi = NCBITaxa()
queries = ["Homo sapiens", "Pan troglodytes", "Mus musculus"]
name_to_taxids = ncbi.get_name_translator(queries)
for query in queries:
candidates = name_to_taxids.get(query, [])
if not candidates:
print("unresolved:", query)
elif len(candidates) > 1:
print("ambiguous:", query, candidates)
else:
print(query, candidates[0])
The translator returns a list because a name can map to multiple taxonomy records. Do not blindly select index zero without checking ambiguity.
taxid_to_name = ncbi.get_taxid_translator([9606, 9598, 10090])
print(taxid_to_name)
taxid = 9606
lineage = ncbi.get_lineage(taxid)
names = ncbi.get_taxid_translator(lineage)
ranks = ncbi.get_rank(lineage)
for ancestor in lineage:
print(ancestor, names.get(ancestor), ranks.get(ancestor, "no rank"))
Use .get() because not every taxonomy node is guaranteed to have every
requested annotation.
descendants = ncbi.get_descendant_taxa("Homo")
print(ncbi.translate_to_names(descendants))
Collapse below the species level:
species = ncbi.get_descendant_taxa(
"Homo",
collapse_subspecies=True,
)
Return an ETE tree:
tree = ncbi.get_descendant_taxa(
"Homo",
collapse_subspecies=True,
return_tree=True,
)
print(tree.to_str(props=["sci_name", "taxid", "rank"]))
Large internal taxa can have many descendants. Estimate scope before materializing or printing the complete result.
taxids = [9606, 9598, 10090, 7707, 8782]
tree = ncbi.get_topology(
taxids,
intermediate_nodes=False,
collapse_subspecies=False,
annotate=True,
)
print(tree.to_str(props=["sci_name", "rank", "taxid"]))
Retain every intermediate taxonomy node:
tree = ncbi.get_topology(
[2, 33208],
intermediate_nodes=True,
annotate=True,
)
Taxonomy topology is a classification hierarchy. Do not treat branch lengths or omitted intermediate ranks as a molecular phylogeny.
GTDB identifiers are strings such as:
d__Bacteriap__Firmicutes_Bf__KorarchaeaceaeGB_GCA_020833055.1RS_GCF_000019605.1Do not pass them to NCBITaxa, and do not pass NCBI numeric TaxIDs to
GTDBTaxa.
from ete4 import GTDBTaxa
gtdb = GTDBTaxa()
descendants = gtdb.get_descendant_taxa("f__Thorarchaeaceae")
print(descendants)
queries = [
"p__Huberarchaeota",
"o__Peptococcales",
"f__Korarchaeaceae",
]
tree = gtdb.get_topology(
queries,
intermediate_nodes=True,
collapse_subspecies=True,
annotate=True,
)
print(tree.to_str(props=["sci_name", "rank"]))
GTDB and NCBI classifications can disagree because they use different data, release cycles, nomenclature, and taxonomic frameworks. State which one was used rather than combining labels without a mapping policy.
from ete4 import PhyloTree
tree = PhyloTree("((9606,9598),10090);")
taxid_to_name, taxid_to_lineage, taxid_to_rank = tree.annotate_ncbi_taxa(
taxid_attr="name",
)
print(tree.to_str(props=["name", "sci_name", "taxid", "rank"]))
tree = PhyloTree(
"((9606|protA,9598|protA),10090|protB);",
sp_naming_function=lambda name: name.split("|", 1)[0],
)
tree.annotate_ncbi_taxa(taxid_attr="species")
tree = PhyloTree("((protA,protB),protC);")
taxids = {
"protA": 9606,
"protB": 9598,
"protC": 10090,
}
for leaf in tree.leaves():
leaf.add_prop("ncbi_taxid", taxids[leaf.name])
tree.annotate_ncbi_taxa(taxid_attr="ncbi_taxid")
Prefer an explicit mapping when names are not stable taxonomy identifiers.
from ete4 import PhyloTree
tree = PhyloTree(
"((GB_GCA_020833055.1|protA,GB_GCA_003344655.1|protB),"
"RS_GCF_000019605.1|protC);",
sp_naming_function=lambda name: name.split("|", 1)[0],
)
tree.annotate_gtdb_taxa(taxid_attr="species")
print(tree.to_str(props=["name", "sci_name", "rank"]))
The annotation methods infer internal-node taxonomy from descendants when possible and return the translators they used. Preserve those mappings when the analysis needs an auditable record.
Prepare the database in a controlled networked step:
from ete4 import NCBITaxa
db_path = "taxonomy/ncbi_taxa.sqlite"
ncbi = NCBITaxa(dbfile=db_path, update=False)
ncbi.update_taxonomy_database("taxonomy/taxdump.tar.gz")
Use the pinned database without constructor schema updates in analysis jobs:
ncbi = NCBITaxa(
dbfile="taxonomy/ncbi_taxa.sqlite",
update=False,
)
For a read-only container or cluster job, mount the database at an explicit path. Avoid relying on an unwritable home-directory default.
Before using taxonomy annotations: