From 301214cf10783e60b48adc42002040f8710d04b3 Mon Sep 17 00:00:00 2001 From: Michael Malecki Date: Sun, 17 Jun 2018 14:35:12 -0400 Subject: [PATCH] make mr_selections aware they have an items dim --- src/cr/cube/crunch_cube.py | 5 +++-- src/cr/cube/dimension.py | 14 ++++++++++++++ tests/integration/fixtures/__init__.py | 2 ++ .../fixtures/cubes/logical-univariate.json | 1 + .../fixtures/cubes/logical-x-cat.json | 1 + tests/integration/test_dimension.py | 18 ++++++++++++++++++ 6 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/integration/fixtures/cubes/logical-univariate.json create mode 100644 tests/integration/fixtures/cubes/logical-x-cat.json diff --git a/src/cr/cube/crunch_cube.py b/src/cr/cube/crunch_cube.py index 5b0afccf0..a997ff0b6 100644 --- a/src/cr/cube/crunch_cube.py +++ b/src/cr/cube/crunch_cube.py @@ -107,7 +107,7 @@ def _fix_shape(self, array): # selections dimension (and subsequently purge the dimension itself). display_ind = [ - 0 if dim.is_selections else slice(None) + 0 if dim.is_mr_selections(self.all_dimensions) else slice(None) for dim in self.all_dimensions ] array = array[display_ind] @@ -309,6 +309,7 @@ def _prune_indices(self, transforms): row_indices = self._margin_pruned_indices( row_margin, self._inserted_dim_inds(transforms, 0), 0 ) + if row_indices.ndim > 1: # In case of MR, we'd have 2D prune indices row_indices = row_indices.all(axis=1) @@ -909,7 +910,7 @@ def hs_dims_for_den(hs_dims, axis): ) if isinstance(arr, np.ma.core.MaskedArray): inflate_ind = [ - None if d.is_selections else slice(None) + None if d.is_mr_selections(self.all_dimensions) else slice(None) for i, d in enumerate(self.all_dimensions) ] mask = np.logical_or( diff --git a/src/cr/cube/dimension.py b/src/cr/cube/dimension.py index becf542c8..7e09f12a7 100644 --- a/src/cr/cube/dimension.py +++ b/src/cr/cube/dimension.py @@ -307,3 +307,17 @@ def is_selections(self): if category_ids == [1, 0, -1]: return True return False + + def is_mr_selections(self, others): + '''Check whether a selections dimension has a corresponding items dim + + Sometimes selections are used to in conjunction with another dim, + that knows where to find them (following it). Other times, they behave + as a normal categorical dimension. This checks against the aliases of + all other dims to see which is the case. + ''' + if self.is_selections: + for dim in others: + if dim.alias == self.alias and not dim.is_selections: + return True + return False diff --git a/tests/integration/fixtures/__init__.py b/tests/integration/fixtures/__init__.py index 8705ca607..f756a8135 100644 --- a/tests/integration/fixtures/__init__.py +++ b/tests/integration/fixtures/__init__.py @@ -22,6 +22,7 @@ def _load(cube_file): # Bivariate Cubes CAT_X_CAT = _load('cat-x-cat.json') CAT_X_DATETIME = _load('cat-x-datetime.json') +LOGICAL_X_CAT = _load('logical-x-cat.json') # Univariate Cubes UNIVARIATE_CATEGORICAL = _load('univariate-categorical.json') @@ -29,6 +30,7 @@ def _load(cube_file): SIMPLE_DATETIME = _load('simple-datetime.json') SIMPLE_TEXT = _load('simple-text.json') SIMPLE_CAT_ARRAY = _load('simple-cat-array.json') +LOGICAL_UNIVARIATE = _load('logical-univariate.json') # Various other Cubes CAT_X_NUM_X_DATETIME = _load('cat-x-num-x-datetime.json') diff --git a/tests/integration/fixtures/cubes/logical-univariate.json b/tests/integration/fixtures/cubes/logical-univariate.json new file mode 100644 index 000000000..eed686d23 --- /dev/null +++ b/tests/integration/fixtures/cubes/logical-univariate.json @@ -0,0 +1 @@ +{"element": "shoji:view", "self": "/api/datasets/123/cube/?filter=%5B%5D&query=%7B%22dimensions%22:%5B%7B%22variable%22:%22/api/datasets/123%2Fvariables%2Fd2350ebd19384952856371f3ebfdded5%2F%22%7D%5D,%22measures%22:%7B%22count%22:%7B%22function%22:%22cube_count%22,%22args%22:%5B%5D%7D%7D,%22weight%22:%22/api/datasets/123%2Fvariables%2F000057%2F%22%7D", "value": {"query": {"measures": {"count": {"function": "cube_count", "args": []}}, "dimensions": [{"variable": "/api/datasets/123/variables/d2350ebd19384952856371f3ebfdded5/"}], "weight": "/api/datasets/123/variables/000057/"}, "query_environment": {"filter": []}, "result": {"dimensions": [{"derived": true, "references": {"alias": "bar", "name": "bar"}, "type": {"ordinal": false, "class": "categorical", "categories": [{"numeric_value": 1, "selected": true, "id": 1, "missing": false, "name": "Selected"}, {"numeric_value": 0, "missing": false, "id": 0, "name": "Other"}, {"numeric_value": null, "missing": true, "id": -1, "name": "No Data"}]}}], "missing": 15, "measures": {"count": {"data": [653.5771596093624, 815.5660185795266, 30.856821811109807], "n_missing": 15, "metadata": {"references": {}, "derived": true, "type": {"integer": false, "missing_rules": {}, "missing_reasons": {"No Data": -1}, "class": "numeric"}}}}, "element": "crunch:cube", "counts": [734, 751, 15], "n": 1500}}} diff --git a/tests/integration/fixtures/cubes/logical-x-cat.json b/tests/integration/fixtures/cubes/logical-x-cat.json new file mode 100644 index 000000000..4823bf986 --- /dev/null +++ b/tests/integration/fixtures/cubes/logical-x-cat.json @@ -0,0 +1 @@ +{"element": "shoji:view", "self": "/api/datasets/123/cube/?filter=%5B%5D&query=%7B%22dimensions%22:%5B%7B%22variable%22:%22/api/datasets/123%2Fvariables%2F000026%2F%22%7D,%7B%22variable%22:%22/api/datasets/123%2Fvariables%2F5a2969d0f6d440bfb42d3bb4540eee27%2F%22%7D%5D,%22measures%22:%7B%22count%22:%7B%22function%22:%22cube_count%22,%22args%22:%5B%5D%7D%7D,%22weight%22:null%7D", "value": {"query": {"measures": {"count": {"function": "cube_count", "args": []}}, "dimensions": [{"variable": "/api/datasets/123/variables/000026/"}, {"variable": "/api/datasets/123/variables/5a2969d0f6d440bfb42d3bb4540eee27/"}], "weight": null}, "query_environment": {"filter": []}, "result": {"dimensions": [{"references": {"alias": "pasta", "notes": "A categorical variable", "name": "Shapes of pasta", "description": "The geometry of pasta"}, "derived": false, "type": {"ordinal": false, "class": "categorical", "categories": [{"numeric_value": 1, "missing": false, "id": 1, "name": "Bucatini"}, {"numeric_value": 2, "missing": false, "id": 2, "name": "Chitarra"}, {"numeric_value": 0, "missing": false, "id": 0, "name": "Boccoli"}, {"numeric_value": 4, "missing": false, "id": 4, "name": "Orecchiette"}, {"numeric_value": 5, "missing": false, "id": 5, "name": "Quadrefiore"}, {"numeric_value": 6, "missing": false, "id": 6, "name": "Fileja"}, {"numeric_value": 32766, "missing": true, "id": 32766, "name": "Skipped"}, {"numeric_value": 32767, "missing": true, "id": 32767, "name": "Not asked"}, {"numeric_value": null, "missing": true, "id": -1, "name": "No Data"}]}}, {"references": {"alias": "long pastas", "api_derivation": null, "name": "long pastas"}, "derived": true, "type": {"ordinal": false, "class": "categorical", "categories": [{"numeric_value": 1, "selected": true, "id": 1, "name": "Selected", "missing": false}, {"numeric_value": 0, "id": 0, "name": "Other", "missing": false}, {"numeric_value": null, "id": -1, "name": "No Data", "missing": true}]}}], "missing": 4, "measures": {"count": {"data": [55, 0, 0, 126, 0, 0, 0, 616, 0, 0, 310, 0, 0, 401, 0, 0, 150, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0], "n_missing": 4, "metadata": {"references": {}, "derived": true, "type": {"integer": true, "missing_rules": {}, "missing_reasons": {"No Data": -1}, "class": "numeric"}}}}, "element": "crunch:cube", "counts": [55, 0, 0, 126, 0, 0, 0, 616, 0, 0, 310, 0, 0, 401, 0, 0, 150, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0], "n": 1662}}} diff --git a/tests/integration/test_dimension.py b/tests/integration/test_dimension.py index 92c31d490..af214e96b 100644 --- a/tests/integration/test_dimension.py +++ b/tests/integration/test_dimension.py @@ -4,6 +4,7 @@ from .fixtures import ECON_BLAME_WITH_HS_MISSING from .fixtures import ECON_BLAME_X_IDEOLOGY_ROW_HS from .fixtures import CA_WITH_NETS +from .fixtures import LOGICAL_UNIVARIATE, LOGICAL_X_CAT from cr.cube.crunch_cube import CrunchCube @@ -73,3 +74,20 @@ def test_hs_indices_with_bad_data(self): cat_dim = cube.dimensions[1] actual = [entry['anchor_ind'] for entry in cat_dim.hs_indices] assert actual == expected + + def test_logical_univariate_dim(self): + cube = CrunchCube(LOGICAL_UNIVARIATE) + dimension = cube.dimensions[0] + expected = 'categorical' + actual = dimension.type + self.assertEqual(expected, actual) + self.assertFalse(dimension.is_mr_selections(cube.all_dimensions)) + + def test_logical_x_cat_dims(self): + cube = CrunchCube(LOGICAL_X_CAT) + logical_dim = cube.dimensions[1] + self.assertEqual(cube.dimensions[0].type, 'categorical') + self.assertEqual(logical_dim.type, 'categorical') + + self.assertTrue(logical_dim.is_selections) + self.assertFalse(logical_dim.is_mr_selections(cube.all_dimensions))