From 4f42fc4cdb8e99380ec0d421a3e1ba212559e7bc Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 16:57:07 -0600 Subject: [PATCH 01/17] adding initial commit --- biom/table.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/biom/table.py b/biom/table.py index a70846de..579412b5 100644 --- a/biom/table.py +++ b/biom/table.py @@ -948,6 +948,65 @@ def _get_col(self, col_idx): self._data = self._data.tocsc() return self._data.getcol(col_idx) + def align_metadata(self, metadata, axis='sample'): + """ Aligns metadata against biom table, only keeping common ids. + + Parameters + ---------- + metadata : pd.DataFrame + The metadata, either respect to the sample metadata + or observation metadata. + axis : {'sample', 'observation'} + The axis on which to operate. + + Returns + ------- + biom.Table + A filtered biom table. + pd.DataFrame + A filtered metadata table. + """ + ids = set(self.ids(axis)) & set(metadata.index) + filter_f = lambda v, i, m: i in ids + t = self.table.filter(filter_f, axis=axis, inplace=False) + md = metadata.loc[self.ids()] + return t, md + + def align_tree(self, tree, axis='sample'): + """ Aligns biom table against tree, only keeping common ids. + + Parameters + ---------- + tree : skbio.TreeNode + The tree object, either respect to the sample metadata + or observation metadata. + axis : {'sample', 'observation'} + The axis on which to operate. + + Returns + ------- + biom.Table + A filtered biom table. + skbio.TreeNode + A filtered skbio TreeNode object. + """ + tips = [x.name for x in tree.tips()] + common_tips = set(tips) & set(table.ids(axis=axis)) + _tree = tree.shear(names=list(common_tips)) + + def filter_uncommon(val, id_, md): + return id_ in common_tips + + _table = table.filter(filter_uncommon, axis=axis, inplace=False) + _tree.prune() + + def sort_f(x): + return [n.name for n in _tree.tips()] + + _table = _table.sort(sort_f=sort_f, axis=axis) + return _table, _tree + + def reduce(self, f, axis): """Reduce over axis using function `f` From 63d55d0f7c8570da67d371a063bf20ef11b63264 Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 21:20:24 -0600 Subject: [PATCH 02/17] adding align_to_dataframe initial tests --- biom/table.py | 10 +++---- biom/tests/test_table.py | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/biom/table.py b/biom/table.py index 579412b5..327a58e1 100644 --- a/biom/table.py +++ b/biom/table.py @@ -948,8 +948,8 @@ def _get_col(self, col_idx): self._data = self._data.tocsc() return self._data.getcol(col_idx) - def align_metadata(self, metadata, axis='sample'): - """ Aligns metadata against biom table, only keeping common ids. + def align_to_dataframe(self, metadata, axis='sample'): + """ Aligns dataframe against biom table, only keeping common ids. Parameters ---------- @@ -966,10 +966,10 @@ def align_metadata(self, metadata, axis='sample'): pd.DataFrame A filtered metadata table. """ - ids = set(self.ids(axis)) & set(metadata.index) + ids = set(self.ids(axis=axis)) & set(metadata.index) filter_f = lambda v, i, m: i in ids - t = self.table.filter(filter_f, axis=axis, inplace=False) - md = metadata.loc[self.ids()] + t = self.filter(filter_f, axis=axis, inplace=False) + md = metadata.loc[t.ids(axis=axis)] return t, md def align_tree(self, tree, axis='sample'): diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index dd0f4159..a8ec4cf1 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -45,6 +45,12 @@ except ImportError: HAVE_ANNDATA = False +try: + import skbio + HAVE_SKBIO = False +except ImportError: + HAVE_SKBIO = False + __author__ = "Daniel McDonald" __copyright__ = "Copyright 2011-2017, The BIOM Format Development Team" __credits__ = ["Daniel McDonald", "Jai Ram Rideout", "Justin Kuczynski", @@ -1815,6 +1821,61 @@ def test_add_sample_metadata_two_entries(self): self.assertEqual(t._sample_metadata[2]['D'], ['A', 'C']) self.assertEqual(t._sample_metadata[3]['D'], ['A', 'D']) + def test_align_to_dataframe_samples(self): + table = Table(np.array([[0, 0, 1, 1], + [2, 2, 4, 4], + [5, 5, 3, 3], + [0, 0, 0, 1]]).T, + ['o1', 'o2', 'o3', 'o4'], + ['s1', 's2', 's3', 's4']) + metadata = pd.DataFrame([['a', 'control'], + ['c', 'diseased'], + ['b', 'control']], + index=['s1', 's3', 's2'], + columns=['Barcode', 'Treatment']) + exp_table = Table(np.array([[0, 0, 1, 1], + [2, 2, 4, 4], + [5, 5, 3, 3]]).T, + ['o1', 'o2', 'o3', 'o4'], + ['s1', 's2', 's3']) + exp_metadata = pd.DataFrame([['a', 'control'], + ['b', 'control'], + ['c', 'diseased']], + index=['s1', 's2', 's3'], + columns=['Barcode', 'Treatment']) + res_table, res_metadata = table.align_to_dataframe(metadata) + pdt.assert_frame_equal(exp_metadata, res_metadata) + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + + def test_align_to_dataframe_observations(self): + table = Table(np.array([[0, 0, 1, 1], + [2, 2, 4, 4], + [5, 5, 3, 3], + [0, 0, 0, 1]]), + ['s1', 's2', 's3', 's4'], + ['o1', 'o2', 'o3', 'o4']) + metadata = pd.DataFrame([['a', 'control'], + ['c', 'diseased'], + ['b', 'control']], + index=['s1', 's3', 's2'], + columns=['Barcode', 'Treatment']) + exp_table = Table(np.array([[0, 0, 1, 1], + [2, 2, 4, 4], + [5, 5, 3, 3]]), + ['s1', 's2', 's3'], + ['o1', 'o2', 'o3', 'o4']) + exp_metadata = pd.DataFrame([['a', 'control'], + ['b', 'control'], + ['c', 'diseased']], + index=['s1', 's2', 's3'], + columns=['Barcode', 'Treatment']) + res_table, res_metadata = table.align_to_dataframe( + metadata, axis='observation') + pdt.assert_frame_equal(exp_metadata, res_metadata) + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + def test_get_value_by_ids(self): """Return the value located in the matrix by the ids""" t1 = Table(np.array([[5, 6], [7, 8]]), [3, 4], [1, 2]) From 0254dc362fa72764bc076c2dd9a2840058b421bd Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 21:57:43 -0600 Subject: [PATCH 03/17] test for align_tree --- biom/table.py | 24 +++---- biom/tests/test_table.py | 135 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 14 deletions(-) diff --git a/biom/table.py b/biom/table.py index 327a58e1..a9be5eee 100644 --- a/biom/table.py +++ b/biom/table.py @@ -967,12 +967,15 @@ def align_to_dataframe(self, metadata, axis='sample'): A filtered metadata table. """ ids = set(self.ids(axis=axis)) & set(metadata.index) + if len(ids) == 0: + raise TableException("No common ids between table and dataframe.") filter_f = lambda v, i, m: i in ids t = self.filter(filter_f, axis=axis, inplace=False) + t.remove_empty() md = metadata.loc[t.ids(axis=axis)] return t, md - def align_tree(self, tree, axis='sample'): + def align_tree(self, tree, axis='observation'): """ Aligns biom table against tree, only keeping common ids. Parameters @@ -990,20 +993,13 @@ def align_tree(self, tree, axis='sample'): skbio.TreeNode A filtered skbio TreeNode object. """ - tips = [x.name for x in tree.tips()] - common_tips = set(tips) & set(table.ids(axis=axis)) - _tree = tree.shear(names=list(common_tips)) - - def filter_uncommon(val, id_, md): - return id_ in common_tips - - _table = table.filter(filter_uncommon, axis=axis, inplace=False) + tips = {x.name for x in tree.tips()} + common_tips = tips & set(self.ids(axis=axis)) + _tree = tree.shear(names=common_tips) + _table = self.filter(common_tips, axis=axis, inplace=False) _tree.prune() - - def sort_f(x): - return [n.name for n in _tree.tips()] - - _table = _table.sort(sort_f=sort_f, axis=axis) + order = [n.name for n in _tree.tips()] + _table = _table.sort_order(order, axis=axis) return _table, _tree diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index a8ec4cf1..620a6785 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1876,6 +1876,141 @@ def test_align_to_dataframe_observations(self): self.assertEqual(res_table.descriptive_equality(exp_table), 'Tables appear equal') + def test_align_to_dataframe_samples_remove_empty(self): + table = Table(np.array([[0, 0, 1, 0], + [2, 2, 4, 0], + [5, 5, 3, 0], + [0, 0, 0, 1]]).T, + ['o1', 'o2', 'o3', 'o4'], + ['s1', 's2', 's3', 's4']) + metadata = pd.DataFrame([['a', 'control'], + ['c', 'diseased'], + ['b', 'control']], + index=['s1', 's3', 's2'], + columns=['Barcode', 'Treatment']) + exp_table = Table(np.array([[0, 0, 1], + [2, 2, 4], + [5, 5, 3]]).T, + ['o1', 'o2', 'o3'], + ['s1', 's2', 's3']) + exp_metadata = pd.DataFrame([['a', 'control'], + ['b', 'control'], + ['c', 'diseased']], + index=['s1', 's2', 's3'], + columns=['Barcode', 'Treatment']) + res_table, res_metadata = table.align_to_dataframe(metadata) + pdt.assert_frame_equal(exp_metadata, res_metadata) + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + + def test_align_to_dataframe_samples_empty(self): + table = Table(np.array([[0, 0, 1, 0], + [2, 2, 4, 0], + [5, 5, 3, 0], + [0, 0, 0, 1]]).T, + ['o1', 'o2', 'o3', 'o4'], + ['s1', 's2', 's3', 's4']) + metadata = pd.DataFrame([['a', 'control'], + ['c', 'diseased'], + ['b', 'control']], + index=['s1', 's3', 's2'], + columns=['Barcode', 'Treatment']) + exp_table = Table(np.array([[0, 0, 1], + [2, 2, 4], + [5, 5, 3]]).T, + ['o1', 'o2', 'o3'], + ['s1', 's2', 's3']) + exp_metadata = pd.DataFrame([['a', 'control'], + ['b', 'control'], + ['c', 'diseased']], + index=['s1', 's2', 's3'], + columns=['Barcode', 'Treatment']) + res_table, res_metadata = table.align_to_dataframe(metadata) + pdt.assert_frame_equal(exp_metadata, res_metadata) + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + + def test_align_to_dataframe_samples_no_common_ids(self): + table = Table(np.array([[0, 0, 1, 0], + [2, 2, 4, 0], + [5, 5, 3, 0], + [0, 0, 0, 1]]), + ['s1', 's2', 's3', 's4'] + ['o1', 'o2', 'o3', 'o4']) + metadata = pd.DataFrame([['a', 'control'], + ['c', 'diseased'], + ['b', 'control']], + index=['s1', 's3', 's2'], + columns=['Barcode', 'Treatment']) + with self.assertRaises(TableException): + table.align_to_dataframe(metadata) + + @pytest.mark.skipif(not HAVE_SKBIO, reason="skbio not installed") + def test_align_tree_intersect_tips(self): + # there are less tree tips than observations + table = Table(np.array([[0, 0, 1, 1], + [2, 3, 4, 4], + [5, 5, 3, 3], + [0, 0, 0, 1]]).T, + ['a', 'b', 'c', 'd'], + ['s1', 's2', 's3', 's4']) + tree = skbio.TreeNode.read([u"((a,b)f,d)r;"]) + exp_table = Table(np.array([[0, 0, 1], + [2, 3, 4], + [5, 5, 3], + [0, 0, 1]]).T, + ['a', 'b', 'd'], + ['s1', 's2', 's3', 's4']) + exp_tree = tree + res_table, res_tree = table.align_tree(tree) + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + self.assertEqual(str(exp_tree), str(res_tree)) + + @pytest.mark.skipif(not HAVE_SKBIO, reason="skbio not installed") + def test_align_tree_intersect_obs(self): + # table has less observations than tree tips + table = Table(np.array([[0, 0, 1], + [2, 3, 4], + [5, 5, 3], + [0, 0, 1]]).T, + ['a', 'b', 'd'], + ['s1', 's2', 's3', 's4']) + tree = skbio.TreeNode.read([u"(((a,b)f, c),d)r;"]) + exp_table = Table(np.array([[1, 0, 0], + [4, 2, 3], + [3, 5, 5], + [1, 0, 0]]).T, + ['d', 'a', 'b'], + ['s1', 's2', 's3', 's4']) + exp_tree = skbio.TreeNode.read([u"(d,(a,b)f)r;"]) + res_table, res_tree = table.align_tree(tree) + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + self.assertEqual(str(exp_tree), str(res_tree)) + + @pytest.mark.skipif(not HAVE_SKBIO, reason="skbio not installed") + def test_align_tree_sample(self): + # table has less observations than tree tips + table = Table(np.array([[0, 0, 1], + [2, 3, 4], + [5, 5, 3], + [0, 0, 1]]), + ['s1', 's2', 's3', 's4'], + ['a', 'b', 'd']) + tree = skbio.TreeNode.read([u"(((a,b)f, c),d)r;"]) + exp_table = Table(np.array([[1, 0, 0], + [4, 2, 3], + [3, 5, 5], + [1, 0, 0]]), + ['s1', 's2', 's3', 's4'], + ['d', 'a', 'b']) + exp_tree = skbio.TreeNode.read([u"(d,(a,b)f)r;"]) + res_table, res_tree = table.align_tree(tree, axis='sample') + self.assertEqual(res_table.descriptive_equality(exp_table), + 'Tables appear equal') + self.assertEqual(str(exp_tree), str(res_tree)) + def test_get_value_by_ids(self): """Return the value located in the matrix by the ids""" t1 = Table(np.array([[5, 6], [7, 8]]), [3, 4], [1, 2]) From 40e54c0e5cfb685c1aca93a9e53085a1a4f6bd66 Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 21:59:23 -0600 Subject: [PATCH 04/17] add changelog --- ChangeLog.md | 71 +++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 18a55b7a..e626474c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,7 +6,10 @@ biom 2.1.11-dev Bug fixes: -* `Table.from_json` now respects the creation date [issue #770](https://github.com/biocore/biom-format/issues/770) in Python 3.7 and higher + * `Table.from_json` now respects the creation date [issue #770](https://github.com/biocore/biom-format/issues/770) in Python 3.7 and higher + +New Features +* Added support for aligning dataframes and trees against biom tables with `Table.align_to_dataframe` and `Table.align_tree`. see [PR #859](https://github.com/biocore/biom-format/pull/859) biom 2.1.10 ----------- @@ -33,7 +36,7 @@ New Features: * A much faster way to merge tables (without metadata) has been added. For large tables, this was a few minutes rather than a few hours. This method is implicitly invoked when calling `Table.merge` if unioning both axes, and the tables lack metadata. `Table.concat` is still much faster, but assumes one axis is disjoint. See [PR #848](https://github.com/biocore/biom-format/pull/848). * Simplify interaction with the concatenation method, allowing for passing in an individual table and support for a general `biom.concat(tables)` wrapper. See [PR #851](https://github.com/biocore/biom-format/pull/851). -* Added support for parsing adjacency table structures, see [issue #823](https://github.com/biocore/biom-format/issues/823). +* Added support for parsing adjacency table structures, see [issue #823](https://github.com/biocore/biom-format/issues/823). Bug fixes: @@ -47,7 +50,7 @@ New features and bug fixes, released on 28 January 2020. Important: * Python 2.7 and 3.5 support has been dropped. -* Python 3.8 support has been added into Travis CI. +* Python 3.8 support has been added into Travis CI. * A change to the defaults for `Table.nonzero_counts` was performed such that the default now is to count the number of nonzero features. See [issue #685](https://github.com/biocore/biom-format/issues/685) * We now require a SciPy >= 1.3.1. See [issue #816](https://github.com/biocore/biom-format/issues/816) @@ -403,42 +406,42 @@ Changes: * [pyqi](http://bipy.github.io/pyqi) 0.2.0 is now a required dependency. This changes the look-and-feel of the biom-format command-line interfaces and introduces a new executable, ```biom```, which can be used to see a list of all available biom-format command-line commands. The ```biom``` command is now used to run biom-format commands, instead of having a Python script (i.e., .py file) for each biom-format command. The old scripts (e.g., add_metadata.py, convert_biom.py, etc.) are still included but are deprecated. Users are pointed to the new ```biom``` command to run instead. Bash tab completion is now supported for all command and option names (see the biom-format documentation for instructions on how to enable this). * The following scripts have had their names and options changed: * ```add_metadata.py``` is now ```biom add-metadata```. Changed option names: - * ```--input_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp``` - * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` - * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` - * ```--sc_separated``` is now ```--sc-separated``` - * ```--int_fields``` is now ```--int-fields``` - * ```--float_fields``` is now ```--float-fields``` - * ```--sample_header``` is now ```--sample-header``` - * ```--observation_header``` is now ```--observation-header``` - * New option ```--sc-pipe-separated``` + * ```--input_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp``` + * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` + * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` + * ```--sc_separated``` is now ```--sc-separated``` + * ```--int_fields``` is now ```--int-fields``` + * ```--float_fields``` is now ```--float-fields``` + * ```--sample_header``` is now ```--sample-header``` + * ```--observation_header``` is now ```--observation-header``` + * New option ```--sc-pipe-separated``` * ```biom_validator.py``` is now ```biom validate-table```. Changed option names: - * ```-v```/```--verbose``` is now ```--detailed-report``` - * ```--biom_fp``` is now ```--input-fp``` + * ```-v```/```--verbose``` is now ```--detailed-report``` + * ```--biom_fp``` is now ```--input-fp``` * ```convert_biom.py``` is now ```biom convert```. Changed option names: - * ```--input_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp``` - * ```--biom_type``` is now ```--matrix-type``` - * ```--biom_to_classic_table``` is now ```--biom-to-classic-table``` - * ```--sparse_biom_to_dense_biom``` is now ```--sparse-biom-to-dense-biom``` - * ```--dense_biom_to_sparse_biom``` is now ```--dense-biom-to-sparse-biom``` - * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` - * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` - * ```--header_key``` is now ```--header-key``` - * ```--output_metadata_id``` is now ```--output-metadata-id``` - * ```--process_obs_metadata``` is now ```--process-obs-metadata``` - * ```--biom_table_type``` is now ```--table-type``` + * ```--input_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp``` + * ```--biom_type``` is now ```--matrix-type``` + * ```--biom_to_classic_table``` is now ```--biom-to-classic-table``` + * ```--sparse_biom_to_dense_biom``` is now ```--sparse-biom-to-dense-biom``` + * ```--dense_biom_to_sparse_biom``` is now ```--dense-biom-to-sparse-biom``` + * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` + * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` + * ```--header_key``` is now ```--header-key``` + * ```--output_metadata_id``` is now ```--output-metadata-id``` + * ```--process_obs_metadata``` is now ```--process-obs-metadata``` + * ```--biom_table_type``` is now ```--table-type``` * ```print_biom_python_config.py``` is now ```biom show-install-info```. * ```print_biom_table_summary.py``` is now ```biom summarize-table```. Changed option names: - * ```--input_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp```. This is now a required option (output is no longer printed to stdout). - * ```--num_observations``` is now ```--qualitative``` - * ```--suppress_md5``` is now ```--suppress-md5``` + * ```--input_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp```. This is now a required option (output is no longer printed to stdout). + * ```--num_observations``` is now ```--qualitative``` + * ```--suppress_md5``` is now ```--suppress-md5``` * ```subset_biom.py``` is now ```biom subset-table```. Changed option names: - * ```--biom_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp``` - * ```--ids_fp``` is now ```--ids``` + * ```--biom_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp``` + * ```--ids_fp``` is now ```--ids``` * ```biom.parse.parse_mapping``` has been replaced by ```biom.parse.MetadataMap```. ```biom.parse.MetadataMap.from_file``` can be directly substituted in place of ```biom.parse.parse_mapping```. Bug Fixes: From 97bd4e969fdcd381100f5c5302538627172903c4 Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 22:01:36 -0600 Subject: [PATCH 05/17] lint --- biom/table.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/biom/table.py b/biom/table.py index a9be5eee..2e74684b 100644 --- a/biom/table.py +++ b/biom/table.py @@ -969,7 +969,8 @@ def align_to_dataframe(self, metadata, axis='sample'): ids = set(self.ids(axis=axis)) & set(metadata.index) if len(ids) == 0: raise TableException("No common ids between table and dataframe.") - filter_f = lambda v, i, m: i in ids + def filter_f(v, i, m): + return i in ids t = self.filter(filter_f, axis=axis, inplace=False) t.remove_empty() md = metadata.loc[t.ids(axis=axis)] From 06476ddfc85aa9912875bfc312e847b9d9fc1c5c Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 22:02:39 -0600 Subject: [PATCH 06/17] more lint --- biom/table.py | 6 +++--- biom/tests/test_table.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/biom/table.py b/biom/table.py index 2e74684b..e2e54595 100644 --- a/biom/table.py +++ b/biom/table.py @@ -969,8 +969,9 @@ def align_to_dataframe(self, metadata, axis='sample'): ids = set(self.ids(axis=axis)) & set(metadata.index) if len(ids) == 0: raise TableException("No common ids between table and dataframe.") - def filter_f(v, i, m): - return i in ids + + def filter_f(v, i, m): return i in ids + t = self.filter(filter_f, axis=axis, inplace=False) t.remove_empty() md = metadata.loc[t.ids(axis=axis)] @@ -1003,7 +1004,6 @@ def align_tree(self, tree, axis='observation'): _table = _table.sort_order(order, axis=axis) return _table, _tree - def reduce(self, f, axis): """Reduce over axis using function `f` diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index 620a6785..6468dec7 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1978,8 +1978,8 @@ def test_align_tree_intersect_obs(self): ['s1', 's2', 's3', 's4']) tree = skbio.TreeNode.read([u"(((a,b)f, c),d)r;"]) exp_table = Table(np.array([[1, 0, 0], - [4, 2, 3], - [3, 5, 5], + [4, 2, 3], + [3, 5, 5], [1, 0, 0]]).T, ['d', 'a', 'b'], ['s1', 's2', 's3', 's4']) @@ -2000,8 +2000,8 @@ def test_align_tree_sample(self): ['a', 'b', 'd']) tree = skbio.TreeNode.read([u"(((a,b)f, c),d)r;"]) exp_table = Table(np.array([[1, 0, 0], - [4, 2, 3], - [3, 5, 5], + [4, 2, 3], + [3, 5, 5], [1, 0, 0]]), ['s1', 's2', 's3', 's4'], ['d', 'a', 'b']) From 820a00b4546b09b4faf94ef139c660fa66d5ec24 Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 22:05:23 -0600 Subject: [PATCH 07/17] adding remove empty to align tips --- biom/table.py | 1 + 1 file changed, 1 insertion(+) diff --git a/biom/table.py b/biom/table.py index e2e54595..6a7e9119 100644 --- a/biom/table.py +++ b/biom/table.py @@ -999,6 +999,7 @@ def align_tree(self, tree, axis='observation'): common_tips = tips & set(self.ids(axis=axis)) _tree = tree.shear(names=common_tips) _table = self.filter(common_tips, axis=axis, inplace=False) + _table.remove_empty() _tree.prune() order = [n.name for n in _tree.tips()] _table = _table.sort_order(order, axis=axis) From ba69bfb2e4a68c98bf8346bc1fefb5c2fe9e708e Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 22:09:37 -0600 Subject: [PATCH 08/17] found typo --- biom/tests/test_table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index 6468dec7..890625c2 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1935,7 +1935,7 @@ def test_align_to_dataframe_samples_no_common_ids(self): [2, 2, 4, 0], [5, 5, 3, 0], [0, 0, 0, 1]]), - ['s1', 's2', 's3', 's4'] + ['s1', 's2', 's3', 's4'], ['o1', 'o2', 'o3', 'o4']) metadata = pd.DataFrame([['a', 'control'], ['c', 'diseased'], From 5fc6eb3982b5856aa4103e65d9d61ac5e3d91b20 Mon Sep 17 00:00:00 2001 From: mortonjt Date: Mon, 26 Jul 2021 22:30:52 -0600 Subject: [PATCH 09/17] fixing changelog --- ChangeLog.md | 75 ++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index e626474c..46cea788 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,10 +6,7 @@ biom 2.1.11-dev Bug fixes: - * `Table.from_json` now respects the creation date [issue #770](https://github.com/biocore/biom-format/issues/770) in Python 3.7 and higher - -New Features -* Added support for aligning dataframes and trees against biom tables with `Table.align_to_dataframe` and `Table.align_tree`. see [PR #859](https://github.com/biocore/biom-format/pull/859) +* `Table.from_json` now respects the creation date [issue #770](https://github.com/biocore/biom-format/issues/770) in Python 3.7 and higher biom 2.1.10 ----------- @@ -20,6 +17,10 @@ Bug fixes: * During deployment testing for QIIME 2 2020.11, it was observed that certain combinations of hdf5 or h5py dependencies can result in metadata strings parsing as ASCII rather than UTF-8. Parse of BIOM-Format 2.1.0 files now normalize metadata strings as UTF-8, see [PR #853](https://github.com/biocore/biom-format/pull/853). +New Features + +* Added support for aligning dataframes and trees against biom tables with `Table.align_to_dataframe` and `Table.align_tree`. see [PR #859](https://github.com/biocore/biom-format/pull/859) + biom 2.1.9 ---------- @@ -36,7 +37,7 @@ New Features: * A much faster way to merge tables (without metadata) has been added. For large tables, this was a few minutes rather than a few hours. This method is implicitly invoked when calling `Table.merge` if unioning both axes, and the tables lack metadata. `Table.concat` is still much faster, but assumes one axis is disjoint. See [PR #848](https://github.com/biocore/biom-format/pull/848). * Simplify interaction with the concatenation method, allowing for passing in an individual table and support for a general `biom.concat(tables)` wrapper. See [PR #851](https://github.com/biocore/biom-format/pull/851). -* Added support for parsing adjacency table structures, see [issue #823](https://github.com/biocore/biom-format/issues/823). +* Added support for parsing adjacency table structures, see [issue #823](https://github.com/biocore/biom-format/issues/823). Bug fixes: @@ -50,7 +51,7 @@ New features and bug fixes, released on 28 January 2020. Important: * Python 2.7 and 3.5 support has been dropped. -* Python 3.8 support has been added into Travis CI. +* Python 3.8 support has been added into Travis CI. * A change to the defaults for `Table.nonzero_counts` was performed such that the default now is to count the number of nonzero features. See [issue #685](https://github.com/biocore/biom-format/issues/685) * We now require a SciPy >= 1.3.1. See [issue #816](https://github.com/biocore/biom-format/issues/816) @@ -406,42 +407,42 @@ Changes: * [pyqi](http://bipy.github.io/pyqi) 0.2.0 is now a required dependency. This changes the look-and-feel of the biom-format command-line interfaces and introduces a new executable, ```biom```, which can be used to see a list of all available biom-format command-line commands. The ```biom``` command is now used to run biom-format commands, instead of having a Python script (i.e., .py file) for each biom-format command. The old scripts (e.g., add_metadata.py, convert_biom.py, etc.) are still included but are deprecated. Users are pointed to the new ```biom``` command to run instead. Bash tab completion is now supported for all command and option names (see the biom-format documentation for instructions on how to enable this). * The following scripts have had their names and options changed: * ```add_metadata.py``` is now ```biom add-metadata```. Changed option names: - * ```--input_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp``` - * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` - * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` - * ```--sc_separated``` is now ```--sc-separated``` - * ```--int_fields``` is now ```--int-fields``` - * ```--float_fields``` is now ```--float-fields``` - * ```--sample_header``` is now ```--sample-header``` - * ```--observation_header``` is now ```--observation-header``` - * New option ```--sc-pipe-separated``` + * ```--input_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp``` + * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` + * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` + * ```--sc_separated``` is now ```--sc-separated``` + * ```--int_fields``` is now ```--int-fields``` + * ```--float_fields``` is now ```--float-fields``` + * ```--sample_header``` is now ```--sample-header``` + * ```--observation_header``` is now ```--observation-header``` + * New option ```--sc-pipe-separated``` * ```biom_validator.py``` is now ```biom validate-table```. Changed option names: - * ```-v```/```--verbose``` is now ```--detailed-report``` - * ```--biom_fp``` is now ```--input-fp``` + * ```-v```/```--verbose``` is now ```--detailed-report``` + * ```--biom_fp``` is now ```--input-fp``` * ```convert_biom.py``` is now ```biom convert```. Changed option names: - * ```--input_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp``` - * ```--biom_type``` is now ```--matrix-type``` - * ```--biom_to_classic_table``` is now ```--biom-to-classic-table``` - * ```--sparse_biom_to_dense_biom``` is now ```--sparse-biom-to-dense-biom``` - * ```--dense_biom_to_sparse_biom``` is now ```--dense-biom-to-sparse-biom``` - * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` - * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` - * ```--header_key``` is now ```--header-key``` - * ```--output_metadata_id``` is now ```--output-metadata-id``` - * ```--process_obs_metadata``` is now ```--process-obs-metadata``` - * ```--biom_table_type``` is now ```--table-type``` + * ```--input_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp``` + * ```--biom_type``` is now ```--matrix-type``` + * ```--biom_to_classic_table``` is now ```--biom-to-classic-table``` + * ```--sparse_biom_to_dense_biom``` is now ```--sparse-biom-to-dense-biom``` + * ```--dense_biom_to_sparse_biom``` is now ```--dense-biom-to-sparse-biom``` + * ```--sample_mapping_fp``` is now ```--sample-metadata-fp``` + * ```--observation_mapping_fp``` is now ```--observation-metadata-fp``` + * ```--header_key``` is now ```--header-key``` + * ```--output_metadata_id``` is now ```--output-metadata-id``` + * ```--process_obs_metadata``` is now ```--process-obs-metadata``` + * ```--biom_table_type``` is now ```--table-type``` * ```print_biom_python_config.py``` is now ```biom show-install-info```. * ```print_biom_table_summary.py``` is now ```biom summarize-table```. Changed option names: - * ```--input_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp```. This is now a required option (output is no longer printed to stdout). - * ```--num_observations``` is now ```--qualitative``` - * ```--suppress_md5``` is now ```--suppress-md5``` + * ```--input_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp```. This is now a required option (output is no longer printed to stdout). + * ```--num_observations``` is now ```--qualitative``` + * ```--suppress_md5``` is now ```--suppress-md5``` * ```subset_biom.py``` is now ```biom subset-table```. Changed option names: - * ```--biom_fp``` is now ```--input-fp``` - * ```--output_fp``` is now ```--output-fp``` - * ```--ids_fp``` is now ```--ids``` + * ```--biom_fp``` is now ```--input-fp``` + * ```--output_fp``` is now ```--output-fp``` + * ```--ids_fp``` is now ```--ids``` * ```biom.parse.parse_mapping``` has been replaced by ```biom.parse.MetadataMap```. ```biom.parse.MetadataMap.from_file``` can be directly substituted in place of ```biom.parse.parse_mapping```. Bug Fixes: From 616747aef263df3f0db2260fd1f10246ddf977d7 Mon Sep 17 00:00:00 2001 From: Jamie Morton Date: Tue, 27 Jul 2021 12:58:48 -0600 Subject: [PATCH 10/17] Update biom/table.py Co-authored-by: Daniel McDonald --- biom/table.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/biom/table.py b/biom/table.py index 6a7e9119..2825b011 100644 --- a/biom/table.py +++ b/biom/table.py @@ -970,9 +970,7 @@ def align_to_dataframe(self, metadata, axis='sample'): if len(ids) == 0: raise TableException("No common ids between table and dataframe.") - def filter_f(v, i, m): return i in ids - - t = self.filter(filter_f, axis=axis, inplace=False) + t = self.filter(ids, axis=axis, inplace=False) t.remove_empty() md = metadata.loc[t.ids(axis=axis)] return t, md From b4c5ca080891d58782abcc2641748e0914b2916f Mon Sep 17 00:00:00 2001 From: Jamie Morton Date: Tue, 27 Jul 2021 12:59:05 -0600 Subject: [PATCH 11/17] Update biom/table.py Co-authored-by: Daniel McDonald --- biom/table.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/biom/table.py b/biom/table.py index 2825b011..d8e751a7 100644 --- a/biom/table.py +++ b/biom/table.py @@ -995,6 +995,8 @@ def align_tree(self, tree, axis='observation'): """ tips = {x.name for x in tree.tips()} common_tips = tips & set(self.ids(axis=axis)) + if len(common_tips) == 0: + raise TableException("No common ids between table and tree.") _tree = tree.shear(names=common_tips) _table = self.filter(common_tips, axis=axis, inplace=False) _table.remove_empty() From d3320c437f6941ea8bd544d951ccf69320fe5ee0 Mon Sep 17 00:00:00 2001 From: Jamie Morton Date: Tue, 27 Jul 2021 12:59:12 -0600 Subject: [PATCH 12/17] Update biom/tests/test_table.py Co-authored-by: Daniel McDonald --- biom/tests/test_table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index 890625c2..b8916786 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -47,7 +47,7 @@ try: import skbio - HAVE_SKBIO = False + HAVE_SKBIO = True except ImportError: HAVE_SKBIO = False From c74d474e7b044fe0c09f98aac846a6d80909477f Mon Sep 17 00:00:00 2001 From: mortonjt Date: Tue, 27 Jul 2021 13:11:00 -0600 Subject: [PATCH 13/17] addressing comments. adding skbio to ci. adding rfd_distance. making sample ids readable --- .github/workflows/python-package-conda.yml | 18 +++++----- biom/tests/test_table.py | 40 +++++++++++----------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index c0cb58d1..65bdd0d1 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -1,10 +1,10 @@ name: biom-format CI -on: +on: push: branches: [ master ] pull_request: - + jobs: lint: runs-on: ubuntu-latest @@ -20,7 +20,7 @@ jobs: run: | pip install -q flake8 flake8 biom setup.py - + docs: runs-on: ubuntu-latest steps: @@ -28,21 +28,21 @@ jobs: - uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true - python-version: 3.7 + python-version: 3.7 - name: Install dependencies shell: bash -l {0} run: | conda create --yes -n env_name python=3.7 conda activate env_name - conda install --name env_name pip click numpy "scipy>=1.3.1" pep8 flake8 coverage future six "pandas>=0.20.0" nose h5py>=2.2.0 cython + conda install --name env_name pip click numpy "scipy>=1.3.1" pep8 flake8 coverage future six "pandas>=0.20.0" nose h5py>=2.2.0 cython scikit-bio pip install sphinx==1.2.2 "docutils<0.14" pip install -e . --no-deps - - name: Build docs + - name: Build docs shell: bash -l {0} run: | conda activate env_name make -C doc html - + build: strategy: matrix: @@ -63,13 +63,13 @@ jobs: conda activate env_name conda install -y pip click numpy "scipy>=1.3.1" pep8 flake8 coverage future six "pandas>=0.20.0" nose h5py>=2.2.0 cython pip install anndata - + - name: Tests shell: bash -l {0} run: | conda activate env_name conda install -y pytest - which python + which python pip install -e . --no-deps make test biom show-install-info diff --git a/biom/tests/test_table.py b/biom/tests/test_table.py index b8916786..30071772 100644 --- a/biom/tests/test_table.py +++ b/biom/tests/test_table.py @@ -1853,22 +1853,22 @@ def test_align_to_dataframe_observations(self): [2, 2, 4, 4], [5, 5, 3, 3], [0, 0, 0, 1]]), - ['s1', 's2', 's3', 's4'], - ['o1', 'o2', 'o3', 'o4']) - metadata = pd.DataFrame([['a', 'control'], - ['c', 'diseased'], - ['b', 'control']], - index=['s1', 's3', 's2'], + ['o1', 'o2', 'o3', 'o4'], + ['s1', 's2', 's3', 's4']) + metadata = pd.DataFrame([['a', 'Firmicutes'], + ['c', 'Proteobacteria'], + ['b', 'Firmicutes']], + index=['o1', 'o3', 'o2'], columns=['Barcode', 'Treatment']) exp_table = Table(np.array([[0, 0, 1, 1], [2, 2, 4, 4], [5, 5, 3, 3]]), - ['s1', 's2', 's3'], - ['o1', 'o2', 'o3', 'o4']) - exp_metadata = pd.DataFrame([['a', 'control'], - ['b', 'control'], - ['c', 'diseased']], - index=['s1', 's2', 's3'], + ['o1', 'o2', 'o3'], + ['s1', 's2', 's3', 's4']) + exp_metadata = pd.DataFrame([['a', 'Firmicutes'], + ['b', 'Firmicutes'], + ['c', 'Proteobacteria']], + index=['o1', 'o2', 'o3'], columns=['Barcode', 'Treatment']) res_table, res_metadata = table.align_to_dataframe( metadata, axis='observation') @@ -1987,7 +1987,7 @@ def test_align_tree_intersect_obs(self): res_table, res_tree = table.align_tree(tree) self.assertEqual(res_table.descriptive_equality(exp_table), 'Tables appear equal') - self.assertEqual(str(exp_tree), str(res_tree)) + self.assertEqual(exp_tree.compare_rfd(res_tree), 0) @pytest.mark.skipif(not HAVE_SKBIO, reason="skbio not installed") def test_align_tree_sample(self): @@ -1996,20 +1996,20 @@ def test_align_tree_sample(self): [2, 3, 4], [5, 5, 3], [0, 0, 1]]), - ['s1', 's2', 's3', 's4'], - ['a', 'b', 'd']) - tree = skbio.TreeNode.read([u"(((a,b)f, c),d)r;"]) + ['o1', 'o2', 'o3', 'o4'], + ['s1', 's2', 's4']) + tree = skbio.TreeNode.read([u"(((s1,s2)F, s3),s4)R;"]) exp_table = Table(np.array([[1, 0, 0], [4, 2, 3], [3, 5, 5], [1, 0, 0]]), - ['s1', 's2', 's3', 's4'], - ['d', 'a', 'b']) - exp_tree = skbio.TreeNode.read([u"(d,(a,b)f)r;"]) + ['o1', 'o2', 'o3', 'o4'], + ['s4', 's1', 's2']) + exp_tree = skbio.TreeNode.read([u"(s4,(s1,s2)F)R;"]) res_table, res_tree = table.align_tree(tree, axis='sample') self.assertEqual(res_table.descriptive_equality(exp_table), 'Tables appear equal') - self.assertEqual(str(exp_tree), str(res_tree)) + self.assertEqual(exp_tree.compare_rfd(res_tree), 0) def test_get_value_by_ids(self): """Return the value located in the matrix by the ids""" From dfd9a51d0ca5b6211a31c0552783da9cbf27dac9 Mon Sep 17 00:00:00 2001 From: mortonjt Date: Tue, 27 Jul 2021 18:17:33 -0600 Subject: [PATCH 14/17] getting that other skbio import --- .github/workflows/python-package-conda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 65bdd0d1..dbe3932d 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -61,7 +61,7 @@ jobs: run: | conda create --yes -n env_name python=${{ matrix.python-version }} conda activate env_name - conda install -y pip click numpy "scipy>=1.3.1" pep8 flake8 coverage future six "pandas>=0.20.0" nose h5py>=2.2.0 cython + conda install -y pip click numpy "scipy>=1.3.1" pep8 flake8 coverage future six "pandas>=0.20.0" nose h5py>=2.2.0 cython scikit-bio pip install anndata - name: Tests From b56b771cba53e3d467d9c7d23f1c43d954fb78a6 Mon Sep 17 00:00:00 2001 From: Daniel McDonald Date: Tue, 27 Jul 2021 17:47:59 -0700 Subject: [PATCH 15/17] Temporarily limit to py3.6 --- .github/workflows/python-package-conda.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index dbe3932d..bf370e99 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -28,11 +28,11 @@ jobs: - uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true - python-version: 3.7 + python-version: 3.6 - name: Install dependencies shell: bash -l {0} run: | - conda create --yes -n env_name python=3.7 + conda create --yes -n env_name python=3.6 conda activate env_name conda install --name env_name pip click numpy "scipy>=1.3.1" pep8 flake8 coverage future six "pandas>=0.20.0" nose h5py>=2.2.0 cython scikit-bio pip install sphinx==1.2.2 "docutils<0.14" @@ -46,7 +46,7 @@ jobs: build: strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.6, ] # 3.7, 3.8] os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} From a435527dd649c23b3759abdb2f87ce5066994dcc Mon Sep 17 00:00:00 2001 From: mortonjt Date: Tue, 27 Jul 2021 21:51:30 -0600 Subject: [PATCH 16/17] adding doctests --- biom/table.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/biom/table.py b/biom/table.py index d8e751a7..00754da7 100644 --- a/biom/table.py +++ b/biom/table.py @@ -965,6 +965,35 @@ def align_to_dataframe(self, metadata, axis='sample'): A filtered biom table. pd.DataFrame A filtered metadata table. + + Examples + -------- + >>> from biom import Table + >>> import numpy as np + >>> import pandas as pd + >>> table = Table(np.array([[0, 0, 1, 1], + ... [2, 2, 4, 4], + ... [5, 5, 3, 3], + ... [0, 0, 0, 1]]), + ... ['o1', 'o2', 'o3', 'o4'], + ... ['s1', 's2', 's3', 's4']) + >>> metadata = pd.DataFrame([['a', 'control'], + ... ['c', 'diseased'], + ... ['b', 'control']], + ... index=['s1', 's3', 's2'], + ... columns=['Barcode', 'Treatment']) + >>> res_table, res_metadata = table.align_to_dataframe(metadata) + >>> print(res_table) + # Constructed from biom file + #OTU ID s1 s2 s3 + o1 0.0 0.0 1.0 + o2 2.0 2.0 4.0 + o3 5.0 5.0 3.0 + >>> print(res_metadata) + Barcode Treatment + s1 a control + s2 b control + s3 c diseased """ ids = set(self.ids(axis=axis)) & set(metadata.index) if len(ids) == 0: @@ -992,6 +1021,32 @@ def align_tree(self, tree, axis='observation'): A filtered biom table. skbio.TreeNode A filtered skbio TreeNode object. + + Examples + -------- + >>> from biom import Table + >>> import numpy as np + >>> from skbio import TreeNode + >>> table = Table(np.array([[0, 0, 1, 1], + ... [2, 2, 4, 4], + ... [5, 5, 3, 3], + ... [0, 0, 0, 1]]), + ... ['o1', 'o2', 'o3', 'o4'], + ... ['s1', 's2', 's3', 's4']) + >>> tree = TreeNode.read([u"((o1,o2)f,o3)r;"]) + >>> res_table, res_tree = table.align_tree(tree) + >>> print(res_table) + # Constructed from biom file + #OTU ID s1 s2 s3 s4 + o1 0.0 0.0 1.0 1.0 + o2 2.0 2.0 4.0 4.0 + o3 5.0 5.0 3.0 3.0 + >>> print(res_tree.ascii_art()) + /-o1 + /f-------| + -r-------| \-o2 + | + \-o3 """ tips = {x.name for x in tree.tips()} common_tips = tips & set(self.ids(axis=axis)) From 34341c3fccb7d1b580959cdbf3e873ef8e2c9ccd Mon Sep 17 00:00:00 2001 From: mortonjt Date: Tue, 27 Jul 2021 21:54:10 -0600 Subject: [PATCH 17/17] oops flake8 --- biom/table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biom/table.py b/biom/table.py index 00754da7..49da56b2 100644 --- a/biom/table.py +++ b/biom/table.py @@ -1005,7 +1005,7 @@ def align_to_dataframe(self, metadata, axis='sample'): return t, md def align_tree(self, tree, axis='observation'): - """ Aligns biom table against tree, only keeping common ids. + r""" Aligns biom table against tree, only keeping common ids. Parameters ----------