Package: newick
python-newickpython package to read and write theNewick format.Reading NewickSince Newick specifies a format for aset of trees, all functions to read Newick return
alistofnewick.Nodeobjects.Reading from a string:>>>fromnewickimportloads>>>trees=loads('(A,B,(C,D)E)F;')>>>trees[0].name'F'>>>[n.namefornintrees[0].descendants]['A','B','E']Reading from  afile-like object:>>>importio>>>fromnewickimportload>>>withio.open('fname',encoding='utf8')asfp:...trees=load(fp)Reading from a path:>>>fromnewickimportread>>>trees=read('fname')>>>importpathlib>>>trees=read(pathlib.Path('fname'))Supported Newick dialectsWhile the set of reserved characters in Newick (;(),:) is relatively small, it's still often
seen as too restrictive, in particular when it comes to adding more data to tree nodes. Thus, Newick
provides two mechanisms to overcome this restriction:quoted labelsto allow arbitrary text as node names,commentsenclosed in square brackets.Quoted node labelsNode labels in Newick may be quoted (i.e. enclosed in single quotes') to make it possible to
add characters which are otherwise reserved. Thenewickpackage supports quoted labels.>>>fromnewickimportloads>>>print(loads("('A:B','C''D')'E(F)'")[0].ascii_art())┌─'A:B'──'E(F)'─┤└─'C''D'When creating Newick trees programmatically, names can be quoted (if necessary) automatically:>>>fromnewickimportNode>>>print(Node("A(F')",auto_quote=True).name)'A(F'')'>>>print(Node("A(F')",auto_quote=True).unquoted_name)A(F')Note:newickprovides no support to parse structured data from node labels (as it can be found
in the trees distributed by the Genome Taxonomy Database).Additional information in commentsThe"Newick specification"statesComments are enclosed in square brackets and may appear anywhereThis has spawned a host of ad-hoc mechanisms to insert additional data into Newick trees.Thenewickpackage allows to deal with comments in two ways.Ignoring comments:>>>newick.loads('[a comment](a,b)c;',strip_comments=True)[0].newick'(a,b)c'Reading comments as node annotations: Several software packages use Newick comments to
store node annotations, e.g. *BEAST, MrBayes or TreeAnnotator. Provided there are no
comments in places where they cannot be interpreted as node annotations,newicksupports
reading and writing these annotations:>>>newick.loads('(a[annotation],b)c;')[0].descendants[0].name'a'>>>newick.loads('(a[annotation],b)c;')[0].descendants[0].comment'annotation'>>>newick.loads('(a[annotation],b)c;')[0].newick'(a[annotation],b)c'Annotations may come before and/or after the:which separates node label and length:>>>newick.loads('(a[annotation]:2,b)c;')[0].descendants[0].length2.0>>>newick.loads('(a:[annotation]2,b)c;')[0].descendants[0].length2.0>>>newick.loads('(a[annotation1]:[annotation2]2,b)c;')[0].descendants[0].comments['annotation1','annotation2']Note that square brackets insidequoted labelswillnotbe interpreted as comments
or annotations:>>>newick.loads("('a[label]',b)c;")[0].descendants[0].name"'a[label]'">>>newick.loads("('a[label]',b)c;")[0].newick"('a[label]',b)c"Some support for reading key-value data from node comments is available as well. If the comment
format follows theNHXspec
or the&<key>=<value>,...-format used e.g. by the MrBayes or BEAST software, additional data
can be accessed from thedictNode.properties:>>>newick.loads('(A,B)C[&&NHX:k1=v1:k2=v2];')[0].properties{'k1':'v1','k2':'v2'}Limitations:Typednode properties are not supported. I.e. values inNode.propertiesare
always strings. Since typed properties tend to be specific to the application writing the newick,
this level of support would require more knowledge of the creation context of the tree than can
safely be inferred from the Newick string alone.>>>newick.loads('(A,B)C[&range={1,5},support="100"];')[0].properties{'range':'{1,5}','support':'"100"'}Node annotations in comments are not completely round-trip-safe. In particular multiple comments
per node may be lumped together (using|as separator) when serializing a Newick node:>>>newick.loads('(a,b)c[c1][c2]:3')[0].newick'(a,b)c[c1|c2]:3'Writing NewickIn parallel to the read operations there are three functions to serialize a singleNodeobject or alistofNodeobjects to Newick format:dumps(trees) -> strdump(trees, fp)write(trees, 'fname')A tree may be assembled using the factory methods of theNodeclass:Node.__init__Node.createNode.add_descendantManipulating treesDisplaying tree topology in the terminal:>>>importnewick>>>tree=newick.loads('(b,(c,(d,(e,(f,g))h)i)a)')[0]>>>print(tree.ascii_art())┌─b────┤│┌─c└─a─┤│┌─d└─i─┤│┌─e└─h─┤│┌─f└───┤└─gPruning trees: The example below prunes the tree such thatb,candiare the only
remaining leafs.>>>tree.prune_by_names(['b','c','i'],inverse=True)>>>print(tree.ascii_art())┌─b────┤│┌─c└─a─┤└─iRunning a callable on a filtered set of nodes:>>>tree.visit(lambdan:setattr(n,'name',n.name.upper()),lambdan:n.namein['a','b'])>>>print(tree.ascii_art())┌─B────┤│┌─c└─A─┤└─iRemoving (topologically) redundant internal nodes:>>>tree.prune_by_names(['B','c'],inverse=True)>>>print(tree.ascii_art())┌─B────┤└─A──c>>>tree.remove_redundant_nodes(keep_leaf_name=True)>>>print(tree.ascii_art())┌─B────┤└─c