| name | biopython-phylo |
| description | Use Bio.Phylo to read/write phylogenetic trees and perform visualization and statistics; use when tree parsing/conversion, pruning/rerooting, distance calculation, or plotting is required. |
| license | MIT |
| author | AIPOCH |
Source: https://github.com/aipoch/medical-research-skills
biopython-phylo
When to Use
- Converting phylogenetic tree files between Newick, NEXUS, and phyloXML formats.
- Traversing a tree to locate clades, prune taxa, or reroot at a specific node/outgroup.
- Computing pairwise distances, distance matrices, or basic tree statistics (e.g., branch length summaries).
- Producing quick tree visualizations as ASCII output for logs/CLI workflows.
- Generating publication-ready plots of trees using Matplotlib.
Key Features
- Read and write phylogenetic trees via
Bio.Phylo with support for common formats (Newick/NEXUS/phyloXML).
- Tree manipulation utilities: traversal, clade selection, pruning, and rerooting.
- Distance computation and simple statistics derived from branch lengths/topology.
- Visualization options:
- ASCII rendering for terminal output.
- Matplotlib-based plotting for figures.
Dependencies
biopython>=1.80
- Optional (for plotting):
Example Usage
The following example is runnable end-to-end and follows the conventions:
- Configuration is stored in
config/task_config.json.
- Script is invoked as
python scripts/phylo_task.py.
- All file I/O uses
encoding="utf-8".
- JSON output uses
ensure_ascii=False.
config/task_config.json
{
"input_tree": "data/input_tree.nwk",
"input_format": "newick",
"output_tree": "artifacts/output_tree.xml",
"output_format": "phyloxml",
"prune_terminals": ["TaxonC"],
"reroot_outgroup": "TaxonB",
"ascii_out": "artifacts/tree_ascii.txt",
"stats_out": "artifacts/tree_stats.json",
"plot_enabled": true,
"plot_out": "artifacts/tree_plot.png"
}
scripts/phylo_task.py
import json
import os
from typing import Any, Dict, List, Optional
from Bio import Phylo
def ensure_parent_dir(path: str) -> None:
parent = os.path.dirname(path)
if parent:
os.makedirs(parent, exist_ok=True)
def load_config(path: str) -> Dict[str, Any]:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def prune_by_names(tree, names: List[str]) -> None:
for n in names:
if tree.find_any(name=n) is not None:
tree.prune(target=n)
def reroot_by_outgroup_name(tree, outgroup_name: str) -> None:
outgroup = tree.find_any(name=outgroup_name)
if outgroup is None:
raise ValueError()
tree.root_with_outgroup(outgroup)
() -> [, ]:
terminals = tree.get_terminals()
nonterminals = tree.get_nonterminals()
lengths = []
clade tree.find_clades(order=):
clade.branch_length :
lengths.append((clade.branch_length))
{
: (terminals),
: (nonterminals),
: (terminals) + (nonterminals),
: (lengths),
: (lengths) lengths ,
: (lengths) lengths ,
: (lengths) lengths ,
: ((lengths) / (lengths)) lengths ,
}
() -> :
ensure_parent_dir(out_path)
(out_path, , encoding=) f:
Phylo.draw_ascii(tree, file=f)
() -> :
matplotlib
matplotlib.use()
matplotlib.pyplot plt
ensure_parent_dir(out_path)
fig = plt.figure(figsize=(, ))
ax = fig.add_subplot(, , )
Phylo.draw(tree, do_show=, axes=ax)
fig.tight_layout()
fig.savefig(out_path, dpi=)
plt.close(fig)
() -> :
cfg = load_config(config_path)
input_tree = cfg[]
input_format = cfg.get(, )
output_tree = cfg[]
output_format = cfg.get(, )
prune_terminals: [] = cfg.get(, [])
reroot_outgroup: [] = cfg.get()
ascii_out = cfg.get(, )
stats_out = cfg.get(, )
plot_enabled = (cfg.get(, ))
plot_out = cfg.get(, )
tree = Phylo.read(input_tree, input_format)
prune_terminals:
prune_by_names(tree, prune_terminals)
reroot_outgroup:
reroot_by_outgroup_name(tree, reroot_outgroup)
ensure_parent_dir(output_tree)
Phylo.write(tree, output_tree, output_format)
write_ascii(tree, ascii_out)
ensure_parent_dir(stats_out)
(stats_out, , encoding=) f:
json.dump(tree_stats(tree), f, ensure_ascii=, indent=)
plot_enabled:
plot_tree(tree, plot_out)
__name__ == :
main()
Run
python scripts/phylo_task.py
Implementation Details
- Configuration-first execution: parameters are stored in
config/task_config.json as an intermediate artifact; scripts are invoked uniformly via python scripts/<task_name>.py. Avoid stacking many CLI -- arguments; prefer config files.
- Encoding and JSON output:
- Always open files with
encoding="utf-8".
- When writing JSON, use
ensure_ascii=False to preserve non-ASCII characters.
- Supported formats:
- Input/output formats are passed to
Phylo.read(...) and Phylo.write(...) (e.g., newick, nexus, phyloxml).
- Pruning:
- Pruning is performed by terminal/clade name using
tree.prune(target=<name>). Names not found are skipped (or can be treated as errors depending on your policy).
- Rerooting:
- Rerooting uses
tree.root_with_outgroup(outgroup_clade); the outgroup is located via tree.find_any(name=...).
- Statistics:
- Branch lengths may be missing (
None); statistics should ignore missing values.
- Basic counts can be derived from
tree.get_terminals() and tree.get_nonterminals().
- Visualization:
- ASCII output uses
Phylo.draw_ascii(tree, file=...) for deterministic CLI-friendly rendering.
- Matplotlib plotting uses a non-interactive backend (
Agg) for headless environments and saves to an image file.