Skip to content

Commit

Permalink
Merge 0abcbf4 into 5c74fb1
Browse files Browse the repository at this point in the history
  • Loading branch information
slobodan-ilic committed Oct 16, 2018
2 parents 5c74fb1 + 0abcbf4 commit 3f66add
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 10 deletions.
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# History of Changes

#### 1.6.10 Fix README on pypi

#### 1.6.9 Bugfix
- When Categorical Array variable is selected in multitable export, and Scale Means is selected, the cube fails, because it tries to access the non-existing slice (the CA is only _interpreted_ as multiple slices in tabbooks). This fix makes sure that the export cube doesn't fail in such case.

Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ The detailed description can be found

## Changes

#### 1.7.1 Fix index error
- Fix peculiar case of CA x CAT (single elem) index error
- Support with unit tests
- Create base for fixing exporter issues

#### 1.7.0 Normalization, PEP8 and bugfix
- Refactored to remove a couple modules
- Fixed pesky numpy warnings
Expand All @@ -106,6 +111,4 @@ The detailed description can be found
- Use `get_shape(prune=False)` instead
- Will be removed in future versions

#### 1.6.10 Fix README on pypi

For a complete list of changes see [history](https://github.com/Crunch-io/crunch-cube/blob/master/HISTORY.md).
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from setuptools import setup, find_packages

version = '1.7.0'
version = '1.7.1'


def get_long_desc():
Expand Down
27 changes: 20 additions & 7 deletions src/cr/cube/measures/scale_means.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,22 @@ def data(self):
continue

# Calculate means
num = np.sum(product[self.valid_indices(axis)], axis)
den = np.sum(table[self.valid_indices(axis)], axis)
num = np.sum(product[self._valid_indices(axis)], axis)
den = np.sum(table[self._valid_indices(axis)], axis)
mean = num / den
if not isinstance(mean, np.ndarray):
mean = np.array([mean])
means.append(mean)
return means

def margin(self, axis):
'''Return marginal value of the current slice scaled means.
This value is the the same what you would get from a single variable
(constituting a 2D cube/slice), when the "non-missing" filter of the
opposite variable would be applied. This behavior is consistent with
what is visible in the front-end client.
'''
if self._slice.ndim < 2:
msg = (
'Scale Means marginal cannot be calculated on 1D cubes, as'
Expand All @@ -67,16 +74,16 @@ def margin(self, axis):

return np.sum(values * margin) / total

def valid_indices(self, axis):
def _valid_indices(self, axis):
# --there is an interaction with CrunchCube._fix_shape() which
# --essentially eliminates length-1 dimensions not in the first
# --position. We must mirror that reshaping here. The fact this logic
# --needs to be duplicated indicates we're missing an abstraction
# --somewhere, like perhaps CrunchCube and/or
# --CrunchSlice.reshaped_dimensions.
reshaped_dimensions = [
dim for (idx, dim) in enumerate(self._slice.dimensions)
if len(dim.elements()) != 1 or idx == 0
dimension for dimension in self._slice.dimensions
if len(dimension.values) > 1
]

return tuple(
Expand Down Expand Up @@ -104,14 +111,20 @@ def values(self):
for dim in self._slice.dimensions
]

def _inflate(self, dim_ind):
return (
self._slice.ndim > 1 and
len(self._slice.get_shape()) > 1 and
not dim_ind
)

def _inner_prods(self, contents, values):
products = []
for i, numeric in enumerate(values):
if numeric is None:
products.append(numeric)
continue
inflate = self._slice.ndim > 1 and not i
numeric = numeric[:, None] if inflate else numeric
numeric = numeric[:, None] if self._inflate(i) else numeric
product = contents * numeric
products.append(product)
return products
48 changes: 48 additions & 0 deletions tests/unit/test_scale_means.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# pylint: disable=missing-docstring, redefined-outer-name, protected-access
from __future__ import division
from mock import Mock
import numpy as np
Expand All @@ -22,6 +23,11 @@ def test_scale_means_marginal(scale_means_fixture):
assert scale_means.margin(axis) == expected


def test_inflate(inflate_fixture):
dim_ind, scale_means, expected = inflate_fixture
assert scale_means._inflate(dim_ind) == expected


@pytest.fixture(params=[
(
[None, ROWS_DIM_VALUES], margin, 0, 2,
Expand Down Expand Up @@ -52,3 +58,45 @@ def scale_means_fixture(request, values_prop_):
@pytest.fixture
def values_prop_(request):
return property_mock(request, ScaleMeans, 'values')


@pytest.fixture(params=[
(0, 2, (Mock(), Mock()), True),
(1, 2, (Mock(), Mock()), False),
(0, 2, (Mock(),), False),
(0, 1, (Mock(),), False),
])
def inflate_fixture(request):
dim_ind, ndim, shape, expected = request.param
slice_ = Mock()
slice_.ndim = ndim
slice_.get_shape.return_value = shape
scale_means = ScaleMeans(slice_)
return dim_ind, scale_means, expected


def test_valid_indices(valid_indices_fixture):
scale_means, axis, expected = valid_indices_fixture
actual = scale_means._valid_indices(axis)
np.testing.assert_equal(actual, expected)


@pytest.fixture(params=[
([Mock(values=[])], 0, []),
([Mock(values=[1, 2, 3])], 0, [np.array([True, True, True])]),
(
[Mock(values=[1, 2, np.nan, 4])], 0,
[np.array([True, True, False, True])],
),
([Mock(values=[1])], 0, []),
(
[Mock(values=[1, 2, 3]), Mock(values=[])], 0,
[np.array([True, True, True])],
),
([Mock(values=[1, 2, 3]), Mock(values=[])], 1, [slice(None)]),
])
def valid_indices_fixture(request):
dimensions, axis, expected = request.param
slice_ = Mock()
slice_.dimensions = dimensions
return ScaleMeans(slice_), axis, expected

0 comments on commit 3f66add

Please sign in to comment.