@@ -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
0 commit comments