diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b9f171b..2b484ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,7 @@ Changelog Version 3.1.3 ------------- +- Add a RegionMap conversion to Pandas DataFrame (#6) - Add a function to compute the spatial intersection of a line-segment with a VoxelData object (#11) Version 3.1.1 diff --git a/tests/test_region_map.py b/tests/test_region_map.py index 25fa858..9025da4 100644 --- a/tests/test_region_map.py +++ b/tests/test_region_map.py @@ -7,27 +7,33 @@ from voxcell.exceptions import VoxcellError TEST_RMAP = test_module.RegionMap.from_dict({ - 'id': 1, - 'name': 'A', - 'fullname': 'aA', + 'id': -1, + 'name': 'root', + 'fullname': 'The Root Node', 'children': [ { - 'id': 2, - 'name': 'B', - 'fullname': 'Bb', - }, - { - 'id': 3, - 'name': 'C', - 'fullname': 'cC', - 'children': [ - { - 'id': 4, - 'name': 'B', - 'fullname': 'bB', - } - ] - } + 'id': 1, + 'name': 'A', + 'fullname': 'aA', + 'children': [ + { + 'id': 2, + 'name': 'B', + 'fullname': 'Bb', + }, + { + 'id': 3, + 'name': 'C', + 'fullname': 'cC', + 'children': [ + { + 'id': 4, + 'name': 'B', + 'fullname': 'bB', + } + ] + } + ]} ] }) @@ -77,7 +83,7 @@ def test_get_basic(): def test_get_with_ascendants(): - assert TEST_RMAP.get(4, 'name', with_ascendants=True) == ['B', 'C', 'A'] + assert TEST_RMAP.get(4, 'name', with_ascendants=True) == ['B', 'C', 'A', 'root'] def test_get_missing_attribute(): @@ -160,3 +166,15 @@ def test_is_leaf_id(): def test_is_leaf_id_non_existing_id(): with pytest.raises(VoxcellError): TEST_RMAP.is_leaf_id(0) # non-existing id + + +def test_as_datafram(): + df = TEST_RMAP.as_dataframe() + assert df.loc[-1].parent_id == -1 + assert df.loc[1].parent_id == -1 + assert df.loc[2].parent_id == 1 + assert df.loc[3].parent_id == 1 + assert df.loc[4].parent_id == 3 + + assert df.loc[1]['name'] == 'A' + assert df.loc[1]['fullname'] == 'aA' diff --git a/voxcell/region_map.py b/voxcell/region_map.py index 7fb2c0d..ade1cd3 100644 --- a/voxcell/region_map.py +++ b/voxcell/region_map.py @@ -5,6 +5,8 @@ import logging import re +import pandas as pd + from voxcell.exceptions import VoxcellError L = logging.getLogger(__name__) @@ -125,6 +127,20 @@ def is_leaf_id(self, _id): raise VoxcellError(f"Region ID not found: {_id}") return not self._children[_id] + def as_dataframe(self): + """Converts a region_map to a dataframe. + + Returns: + pd.DataFrame with an index of the id of the node, + and columns based on the data within the map, and a parent_id + + Note: the 'root' node should have a parent value of -1 + """ + ret = pd.DataFrame.from_dict(self._data, orient='index').set_index('id') + parents = {k: v if v is not None else -1 for k, v in self._parent.items()} + ret['parent_id'] = pd.DataFrame.from_dict(parents, orient='index') + return ret + def _get(self, _id, attr): """Fetch attribute value for a given region ID.""" if _id not in self._data: