commonnexus 1.8.0__tar.gz → 1.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {commonnexus-1.8.0 → commonnexus-1.9.1}/PKG-INFO +10 -9
  2. {commonnexus-1.8.0 → commonnexus-1.9.1}/README.md +9 -8
  3. {commonnexus-1.8.0 → commonnexus-1.9.1}/setup.cfg +1 -1
  4. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/__init__.py +1 -1
  5. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/nexus.py +15 -2
  6. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/tools/normalise.py +34 -3
  7. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/PKG-INFO +10 -9
  8. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_nexus.py +15 -0
  9. commonnexus-1.9.1/tests/test_tools_normalise.py +185 -0
  10. commonnexus-1.8.0/tests/test_tools_normalise.py +0 -83
  11. {commonnexus-1.8.0 → commonnexus-1.9.1}/LICENSE +0 -0
  12. {commonnexus-1.8.0 → commonnexus-1.9.1}/pyproject.toml +0 -0
  13. {commonnexus-1.8.0 → commonnexus-1.9.1}/setup.py +0 -0
  14. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/__main__.py +0 -0
  15. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/__init__.py +0 -0
  16. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/assumptions.py +0 -0
  17. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/base.py +0 -0
  18. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/characters.py +0 -0
  19. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/codons.py +0 -0
  20. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/distances.py +0 -0
  21. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/notes.py +0 -0
  22. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/sets.py +0 -0
  23. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/taxa.py +0 -0
  24. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/trees.py +0 -0
  25. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/blocks/unaligned.py +0 -0
  26. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/cli_util.py +0 -0
  27. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/command.py +0 -0
  28. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/__init__.py +0 -0
  29. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/characters.py +0 -0
  30. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/combine.py +0 -0
  31. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/help.py +0 -0
  32. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/normalise.py +0 -0
  33. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/split.py +0 -0
  34. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/taxa.py +0 -0
  35. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/commands/trees.py +0 -0
  36. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/tokenizer.py +0 -0
  37. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/tools/__init__.py +0 -0
  38. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/tools/combine.py +0 -0
  39. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/tools/matrix.py +0 -0
  40. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus/util.py +0 -0
  41. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/SOURCES.txt +0 -0
  42. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/dependency_links.txt +0 -0
  43. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/entry_points.txt +0 -0
  44. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/not-zip-safe +0 -0
  45. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/requires.txt +0 -0
  46. {commonnexus-1.8.0 → commonnexus-1.9.1}/src/commonnexus.egg-info/top_level.txt +0 -0
  47. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_base.py +0 -0
  48. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_characters.py +0 -0
  49. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_distances.py +0 -0
  50. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_notes.py +0 -0
  51. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_sets.py +0 -0
  52. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_taxa.py +0 -0
  53. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_blocks_trees.py +0 -0
  54. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_cli.py +0 -0
  55. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_command.py +0 -0
  56. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_dendropy_examples.py +0 -0
  57. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_regressions.py +0 -0
  58. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_tokenizer.py +0 -0
  59. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_tools_combine.py +0 -0
  60. {commonnexus-1.8.0 → commonnexus-1.9.1}/tests/test_tools_matrix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: commonnexus
3
- Version: 1.8.0
3
+ Version: 1.9.1
4
4
  Summary: A nexus (phylogenetics) file reader and writer (.nex, .trees)
5
5
  Home-page: https://github.com/dlce-eva/commonnexus
6
6
  Author: Robert Forkel
@@ -83,14 +83,15 @@ and writing NEXUS
83
83
  >>> print(Nexus.from_blocks(Data.from_data(nex.CHARACTERS.get_matrix())))
84
84
  #NEXUS
85
85
  BEGIN DATA;
86
- DIMENSIONS NCHAR=10;
87
- FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
88
- MATRIX
89
- t1 1001010000
90
- t2 0101000100
91
- t3 0011101010
92
- t4 0001100001
93
- t5 0001100001;
86
+ DIMENSIONS NCHAR=10;
87
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
88
+ MATRIX
89
+ t1 1001010000
90
+ t2 0101000100
91
+ t3 0011101010
92
+ t4 0001100001
93
+ t5 0001100001
94
+ ;
94
95
  END;
