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 import warnings
15
16 from cStringIO import StringIO
17
18 from Bio import BiopythonDeprecationWarning
19 from Bio.Phylo import Newick
20
21
22 NODECOMMENT_START = '[&'
23 NODECOMMENT_END = ']'
27 """Exception raised when Newick object construction cannot continue."""
28 pass
29
30
31
32
33
34 -def parse(handle, **kwargs):
35 """Iterate over the trees in a Newick file handle.
36
37 :returns: generator of Bio.Phylo.Newick.Tree objects.
38 """
39 return Parser(handle).parse(**kwargs)
40
41 -def write(trees, handle, plain=False, **kwargs):
42 """Write a trees in Newick format to the given file handle.
43
44 :returns: number of trees written.
45 """
46 return Writer(trees).write(handle, plain=plain, **kwargs)
47
48
49
50
51
52 -class Parser(object):
53 """Parse a Newick tree given a file handle.
54
55 Based on the parser in `Bio.Nexus.Trees`.
56 """
57
60
61 @classmethod
65
66 - def parse(self, values_are_confidence=False, rooted=False,
67
68 values_are_support=None):
69 """Parse the text stream this object was initialized with."""
70
71 if values_are_support is not None:
72 warnings.warn("use the argument values_are_confidence instead",
73 BiopythonDeprecationWarning)
74 values_are_confidence = values_are_support
75 self.values_are_confidence = values_are_confidence
76 self.rooted = rooted
77 buf = ''
78 for line in self.handle:
79 buf += line.rstrip()
80 if buf.endswith(';'):
81 yield self._parse_tree(buf, rooted)
82 buf = ''
83 if buf:
84
85 yield self._parse_tree(buf, rooted)
86
91
93 """Parse ``(a,b,c...)[[[xx]:]yy]`` into subcomponents, recursively."""
94 text = text.strip().rstrip(';')
95 if text.count('(')!=text.count(')'):
96 raise NewickError("Parentheses do not match in (sub)tree: " + text)
97
98 if text.count('(') == 0:
99
100 return self._parse_tag(text)
101
102
103 close_posn = text.rfind(')')
104 subtrees = []
105
106 plevel = 0
107 prev = 1
108 for posn in range(1, close_posn):
109 if text[posn] == '(':
110 plevel += 1
111 elif text[posn] == ')':
112 plevel -= 1
113 elif text[posn] == ',' and plevel == 0:
114 subtrees.append(text[prev:posn])
115 prev = posn + 1
116 subtrees.append(text[prev:close_posn])
117
118 clade = self._parse_tag(text[close_posn+1:])
119 clade.clades = [self._parse_subtree(st) for st in subtrees]
120 return clade
121
162
163
164
165
166
167 -class Writer(object):
168 """Based on the writer in Bio.Nexus.Trees (str, to_string)."""
169
172
173 - def write(self, handle, **kwargs):
174 """Write this instance's trees to a file handle."""
175 count = 0
176 for treestr in self.to_strings(**kwargs):
177 handle.write(treestr + '\n')
178 count += 1
179 return count
180
181 - def to_strings(self, confidence_as_branch_length=False,
182 branch_length_only=False, plain=False,
183 plain_newick=True, ladderize=None, max_confidence=1.0,
184 format_confidence='%1.2f', format_branch_length='%1.5f',
185
186 support_as_branchlengths=None, branchlengths_only=None,
187 max_support=None):
188 """Return an iterable of PAUP-compatible tree lines."""
189
190 if support_as_branchlengths is not None:
191 warnings.warn(
192 "use the argument confidence_as_branch_length instead",
193 BiopythonDeprecationWarning)
194 confidence_as_branch_length = support_as_branchlengths
195 if branchlengths_only is not None:
196 warnings.warn("use the argument branch_length_only instead",
197 BiopythonDeprecationWarning)
198 branch_length_only = branchlengths_only
199 if max_support is not None:
200 warnings.warn("use the argument max_confidence instead",
201 BiopythonDeprecationWarning)
202 max_confidence = max_support
203
204 if confidence_as_branch_length or branch_length_only:
205 plain = False
206 make_info_string = self._info_factory(plain,
207 confidence_as_branch_length, branch_length_only, max_confidence,
208 format_confidence, format_branch_length)
209 def newickize(clade):
210 """Convert a node tree to a Newick tree string, recursively."""
211 if clade.is_terminal():
212 return ((clade.name or '')
213 + make_info_string(clade, terminal=True))
214 else:
215 subtrees = (newickize(sub) for sub in clade)
216 return '(%s)%s' % (','.join(subtrees),
217 make_info_string(clade))
218
219
220 for tree in self.trees:
221 if ladderize in ('left', 'LEFT', 'right', 'RIGHT'):
222
223 tree.ladderize(reverse=(ladderize in ('right', 'RIGHT')))
224 rawtree = newickize(tree.root) + ';'
225 if plain_newick:
226 yield rawtree
227 continue
228
229 treeline = ['tree', (tree.name or 'a_tree'), '=']
230 if tree.weight != 1:
231 treeline.append('[&W%s]' % round(float(tree.weight), 3))
232 if tree.rooted:
233 treeline.append('[&R]')
234 treeline.append(rawtree)
235 yield ' '.join(treeline)
236
237 - def _info_factory(self, plain, confidence_as_branch_length,
238 branch_length_only, max_confidence, format_confidence,
239 format_branch_length):
240 """Return a function that creates a nicely formatted node tag."""
241 if plain:
242
243 def make_info_string(clade, terminal=False):
244 return ''
245
246 elif confidence_as_branch_length:
247
248 def make_info_string(clade, terminal=False):
249 if terminal:
250
251 return ':' + format_confidence % max_confidence
252 else:
253 return ':' + format_confidence % clade.confidence
254
255 elif branch_length_only:
256
257 def make_info_string(clade, terminal=False):
258 return ':' + format_branch_length % clade.branch_length
259
260 else:
261
262 def make_info_string(clade, terminal=False):
263 if (terminal or
264 not hasattr(clade, 'confidence') or
265 clade.confidence is None):
266 return (':' + format_branch_length
267 ) % (clade.branch_length or 0.0)
268 else:
269 return (format_confidence + ':' + format_branch_length
270 ) % (clade.confidence, clade.branch_length or 0.0)
271
272 return make_info_string
273