Package: treet
Generic tree utilities for PythonTrees are one of the most ubiquitous data structures. It is amazing how often we
as programmers tend to reimplement the same algorithms for different tree
formats and stuctures.This module defines generic tree-traverse and tree-reduce algorithms that can be
used with any tree-like object such as filesystem paths, lists, nested
dictionaries an expression tree or even specialized tree classes! The only thing
that must be provided is a function to get child nodes from a parent node.Also, trees are usually represented in some fields (such as bioinformatics) in
the newick format, which is nontrivial to parse, so this module includes a
function to do this.Usage and examplesInstall fromPyPi:pip install treetImport the basic functions,traverse,reduceandparse_newick:importtreetUse with any kind of structured tree!Any kind of structured data is supported, in this case, nested dictionaries:tree={'label':'A','children':[{'label':'B','children':[]},{'label':'C','children':[{'label':'D','children':[]},{'label':'E','children':[]}]}]}defchildren(node):returnnode['children'][node['label']fornodeintreet.traverse(tree,children,mode='inorder')]# Output --> ['B, 'A', 'D', 'C', 'E']defas_list(node,children):ifnotchildren:returnnode['label']else:returnchildrentreet.reduce(tree,children,reduce_fn=as_list)# Output --> ['B, ['D', 'E']]Even with user-defined classes!Dump a tree in a specialized class format to a string in the newick format.classTree:def__init__(self,label,children=None):self.label=labelself.children=childrenifchildrenelse[]defis_leaf(self):returnlen(self.children)==0tree=Tree('A',[Tree('B'),Tree('C',[Tree('D'),Tree('E')])])defget_children(node):returnnode.childrendefnode_to_newick(node,children):ifnode.is_leaf():returnnode.labelelse:returnf"({','.join(children)})"treet.reduce(tree,get_children,node_to_newick)# Output --> '(B,(D,E))'Parse a newick-formatted tree structureAssemble the Newick string to a custom data format:defparse_node_data(data_string):'''Example:'data1=xx,data2=yy'-> {'data1':'xx', 'data2': 'yy'}'''items=data_string.split(',')key_value_pairs=(item.split('=')foriteminitems)returndict(key_value_pairs)defparse_branch_length(length_str):returnfloat(length_str)iflength_strelse0.0deftree_builder(label,children,branch_length,node_data):return{'label':label,'length':branch_length,'data':node_data,'children':children}newick="(A:0.2[dat=23,other=45], B:12.4[dat=122,other=xyz])root[x=y];"treet.parse_newick(newick,aggregator=tree_builder,feature_parser=parse_node_data,distance_parser=parse_branch_length)# Output ->{'label':'root','length':0.0,'data':{'x':'y'},'children':[{'label':'A','length':0.2,'data':{'dat':'23','other':'45'},'children':[]},{'label':'B','length':12.4,'data':{'dat':'122','other':'xyz'},'children':[]},]}Compose to perform complex algorithmsGet the subtree induced by a subset of the leaves:tree=(('A',('B',('C','D'))),'E')defis_leaf(node):returnisinstance(node,str)defget_children(node):returnnodeifnotis_leaf(node)else[]definduced_subtree(leafs):definduced_subtree_generator(node,children):ifchildren:returntuple(chforchinchildrenifnotchisNone)else:returnnodeifnodeinleafselseNonereturninduced_subtree_generatorleafs=['B','D','E']induced=treet.reduce(tree,get_children,induced_subtree(leafs))print(induced)# Output --> ((('B',('D',)),),'E')defmerge_unary_nodes(node,children):ifis_leaf(node):returnnodenew_children=[ch[0]if(len(ch)==1)elsechforchinchildren]returntuple(new_children)treet.reduce(induced,get_children,merge_unary_nodes)# Output --> (('B','D'),'E')Use even with filesystem paths!Traverse the/usrdirectory in breadth-first order:frompathlibimportPathdefenter_folder(path):path=Path(path)returnlist(path.iterdir())ifpath.is_dir()else[]foritemintreet.traverse('/usr',enter_folder,mode='breadth_first'):print(item)# Output --># /# /proc# /usr# ...# /usr/share# /usr/bin# /usr/sbin# ...# /usr/bin/jinfo# /usr/bin/m2400w# ...MetaAuthor:Ad115-Github–a.garcia230395@gmail.comDistributed under the MIT license. SeeLICENSEmore information.ContributingTo run tests:pytest treet/* --hypothesis-show-statistics --verboseTo run static type check:mypy treet/*.pyTo run coverage analysis:coverage run --source=. -m pytest treet/* --hypothesis-show-statistics --verboseCheck for open issues or open a fresh issue to start a discussion around a feature idea or a bug.Forkthe repositoryon GitHub to start making your changes to a feature branch, derived from themasterbranch.Write a test which shows that the bug was fixed or that the feature works as expected.Send a pull request and bug the maintainer until it gets merged and published.