95
96
  ```
96
97
 
@@ -46,14 +46,15 @@ and writing NEXUS
46
46
  >>> print(Nexus.from_blocks(Data.from_data(nex.CHARACTERS.get_matrix())))
47
47
  #NEXUS
48
48
  BEGIN DATA;
49
- DIMENSIONS NCHAR=10;
50
- FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
51
- MATRIX
52
- t1 1001010000
53
- t2 0101000100
54
- t3 0011101010
55
- t4 0001100001
56
- t5 0001100001;
49
+ DIMENSIONS NCHAR=10;
50
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
51
+ MATRIX
52
+ t1 1001010000
53
+ t2 0101000100
54
+ t3 0011101010
55
+ t4 0001100001
56
+ t5 0001100001
57
+ ;
57
58
  END;
58
59
  ```
59
60
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = commonnexus
3
- version = 1.8.0
3
+ version = 1.9.1
4
4
  author = Robert Forkel
5
5
  author_email = robert_forkel@eva.mpg.de
6
6
  description = A nexus (phylogenetics) file reader and writer (.nex, .trees)
@@ -1,4 +1,4 @@
1
1
  from .nexus import Nexus, Config # noqa: F401
2
2
  from commonnexus.blocks import Block # noqa: F401
3
3
 
4
- __version__ = '1.8.0'
4
+ __version__ = '1.9.1'
@@ -5,6 +5,7 @@ import collections
5
5
  import dataclasses
6
6
 
7
7
  from .tokenizer import TokenType, iter_tokens, get_name
8
+ from .util import log_or_raise
8
9
  from commonnexus.command import Command
9
10
  from commonnexus.blocks import Block
10
11
 
@@ -45,7 +46,7 @@ class Config:
45
46
 
46
47
  class Nexus(list):
47
48
  """
48
- A NEXUS object implemented as list of tokens with methods to access newick constituents.
49
+ A NEXUS object implemented as list of commands with methods to read and write blocks.
49
50
 
50
51
  From the spec:
51
52
 
@@ -95,6 +96,7 @@ class Nexus(list):
95
96
  """
96
97
  self.cfg = config or Config(**kw)
97
98
  self.trailing_whitespace = []
99
+ self.leading = []
98
100
  self.block_implementations = {}
99
101
  for cls in Block.__subclasses__():
100
102
  self.block_implementations[cls.__name__.upper()] = cls
@@ -120,7 +122,10 @@ class Nexus(list):
120
122
  if token.is_semicolon:
121
123
  commands.append(Command(tuple(tokens)))
122
124
  tokens = []
123
- self.trailing_whitespace = tokens
125
+ if commands:
126
+ self.trailing_whitespace = tokens
127
+ else:
128
+ self.leading = tokens
124
129
  s = commands
125
130
  list.__init__(self, s)
126
131
 
@@ -211,6 +216,7 @@ class Nexus(list):
211
216
  END;
212
217
  """
213
218
  return NEXUS \
219
+ + ''.join(str(t) for t in self.leading) \
214
220
  + ''.join(''.join(str(t) for t in cmd) for cmd in self) \
215
221
  + ''.join(str(t) for t in self.trailing_whitespace)
216
222
 
@@ -224,8 +230,10 @@ class Nexus(list):
224
230
  p.write_text(text, encoding=self.cfg.encoding)
225
231
 
226
232
  def iter_comments(self):
233
+ yield from (t for t in self.leading if t.type == TokenType.COMMENT)
227
234
  for cmd in self:
228
235
  yield from (t for t in cmd if t.type == TokenType.COMMENT)
236
+ yield from (t for t in self.trailing_whitespace if t.type == TokenType.COMMENT)
229
237
 
230
238
  @property
231
239
  def comments(self) -> typing.List[str]:
@@ -262,6 +270,8 @@ class Nexus(list):
262
270
 
263
271
  def validate(self, log=None):
264
272
  valid = True
273
+ if any(t.type not in {TokenType.WHITESPACE, TokenType.COMMENT} for t in self.leading):
274
+ log_or_raise('Invalid token in preamble', log=log)
265
275
  for block in self.iter_blocks():
266
276
  #
267
277
  # FIXME: we can do a lot of validation here! If block.__commands__ is a list, there is
@@ -269,6 +279,9 @@ class Nexus(list):
269
279
  # If Payload.__multivalued__ == False, only one command instance is allowed, ...
270
280
  #
271
281
  valid = valid and block.validate(log=log)
282
+ if any(t.type not in {TokenType.WHITESPACE, TokenType.COMMENT}
283
+ for t in self.trailing_whitespace):
284
+ log_or_raise('Invalid token in text after the last command', log=log)
272
285
  return valid
273
286
 
274
287
  def get_numbers(self, object_name, items):
@@ -20,6 +20,8 @@ In addition, after normalisation, the following assumptions hold:
20
20
  import typing
21
21
  import collections
22
22
 
23
+ import newick
24
+
23
25
  from commonnexus import Nexus
24
26
  from commonnexus.blocks.characters import Data
25
27
  from commonnexus.blocks import Taxa, Distances, Characters, Trees
@@ -28,15 +30,28 @@ from commonnexus.blocks import Taxa, Distances, Characters, Trees
28
30
  def normalise(nexus: Nexus,
29
31
  data_to_characters: bool = False,
30
32
  strip_comments: bool = False,
31
- remove_taxa: typing.Optional[typing.Container[str]] = None) -> Nexus:
33
+ remove_taxa: typing.Optional[typing.Container[str]] = None,
34
+ rename_taxa: typing.Optional[
35
+ typing.Union[typing.Callable[[str], str], typing.Dict[str, str]]] = None,
36
+ ) -> Nexus:
32
37
  """
