1
2
3
4
5
6
7
8 """I/O function wrappers for the Newick file format.
9
10 See: http://evolution.genetics.washington.edu/phylip/newick_doc.html
11 """
12 __docformat__ = "restructuredtext en"
13
14 from cStringIO import StringIO
15
16 from Bio.Phylo import Newick
17
18
19 NODECOMMENT_START = '[&'
20 NODECOMMENT_END = ']'
24 """Exception raised when Newick object construction cannot continue."""
25 pass
26
27
28
29
30
31 -def parse(handle, **kwargs):
32 """Iterate over the trees in a Newick file handle.
33
34 :returns: generator of Bio.Phylo.Newick.Tree objects.
35 """
36 return Parser(handle).parse(**kwargs)
37
38
39 -def write(trees, handle, plain=False, **kwargs):
40 """Write a trees in Newick format to the given file handle.
41
42 :returns: number of trees written.
43 """
44 return Writer(trees).write(handle, plain=plain, **kwargs)
45
46
47
48
49
50 -class Parser(object):
51 """Parse a Newick tree given a file handle.
52
53 Based on the parser in `Bio.Nexus.Trees`.
54 """
55
58
59 @classmethod
63
64 - def parse(self, values_are_confidence=False, rooted=False):
65 """Parse the text stream this object was initialized with."""
66 self.values_are_confidence = values_are_confidence
67 self.rooted = rooted
68 buf = ''
69 for line in self.handle:
70 buf += line.rstrip()
71 if buf.endswith(';'):
72 yield self._parse_tree(buf, rooted)
73 buf = ''
74 if buf:
75
76 yield self._parse_tree(buf, rooted)
77
82
84 """Parse ``(a,b,c...)[[[xx]:]yy]`` into subcomponents, recursively."""
85 text = text.strip().rstrip(';')
86 if text.count('(')!=text.count(')'):
87 raise NewickError("Parentheses do not match in (sub)tree: " + text)
88
89 if text.count('(') == 0:
90
91 return self._parse_tag(text)
92
93
94 close_posn = text.rfind(')')
95 subtrees = []
96
97 plevel = 0
98 prev = 1
99 for posn in range(1, close_posn):
100 if text[posn] == '(':
101 plevel += 1
102 elif text[posn] == ')':
103 plevel -= 1
104 elif text[posn] == ',' and plevel == 0:
105 subtrees.append(text[prev:posn])
106 prev = posn + 1
107 subtrees.append(text[prev:close_posn])
108
109 clade = self._parse_tag(text[close_posn+1:])
110 clade.clades = [self._parse_subtree(st) for st in subtrees]
111 return clade
112
153
154
155
156
157
158 -class Writer(object):
159 """Based on the writer in Bio.Nexus.Trees (str, to_string)."""
160
163
164 - def write(self, handle, **kwargs):
165 """Write this instance's trees to a file handle."""
166 count = 0
167 for treestr in self.to_strings(**kwargs):
168 handle.write(treestr + '\n')
169 count += 1
170 return count
171
172 - def to_strings(self, confidence_as_branch_length=False,
173 branch_length_only=False, plain=False,
174 plain_newick=True, ladderize=None, max_confidence=1.0,
175 format_confidence='%1.2f', format_branch_length='%1.5f'):
176 """Return an iterable of PAUP-compatible tree lines."""
177
178 if confidence_as_branch_length or branch_length_only:
179 plain = False
180 make_info_string = self._info_factory(plain,
181 confidence_as_branch_length, branch_length_only, max_confidence,
182 format_confidence, format_branch_length)
183
184 def newickize(clade):
185 """Convert a node tree to a Newick tree string, recursively."""
186 if clade.is_terminal():
187 return ((clade.name or '')
188 + make_info_string(clade, terminal=True))
189 else:
190 subtrees = (newickize(sub) for sub in clade)
191 return '(%s)%s' % (','.join(subtrees),
192 (clade.name or '') + make_info_string(clade))
193
194
195 for tree in self.trees:
196 if ladderize in ('left', 'LEFT', 'right', 'RIGHT'):
197
198 tree.ladderize(reverse=(ladderize in ('right', 'RIGHT')))
199 rawtree = newickize(tree.root) + ';'
200 if plain_newick:
201 yield rawtree
202 continue
203
204 treeline = ['tree', (tree.name or 'a_tree'), '=']
205 if tree.weight != 1:
206 treeline.append('[&W%s]' % round(float(tree.weight), 3))
207 if tree.rooted:
208 treeline.append('[&R]')
209 treeline.append(rawtree)
210 yield ' '.join(treeline)
211
212 - def _info_factory(self, plain, confidence_as_branch_length,
213 branch_length_only, max_confidence, format_confidence,
214 format_branch_length):
215 """Return a function that creates a nicely formatted node tag."""
216 if plain:
217
218 def make_info_string(clade, terminal=False):
219 return ''
220
221 elif confidence_as_branch_length:
222
223 def make_info_string(clade, terminal=False):
224 if terminal:
225
226 return ':' + format_confidence % max_confidence
227 else:
228 return ':' + format_confidence % clade.confidence
229
230 elif branch_length_only:
231
232 def make_info_string(clade, terminal=False):
233 return ':' + format_branch_length % clade.branch_length
234
235 else:
236
237 def make_info_string(clade, terminal=False):
238 if (terminal or
239 not hasattr(clade, 'confidence') or
240 clade.confidence is None):
241 return (':' + format_branch_length
242 ) % (clade.branch_length or 0.0)
243 else:
244 return (format_confidence + ':' + format_branch_length
245 ) % (clade.confidence, clade.branch_length or 0.0)
246
247 return make_info_string
248