Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate memoize from dimension.py #142

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/cr/cube/crunch_cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ def hs_dims_for_den(hs_dims, axis):
if (
d.dimension_type == DT.MR_CAT
or i != 0
and (n <= 1 or len(d.elements()) <= 1)
and (n <= 1 or len(d.valid_elements) <= 1)
)
else slice(None)
)
Expand Down Expand Up @@ -831,7 +831,12 @@ def _apply_missings_and_insertions(
# --element idxs that satisfy `include_missing` arg. Note this
# --includes MR_CAT elements so is essentially all-or-valid-elements
element_idxs = tuple(
d.element_indices(include_missing) for d in self._all_dimensions
(
d.all_elements.element_idxs
if include_missing
else d.valid_elements.element_idxs
)
for d in self._all_dimensions
)
if not include_transforms_for_dims:
return res[np.ix_(*element_idxs)] if element_idxs else res
Expand Down Expand Up @@ -990,7 +995,7 @@ def _drop_mr_cat_dims(self, array, fix_valids=False):
if not fix_valids
else np.ix_(
*[
dim.element_indices(include_missing=False) if n > 1 else [0]
dim.valid_elements.element_idxs if n > 1 else [0]
for dim, n in zip(self._all_dimensions, array.shape)
]
)
Expand Down
60 changes: 21 additions & 39 deletions src/cr/cube/dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy as np

from cr.cube.enum import DIMENSION_TYPE as DT
from cr.cube.util import lazyproperty, memoize
from cr.cube.util import lazyproperty


class _BaseDimensions(Sequence):
Expand Down Expand Up @@ -266,24 +266,6 @@ def dimension_type(self):
"""Member of DIMENSION_TYPE appropriate to this cube dimension."""
return self._dimension_type

@memoize
def element_indices(self, include_missing):
"""Return tuple of int element idxs for this dimension.

*include_missing* determines whether missing elements are included or
only valid element index values are returned.
"""
return (
self._all_elements.element_idxs
if include_missing
else self._valid_elements.element_idxs
)

@memoize
def elements(self, include_missing=False):
"""_Elements object providing access to elements of this dimension."""
return self._all_elements if include_missing else self._valid_elements

@lazyproperty
def has_transforms(self):
"""True if there are subtotals on this dimension, False otherwise."""
Expand Down Expand Up @@ -327,7 +309,7 @@ def inserted_hs_indices(self):
return [
idx
for idx, item in enumerate(
self._iter_interleaved_items(self._valid_elements)
self._iter_interleaved_items(self.valid_elements)
)
if item.is_insertion
]
Expand All @@ -351,7 +333,7 @@ def labels(
# that effectively squashes what should be two methods into one.
# Either get rid of the need for that alternate return value type or
# create a separate method for it.
elements = self._all_elements if include_missing else self._valid_elements
elements = self.all_elements if include_missing else self.valid_elements

include_subtotals = include_transforms and self.dimension_type != DT.CA_SUBVAR

Expand Down Expand Up @@ -395,14 +377,14 @@ def numeric_values(self):
a value, but an element with no numeric value appears as `np.nan` in
the returned list.
"""
return tuple(element.numeric_value for element in self._valid_elements)
return tuple(element.numeric_value for element in self.valid_elements)

@lazyproperty
def shape(self):
return len(self._all_elements)
return len(self.all_elements)

@lazyproperty
def _all_elements(self):
def all_elements(self):
"""_AllElements object providing cats or subvars of this dimension."""
return _AllElements(self._dimension_dict["type"])

Expand All @@ -416,7 +398,7 @@ def _iter_interleaved_items(self, elements):

Only elements in the passed *elements* collection appear, which
allows control over whether missing elements are included by choosing
`._all_elements` or `._valid_elements`.
`.all_elements` or `.valid_elements`.
"""
subtotals = self._subtotals

Expand All @@ -443,17 +425,17 @@ def _subtotals(self):
insertion_dicts = (
[] if view is None else view.get("transform", {}).get("insertions", [])
)
return _Subtotals(insertion_dicts, self._valid_elements)
return _Subtotals(insertion_dicts, self.valid_elements)

@lazyproperty
def _valid_elements(self):
def valid_elements(self):
"""_Elements object providing access to non-missing elements.

Any categories or subvariables representing missing data are excluded
from the collection; this sequence represents a subset of that
provided by `._all_elements`.
provided by `.all_elements`.
"""
return self._all_elements.valid_elements
return self.all_elements.valid_elements


class _BaseElements(Sequence):
Expand Down Expand Up @@ -558,12 +540,12 @@ class _ValidElements(_BaseElements):
"""

def __init__(self, all_elements):
self._all_elements = all_elements
self.all_elements = all_elements

@lazyproperty
def _elements(self):
"""tuple containing actual sequence of element objects."""
return tuple(element for element in self._all_elements if not element.missing)
return tuple(element for element in self.all_elements if not element.missing)


class _BaseElement(object):
Expand Down Expand Up @@ -667,7 +649,7 @@ class _Subtotals(Sequence):

def __init__(self, insertion_dicts, valid_elements):
self._insertion_dicts = insertion_dicts
self._valid_elements = valid_elements
self.valid_elements = valid_elements

def __getitem__(self, idx_or_slice):
"""Implements indexed access."""
Expand All @@ -688,7 +670,7 @@ def iter_for_anchor(self, anchor):
@lazyproperty
def _element_ids(self):
"""frozenset of int id of each non-missing cat or subvar in dim."""
return frozenset(self._valid_elements.element_ids)
return frozenset(self.valid_elements.element_ids)

def _iter_valid_subtotal_dicts(self):
"""Generate each insertion dict that represents a valid subtotal."""
Expand Down Expand Up @@ -717,7 +699,7 @@ def _iter_valid_subtotal_dicts(self):
def _subtotals(self):
"""Composed tuple storing actual sequence of _Subtotal objects."""
return tuple(
_Subtotal(subtotal_dict, self._valid_elements)
_Subtotal(subtotal_dict, self.valid_elements)
for subtotal_dict in self._iter_valid_subtotal_dicts()
)

Expand All @@ -727,7 +709,7 @@ class _Subtotal(object):

def __init__(self, subtotal_dict, valid_elements):
self._subtotal_dict = subtotal_dict
self._valid_elements = valid_elements
self.valid_elements = valid_elements

@lazyproperty
def anchor(self):
Expand All @@ -744,7 +726,7 @@ def anchor(self):
anchor = self._subtotal_dict["anchor"]
try:
anchor = int(anchor)
if anchor not in self._valid_elements.element_ids:
if anchor not in self.valid_elements.element_ids:
return "bottom"
return anchor
except (TypeError, ValueError):
Expand All @@ -759,7 +741,7 @@ def anchor_idx(self):
anchor = self.anchor
if anchor in ["top", "bottom"]:
return anchor
return self._valid_elements.get_by_id(anchor).index
return self.valid_elements.get_by_id(anchor).index

@lazyproperty
def addend_ids(self):
Expand All @@ -771,7 +753,7 @@ def addend_ids(self):
return tuple(
arg
for arg in self._subtotal_dict.get("args", [])
if arg in self._valid_elements.element_ids
if arg in self.valid_elements.element_ids
)

@lazyproperty
Expand All @@ -783,7 +765,7 @@ def addend_idxs(self):
rather than its element id.
"""
return tuple(
self._valid_elements.get_by_id(addend_id).index
self.valid_elements.get_by_id(addend_id).index
for addend_id in self.addend_ids
)

Expand Down
2 changes: 1 addition & 1 deletion src/cr/cube/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def __set__(self, obj, value):
raise AttributeError("can't set attribute")


def lru_cache(maxsize=100):
def lru_cache(maxsize=100): # pragma: no cover
"""Least-recently-used cache decorator.

Arguments to the cached function must be hashable.
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ def valid_elements_(self, request):

@pytest.fixture
def _valid_elements_prop_(self, request):
return property_mock(request, Dimension, "_valid_elements")
return property_mock(request, Dimension, "valid_elements")


class Describe_BaseElements(object):
Expand Down