38
+ Normalise a `Nexus` object as described above.
39
+
33
40
  :param nexus: A `Nexus` object to be normalised in-place.
34
41
  :param data_to_characters: Flag signaling whether DATA blocks should be converted to CHARACTER \
35
42
  blocks.
36
43
  :param strip_comments: Flag signaling whether to remove all non-command comments.
37
44
  :param remove_taxa: Container of taxon labels specifying taxa to remove from relevant blocks.
45
+ :param rename_taxa: Specification of taxa to rename; either a ``dict``, mapping old names to \
46
+ new names, or a callable, accepting the old name as sole argument and returning the new name.
38
47
  :return: The modified `Nexus` object.
39
48
 
49
+ .. warning::
50
+
51
+ ``remove_taxa`` and ``rename_taxa`` only operate on TAXA, CHARACTERS/DATA, DISTANCES and
52
+ TREES blocks. Thus, normalisation may result in an inconsistent NEXUS file, if the file
53
+ contains other blocks which reference taxa (e.g. NOTES).
54
+
40
55
  .. code-block:: python
41
56
 
42
57
  >>> from commonnexus import Nexus
@@ -94,6 +109,13 @@ def normalise(nexus: Nexus,
94
109
  """
95
110
  remove_taxa = remove_taxa or []
96
111
 
112
+ def rename_taxon(s):
113
+ if rename_taxa is None:
114
+ return s
115
+ if isinstance(rename_taxa, dict):
116
+ return rename_taxa.get(s, s)
117
+ return rename_taxa(s)
118
+
97
119
  if strip_comments:
98
120
  nexus = Nexus([cmd.without_comments() for cmd in nexus], config=nexus.cfg)
99
121
  nexus = Nexus([cmd.with_normalised_whitespace() for cmd in nexus], config=nexus.cfg)
@@ -102,7 +124,8 @@ def normalise(nexus: Nexus,
102
124
  if nexus.characters:
103
125
  matrix = nexus.characters.get_matrix()
104
126
  taxlabels = list(matrix.keys())
105
- matrix = collections.OrderedDict((k, v) for k, v in matrix.items() if k not in remove_taxa)
127
+ matrix = collections.OrderedDict(
128
+ (rename_taxon(k), v) for k, v in matrix.items() if k not in remove_taxa)
106
129
  characters = nexus.DATA or nexus.CHARACTERS
107
130
  cls = Data if characters.name == 'DATA' and not data_to_characters else Characters
108
131
  nexus.replace_block(
@@ -116,20 +139,28 @@ def normalise(nexus: Nexus,
116
139
  else:
117
140
  taxlabels = list(matrix.keys())
118
141
  matrix = collections.OrderedDict(
119
- (k, collections.OrderedDict((kk, vv) for kk, vv in v.items() if kk not in remove_taxa))
142
+ (rename_taxon(k), collections.OrderedDict(
143
+ (rename_taxon(kk), vv) for kk, vv in v.items() if kk not in remove_taxa))
120
144
  for k, v in matrix.items() if k not in remove_taxa)
121
145
  nexus.replace_block(nexus.DISTANCES, Distances.from_data(matrix))
122
146
 
123
147
  if nexus.TREES:
148
+ def rename(n):
149
+ if n.name:
150
+ new = rename_taxon(n.unquoted_name)
151
+ n.name = newick.Node(new, auto_quote=True).name
152
+
124
153
  trees = []
125
154
  for tree in nexus.TREES.trees:
126
155
  nwk = nexus.TREES.translate(tree) if nexus.TREES.TRANSLATE else tree.newick
127
156
  if remove_taxa:
128
157
  nwk.prune_by_names(remove_taxa)
158
+ nwk.visit(rename)
129
159
  trees.append((tree.name, nwk, tree.rooted))
130
160
  nexus.replace_block(nexus.TREES, Trees.from_data(*trees))
131
161
 
132
162
  if taxlabels:
163
+ taxlabels = [rename_taxon(t) for t in taxlabels]
133
164
  taxa = Taxa.from_data([t for t in taxlabels if t not in remove_taxa])
134
165
  if nexus.TAXA:
135
166
  assert nexus.TAXA.DIMENSIONS.ntax == len(taxlabels)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: commonnexus
3
- Version: 1.8.0
3
+ Version: 1.9.1
4
4
  Summary: A nexus (phylogenetics) file reader and writer (.nex, .trees)
5
5
  Home-page: https://github.com/dlce-eva/commonnexus
6
6
  Author: Robert Forkel
@@ -83,14 +83,15 @@ and writing NEXUS
83
83
  >>> print(Nexus.from_blocks(Data.from_data(nex.CHARACTERS.get_matrix())))
84
84
  #NEXUS
85
85
  BEGIN DATA;
86
- DIMENSIONS NCHAR=10;
87
- FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
88
- MATRIX
89
- t1 1001010000
90
- t2 0101000100
91
- t3 0011101010
92
- t4 0001100001
93
- t5 0001100001;
86
+ DIMENSIONS NCHAR=10;
87
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
88
+ MATRIX
89
+ t1 1001010000
90
+ t2 0101000100
91
+ t3 0011101010
92
+ t4 0001100001
93
+ t5 0001100001
94
+ ;
94
95
  END;
95
96
  ```
96
97
 
@@ -161,3 +161,18 @@ def test_Booleans_With_Values(fixture_dir):
161
161
  def test_Mesquite_multitaxa(fixture_dir):
162
162
  nex = Nexus.from_file(fixture_dir / 'multitaxa_mesquite.nex')
163
163
  assert [char.get_matrix() for char in nex.blocks['CHARACTERS']]
164
+
165
+
166
+ def test_comments():
167
+ nex = Nexus('#NEXUS\n[comment]')
168
+ assert nex.comments == ['comment']
169
+ nex.append_block(Block.from_commands([], name='b', comment='block comment'))
170
+ assert nex.comments == ['comment', 'block comment']
171
+
172
+ with pytest.raises(ValueError):
173
+ nex = Nexus('#NEXUS\n[comment]]')
174
+ nex.validate()
175
+
176
+ with pytest.raises(ValueError):
177
+ nex = Nexus('#NEXUS\nBEGIN b; end;[comment]]')
178
+ nex.validate()
@@ -0,0 +1,185 @@
1
+ from commonnexus import Nexus
2
+ from commonnexus.tools.normalise import normalise
3
+
4
+
5
+ def test_normalise(nexus):
6
+ nex = nexus(
7
+ CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
8
+ DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
9
+ TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
10
+ res = str(normalise(nex))
11
+ assert res == """#NEXUS
12
+ BEGIN TAXA;
13
+ DIMENSIONS NTAX=3;
14
+ TAXLABELS 't 1' t2 t3;
15
+ END;
16
+ BEGIN CHARACTERS;
17
+ DIMENSIONS NCHAR=3;
18
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
19
+ MATRIX
20
+ 't 1' 100
21
+ t2 010
22
+ t3 001
23
+ ;
24
+ END;
25
+ BEGIN DISTANCES;
26
+ DIMENSIONS NTAX=3;
27
+ FORMAT TRIANGLE=BOTH MISSING=?;
28
+ MATRIX
29
+ 't 1' 0 1.0 2.0
30
+ t2 1.0 0 3.0
31
+ t3 2.0 3.0 0
32
+ ;
33
+ END;
34
+ BEGIN TREES;
35
+ TREE 1 = ('t 1',t2,t3);
36
+ END;"""
37
+ res = normalise(Nexus(res))
38
+ assert res.characters.get_matrix()
39
+ assert res.DISTANCES.get_matrix()
40
+
41
+ res.remove_block(res.TAXA)
42
+ res.remove_block(res.CHARACTERS)
43
+ res = normalise(res)
44
+ assert res.TAXA
45
+
46
+
47
+ def test_normalise_remove_taxon(nexus):
48
+ nex = nexus(
49
+ CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
50
+ DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
51
+ TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
52
+ res = str(normalise(nex, remove_taxa={'t2'}))
53
+ assert res == """#NEXUS
54
+ BEGIN TAXA;
55
+ DIMENSIONS NTAX=2;
56
+ TAXLABELS 't 1' t3;
57
+ END;
58
+ BEGIN CHARACTERS;
59
+ DIMENSIONS NCHAR=3;
60
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
61
+ MATRIX
62
+ 't 1' 100
63
+ t3 001
64
+ ;
65
+ END;
66
+ BEGIN DISTANCES;
67
+ DIMENSIONS NTAX=2;
68
+ FORMAT TRIANGLE=BOTH MISSING=?;
69
+ MATRIX
70
+ 't 1' 0 2.0
71
+ t3 2.0 0
72
+ ;
73
+ END;
74
+ BEGIN TREES;
75
+ TREE 1 = ('t 1',t3);
76
+ END;"""
77
+
78
+
79
+ def test_normalise_rename_taxon1(nexus):
80
+ nex = nexus(
81
+ CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
82
+ DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
83
+ TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
84
+ res = str(normalise(nex, rename_taxa={'t 1': 't1'}))
85
+ assert res == """#NEXUS
86
+ BEGIN TAXA;
87
+ DIMENSIONS NTAX=3;
88
+ TAXLABELS t1 t2 t3;
89
+ END;
90
+ BEGIN CHARACTERS;
91
+ DIMENSIONS NCHAR=3;
92
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
93
+ MATRIX
94
+ t1 100
95
+ t2 010
96
+ t3 001
97
+ ;
98
+ END;
99
+ BEGIN DISTANCES;
100
+ DIMENSIONS NTAX=3;
101
+ FORMAT TRIANGLE=BOTH MISSING=?;
102
+ MATRIX
103
+ t1 0 1.0 2.0
104
+ t2 1.0 0 3.0
105
+ t3 2.0 3.0 0
106
+ ;
107
+ END;
108
+ BEGIN TREES;
109
+ TREE 1 = (t1,t2,t3);
110
+ END;"""
111
+
112
+
113
+ def test_normalise_rename_taxon2(nexus):
114
+ nex = nexus(
115
+ CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
116
+ DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
117
+ TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
118
+ res = str(normalise(nex, rename_taxa=lambda t: t.replace(' ', '_')))
119
+ assert res == """#NEXUS
120
+ BEGIN TAXA;
121
+ DIMENSIONS NTAX=3;
122
+ TAXLABELS t_1 t2 t3;
123
+ END;
124
+ BEGIN CHARACTERS;
125
+ DIMENSIONS NCHAR=3;
126
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
127
+ MATRIX
128
+ t_1 100
129
+ t2 010
130
+ t3 001
131
+ ;
132
+ END;
133
+ BEGIN DISTANCES;
134
+ DIMENSIONS NTAX=3;
135
+ FORMAT TRIANGLE=BOTH MISSING=?;
136
+ MATRIX
137
+ t_1 0 1.0 2.0
138
+ t2 1.0 0 3.0
139
+ t3 2.0 3.0 0
140
+ ;
141
+ END;
142
+ BEGIN TREES;
143
+ TREE 1 = (t_1,t2,t3);
144
+ END;"""
145
+
146
+
147
+ def test_normalise_rename_taxon3(nexus):
148
+ nex = nexus(
149
+ CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
150
+ DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
151
+ TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
152
+ res = str(normalise(nex, rename_taxa=lambda t: t.replace(' ', ':')))
153
+ assert res == """#NEXUS
154
+ BEGIN TAXA;
155
+ DIMENSIONS NTAX=3;
156
+ TAXLABELS 't:1' t2 t3;
157
+ END;
158
+ BEGIN CHARACTERS;
159
+ DIMENSIONS NCHAR=3;
160
+ FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
161
+ MATRIX
162
+ 't:1' 100
163
+ t2 010
164
+ t3 001
165
+ ;
166
+ END;
167
+ BEGIN DISTANCES;
168
+ DIMENSIONS NTAX=3;
169
+ FORMAT TRIANGLE=BOTH MISSING=?;
170
+ MATRIX
171
+ 't:1' 0 1.0 2.0
172
+ t2 1.0 0 3.0
173
+ t3 2.0 3.0 0
174
+ ;
175
+ END;
176
+ BEGIN TREES;
177
+ TREE 1 = ('t:1',t2,t3);
178
+ END;"""
179
+
180
+
181
+ def test_normalise_stripcomments():
182
+ res = normalise(Nexus('#nexus beg[c]in bl[&c]ock; cmd; end[c];'), strip_comments=True)
183
+ assert '[c]' not in str(res)
184
+ assert '[&c]' in str(res)
185
+ assert res.BLOCK
@@ -1,83 +0,0 @@
1
- from commonnexus import Nexus
2
- from commonnexus.tools.normalise import normalise
3
-
4
-
5
- def test_normalise(nexus):
6
- nex = nexus(
7
- CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
8
- DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
9
- TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
10
- res = str(normalise(nex))
11
- assert res == """#NEXUS
12
- BEGIN TAXA;
13
- DIMENSIONS NTAX=3;
14
- TAXLABELS 't 1' t2 t3;
15
- END;
16
- BEGIN CHARACTERS;
17
- DIMENSIONS NCHAR=3;
18
- FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
19
- MATRIX
20
- 't 1' 100
21
- t2 010
22
- t3 001
23
- ;
24
- END;
25
- BEGIN DISTANCES;
26
- DIMENSIONS NTAX=3;
27
- FORMAT TRIANGLE=BOTH MISSING=?;
28
- MATRIX
29
- 't 1' 0 1.0 2.0
30
- t2 1.0 0 3.0
31
- t3 2.0 3.0 0
32
- ;
33
- END;
34
- BEGIN TREES;
35
- TREE 1 = ('t 1',t2,t3);
36
- END;"""
37
- res = normalise(Nexus(res))
38
- assert res.characters.get_matrix()
39
- assert res.DISTANCES.get_matrix()
40
-
41
- res.remove_block(res.TAXA)
42
- res.remove_block(res.CHARACTERS)
43
- res = normalise(res)
44
- assert res.TAXA
45
-
46
-
47
- def test_normalise_remove_taxon(nexus):
48
- nex = nexus(
49
- CHARACTERS="DIMENSIONS NCHAR=3; MATRIX 't 1' 100 t2 010 t3 001;",
50
- DISTANCES="FORMAT NODIAGONAL; MATRIX 't 1' t2 1.0 t3 2.0 3.0;",
51
- TREES="TRANSLATE a 't 1', b t2, c t3; TREE 1 = (a,b\n,c);")
52
- res = str(normalise(nex, remove_taxa={'t2'}))
53
- assert res == """#NEXUS
54
- BEGIN TAXA;
55
- DIMENSIONS NTAX=2;
56
- TAXLABELS 't 1' t3;
57
- END;
58
- BEGIN CHARACTERS;
59
- DIMENSIONS NCHAR=3;
60
- FORMAT DATATYPE=STANDARD MISSING=? GAP=- SYMBOLS="01";
61
- MATRIX
62
- 't 1' 100
63
- t3 001
64
- ;
65
- END;
66
- BEGIN DISTANCES;
67
- DIMENSIONS NTAX=2;
68
- FORMAT TRIANGLE=BOTH MISSING=?;
69
- MATRIX
70
- 't 1' 0 2.0
71
- t3 2.0 0
72
- ;
73
- END;
74
- BEGIN TREES;
75
- TREE 1 = ('t 1',t3);
76
- END;"""
77
-
78
-
79
- def test_normalise_stripcomments():
80
- res = normalise(Nexus('#nexus beg[c]in bl[&c]ock; cmd; end[c];'), strip_comments=True)
81
- assert '[c]' not in str(res)
82
- assert '[&c]' in str(res)
83
- assert res.BLOCK
File without changes
File without changes
File without changes