def test_all_nonroot_branchlengths_zero(self): newicks = ['((b:0)a:0)root:0;', '((b:0)a:0)root:1;'] for nwk in newicks: st = parse_newick(nwk) with self.assertRaisesRegex(ValueError, "must have a positive length"): validate_tree(st)
def test_nonroot_negative_branchlengths(self): newicks = [ '((b:-1)a:1)root:1;', '((b:100)a:-100)root:0;', '(b:1,c:-1)a:2;', '((b:-1)a:0)root;' ] for nwk in newicks: st = parse_newick(nwk) with self.assertRaisesRegex(ValueError, "must have nonnegative lengths"): validate_tree(st)
def _validate_and_match_data(self, ignore_missing_samples, filter_extra_samples, filter_missing_features, shear_to_table, shear_to_feature_metadata): if self.is_community_plot: self.table, self.samples, self.tip_md, self.int_md = match_inputs( self.tree, self.table, self.samples, self.features, self.ordination, ignore_missing_samples, filter_extra_samples, filter_missing_features ) # Remove empty samples and features from the table (and remove the # removed samples from the sample metadata). We also pass in the # ordination, if present, to this function -- so we can throw an # error if the ordination actually contains these empty # samples/features. # # We purposefully do this removal *after* matching (so we know the # data inputs match up) and *before* shearing (so empty features # in the table are no longer included as tips in the tree). self.table, self.samples = remove_empty_samples_and_features( self.table, self.samples, self.ordination ) # remove unobserved features from the phylogeny (shear the tree) if shear_to_table: features = set(self.table.ids(axis='observation')) self.tree = self.tree.shear(features) # Remove features in the feature metadata that are no longer # present in the tree, due to being shorn off if self.tip_md is not None or self.int_md is not None: # (Technically they should always both be None or both be # DataFrames -- there's no in-between) self.tip_md, self.int_md = filter_feature_metadata_to_tree( self.tip_md, self.int_md, self.tree ) else: if shear_to_feature_metadata: features = set(self.features.index) all_tips = set(bp_tree_tips(self.tree)) # check that feature metadata contains at least 1 tip if not features.intersection(all_tips): raise ValueError( "Cannot shear tree to feature metadata: no tips in " "the tree are present in the feature metadata." ) self.tree = self.tree.shear(features) self.tip_md, self.int_md = match_tree_and_feature_metadata( self.tree, self.features ) validate_tree(self.tree)
def test_validate_tree_duplicate_internal_node_names(self): bad_newicks = [ # Two non-root internal nodes have same name '((a:1,b:3)c:2,(d:2,e:3)c:5)r:2;', # Two internal nodes (one of which is the root) have same name '((a:1,b:3)c:2,(d:2,e:3)f:5)c:2;' ] for nwk in bad_newicks: t = parse_newick(nwk) with self.assertWarnsRegex( TreeFormatWarning, "Internal node names in the tree are not unique"): validate_tree(t)
def test_validate_tree_overlapping_tip_and_internal_node_names(self): bad_newicks = [ # Tip overlaps with non-root internal node '((a:1,b:3)a:2,d:5)e:2;', # Tip overlaps with root node '((a:1,b:3)c:2,d:5)a:2;', # Tip overlaps with both non-root and root internal nodes '((a:1,b:3)a:2,d:5)a:2;' ] for nwk in bad_newicks: t = parse_newick(nwk) with self.assertRaisesRegex( ValueError, "Tip names in the tree cannot overlap with internal node names" ): validate_tree(t)
def test_validate_tree(self): validate_tree(self.tree) # check the tree is still equivalent obs = self.tree exp = parse_newick(self.nwk) self.assertEqual(len(obs), len(exp)) for i in range(1, len(exp) + 1): node_o = obs.postorderselect(i) node_e = exp.postorderselect(i) self.assertEqual(node_o, node_e) self.assertEqual(obs.length(node_o), exp.length(node_e)) self.assertEqual(obs.name(node_o), exp.name(node_e))
def test_validate_tree_duplicate_tip_names(self): t = parse_newick('((i:1,a:3)b:2,i:5)r:2;') with self.assertRaisesRegex(ValueError, "Tip names in the tree must be unique"): validate_tree(t)