This crate aims to be a general purpose package to deal with phylogenetic trees and simulate them. Is also comes with a simple CLI tool to manipulate and simulate phylogenetic trees directly from the command line.
phylotree
To use this crate just run cargo add phylotree
in your cargo project or add phylotree = "0.1.2"
to your Cargo.toml
file.
To install the CLI you can use
cargo install phylotree
Or you can build the project from source:
shell
git clone https://github.com/lucblassel/phylotree.git
cd phylotree
cargo build --release
mv target/release/phylotree <somewhere/in/your/PATH>
Below is some sample usage of the phylotree
crate, please see docs.rs/phylotree for the full documentation.
```rust use phylotree::tree::Tree;
////////////////////////////////// // Building a tree from scratch // ////////////////////////////////// let mut tree = Tree::new();
// Add the root node let root = tree.add(Node::new());
// Add a child to the root let child1 = tree.addchild(Node::newnamed("Child1"), root, None).unwrap(); // Add a child to the root with a branch length let child2 = tree.addchild(Node::newnamed("Child2"), root, Some(0.5)).unwrap();
// Add more children let child3 = tree.addchild(Node::newnamed("Child_3"), child1, None).unwrap();
// Get depth of child asserteq!(tree.get(&child3).unwrap().getdepth(), 2)
/////////////////////////////// // Reading and writing trees // /////////////////////////////// let newickstr = "((A:0.1,B:0.2)F:0.6,(C:0.3,D:0.4)E:0.5)G;"; let tree = Tree::fromnewick(newick_str).unwrap();
asserteq!(tree.tonewick().unwrap(), newick_string)
////////////////////// // Traversing trees // ////////////////////// let newickstr = "((A,B)C,(D,E)F)G;"; let mut tree = Tree::fromnewick(newickstr).unwrap(); let root = tree.getroot().unwrap();
let preorder: Vec<_> = tree.preorder(&root).unwrap() .iter() .map(|nodeid| tree.get(nodeid).unwrap().name.clone().unwrap()) .collect();
assert_eq!(preorder, vec!["G", "C", "A", "B", "F", "D", "E"]);
///////////////////// // Comparing trees // /////////////////////
// The second tree is just a random rotation of the first, // they represent the same phylogeney let newickorig = "((A:0.1,B:0.2)F:0.6,(C:0.3,D:0.4)E:0.5)G;"; let newickrota = "((D:0.3,C:0.4)E:0.5,(B:0.2,A:0.1)F:0.6)G;";
let treeorig = Tree::fromnewick(newickorig).unwrap(); let treerota = Tree::fromnewick(newickrota).unwrap();
let rf = treeorig.robinsonfoulds(&tree_rota).unwrap();
assert_eq!(rf, 0)
///////////////////////////////// // Computing a distance matrix // ///////////////////////////////// let newick = "((T3:0.2,T1:0.2):0.3,(T2:0.4,T0:0.5):0.6);"; let tree = Tree::fromnewick(newick).unwrap(); // Compute the whole distance matrix let matrix = tree.distancematrix_recursive().unwrap(); let phylip="\ 4 T0 0 1.6 0.9 1.6 T1 1.6 0 1.5 0.4 T2 0.9 1.5 0 1.5 T3 1.6 0.4 1.5 0 ";
asserteq!(matrix.tophylip(true).unwrap(), phylip) ```
There is a simple CLI that comes with this package:
``` A simple command line tool to manipulate phylogenetic trees
Usage: phylotree
Commands: generate Generate random tree(s) stats Get statistics about a tree compare Compare two phylogenetic trees matrix Output the phylogenetic distance matrix of the tree distance Outputs a subset of phylogenetic distances collapse Collapse branches that are under a certain branch length threshold remove Remove tips from the trees deduplicate Remove or collapse branches corresponding to identical sequences in a reference alignment help Print this message or the help of the given subcommand(s)
Options: -h, --help Print help ```
A python package has also been implemented, using PyO3 to create the python bindings. It is available on PyPi, you can see the API of the package here.
Example usage:
```python from phylotree import Tree
tree = Tree.from_newick("path/to/tree.nwk")
tree = Tree.from_string("((A,(C,E)D)B,((H)I)G)F;")
tree.to_newick()
tree.nnodes() tree.ntips() tree.height() tree.diameter() tree.ncherries() tree.colless() tree.sackin() tree.isbinary tree.is_rooted
names = tree.getleafnames() info = tree.getnodeattributes(name=names[0]) print(f"Parent edge has length: {info['parentedge']}") distbranches, disttopo = tree.getdistance(names=(names[0], names[1]))
distancematrix = tree.tomatrix() distmat = distancematrix[(names[0], names[1])] assert distbranches == distmat
tree.compress() # Remove nodes with 1 parent and 1 child tree.rescale() # Change branch lengths tree.prune(name="D") # Remove sbutree rooted a specific node
for nodeid in tree.traversal(order="levelorder"): node = tree.getnodeattributes(id=nodeid) print(node.name) ```