Skip to content

Commit e6b8479

Browse files
Short-circuit metric grouping until algorithm can be improved.
1 parent be12881 commit e6b8479

File tree

2 files changed

+83
-68
lines changed

2 files changed

+83
-68
lines changed

mensor/metrics/registry.py

Lines changed: 83 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,16 @@ def evaluate(self, metrics, segment_by=None, where=None, **opts):
7070
return pd.concat([result.raw for result in results], axis=1)
7171

7272
def get_ir(self, metrics, segment_by=None, where=None, dry_run=False, **opts):
73+
metrics = [] if metrics is None else metrics
74+
segment_by = [] if segment_by is None else segment_by
75+
76+
if not isinstance(metrics, list):
77+
metrics = [metrics]
78+
if not isinstance(segment_by, list):
79+
segment_by = [segment_by]
80+
7381
for strategy, required_marginal_segmentation, metrics in self._group_metric_evaluations(metrics=metrics, segment_by=segment_by, where=where):
74-
return metrics[0].get_ir(strategy, required_marginal_segmentation, compatible_metrics=metrics[1:])
82+
yield metrics, metrics[0].get_ir(strategy, required_marginal_segmentation, compatible_metrics=metrics[1:], **opts)
7583

7684
def _get_strategy_for_metric(self, metric, segment_by, where):
7785
measures = metric.required_measures
@@ -99,69 +107,77 @@ def _group_metric_evaluations(self, metrics, segment_by, where, **opts):
99107
metrics = [self._metrics[metric] if not isinstance(metric, Metric) else metric for metric in metrics]
100108
strategies = {metric: self._get_strategy_for_metric(metric, segment_by, where, **opts) for metric in metrics}
101109

102-
def is_compatible(metric1, metric2):
103-
strategy1 = strategies[metric1]
104-
strategy2 = strategies[metric2]
105-
106-
for field in ['unit_type', 'segment_by', 'where']:
107-
if getattr(strategy1, field) != getattr(strategy2, field):
108-
return False
109-
110-
implementation1 = metric1.implementation_for_strategy(strategy1)
111-
implementation2 = metric2.implementation_for_strategy(strategy2)
112-
113-
return implementation1._is_compatible_with_metric(implementation2)
114-
115-
def strategy_for_metrics(metrics):
116-
unit_type = None
117-
measures = []
118-
segment_by = None
119-
where = None
120-
121-
for metric in metrics:
122-
if unit_type is None:
123-
unit_type = strategies[metric].unit_type
124-
else:
125-
assert unit_type == strategies[metric].unit_type
126-
127-
for measure in strategies[metric].measures:
128-
if measure not in measures:
129-
measures.append(measure)
130-
131-
if segment_by is None:
132-
segment_by = list(strategies[metric].segment_by)
133-
else:
134-
assert set(segment_by) == set(list(strategies[metric].segment_by))
135-
136-
if where is None:
137-
where = strategies[metric].where
138-
else:
139-
assert where == strategies[metric].where
140-
141-
return self.measures.get_strategy(
142-
unit_type=unit_type,
143-
measures=measures,
144-
segment_by=segment_by,
145-
where=where
146-
)
147-
148-
if segment_by is None:
149-
segment_by = []
150-
if not isinstance(segment_by, list):
151-
segment_by = [segment_by]
152-
153-
offset = 0
154-
while len(metrics) > 0:
155-
metric = metrics.pop(0)
156-
compatible = [metric]
157-
158-
for i, other in enumerate(metrics[:]):
159-
if is_compatible(metric, other):
160-
compatible.append(other)
161-
metrics.pop(i + offset)
162-
offset -= 1
163-
164-
strategy = strategy_for_metrics(compatible)
165-
required_marginal_segmentation = set(strategy.segment_by).difference(segment_by) # Todo: check case when required_dimensions is not empty
166-
167-
yield strategy_for_metrics(compatible), required_marginal_segmentation, compatible
110+
for metric in metrics:
111+
strategy = strategies[metric]
112+
required_marginal_segmentation = set(strategy.segment_by).difference(segment_by)
113+
yield strategy, required_marginal_segmentation, [metric]
114+
115+
# TODO: Generalise grouping correctly. The following is incorrect due to
116+
# ignoring where constraints in nested joins.
117+
#
118+
# def is_compatible(metric1, metric2):
119+
# strategy1 = strategies[metric1]
120+
# strategy2 = strategies[metric2]
121+
#
122+
# for field in ['unit_type', 'segment_by']:
123+
# if getattr(strategy1, field) != getattr(strategy2, field):
124+
# return False
125+
#
126+
# implementation1 = metric1.implementation_for_strategy(strategy1)
127+
# implementation2 = metric2.implementation_for_strategy(strategy2)
128+
#
129+
# return implementation1._is_compatible_with_metric(implementation2)
130+
#
131+
# def strategy_for_metrics(metrics):
132+
# unit_type = None
133+
# measures = []
134+
# segment_by = None
135+
# where = None
136+
#
137+
# for metric in metrics:
138+
# if unit_type is None:
139+
# unit_type = strategies[metric].unit_type
140+
# else:
141+
# assert unit_type == strategies[metric].unit_type
142+
#
143+
# for measure in strategies[metric].measures:
144+
# if measure not in measures:
145+
# measures.append(measure)
146+
#
147+
# if segment_by is None:
148+
# segment_by = list(strategies[metric].segment_by)
149+
# else:
150+
# assert set(segment_by) == set(list(strategies[metric].segment_by))
151+
#
152+
# if where is None:
153+
# where = strategies[metric].where
154+
# else:
155+
# assert where == strategies[metric].where
156+
#
157+
# return self.measures.get_strategy(
158+
# unit_type=unit_type,
159+
# measures=measures,
160+
# segment_by=segment_by,
161+
# where=where
162+
# )
163+
#
164+
# if segment_by is None:
165+
# segment_by = []
166+
# if not isinstance(segment_by, list):
167+
# segment_by = [segment_by]
168+
#
169+
# offset = 0
170+
# while len(metrics) > 0:
171+
# metric = metrics.pop(0)
172+
# compatible = [metric]
173+
#
174+
# for i, other in enumerate(metrics[:]):
175+
# if is_compatible(metric, other):
176+
# compatible.append(other)
177+
# metrics.pop(i + offset)
178+
# offset -= 1
179+
#
180+
# strategy = strategy_for_metrics(compatible)
181+
# required_marginal_segmentation = set(strategy.segment_by).difference(segment_by) # Todo: check case when required_dimensions is not empty
182+
#
183+
# yield strategy_for_metrics(compatible), required_marginal_segmentation, compatible

mensor/utils/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ def __setitem__(self, name, value):
200200
raise KeyError
201201

202202

203-
204203
class OptionsMixin(object):
205204

206205
@property

0 commit comments

Comments
 (0)