11"""Tools for working with reports."""
2- from typing import Type , Dict , TypeVar , Iterable
2+ from typing import Type , Dict , TypeVar , Iterable , Any , Set
33from abc import abstractmethod
44from itertools import groupby
55from logging import getLogger
99from citrine ._serialization .serializable import Serializable
1010from citrine ._rest .asynchronous_object import AsynchronousObject
1111from citrine .informatics .descriptors import Descriptor
12+ from citrine .informatics .predictor_evaluation_result import ResponseMetrics
1213
1314SelfType = TypeVar ('SelfType' , bound = 'Report' )
1415
@@ -62,6 +63,58 @@ def __str__(self):
6263 return "<FeatureImportanceReport {!r}>" .format (self .output_key ) # pragma: no cover
6364
6465
66+ class ModelEvaluationResult (Serializable ["ModelEvaluationResult" ]):
67+ """[ALPHA] Settings and evaluation metrics for a single algorithm from AutoML model selection.
68+
69+ ModelEvaluationResult objects are included in a ModelSelectionReport
70+ and should not be user-instantiated.
71+ """
72+
73+ model_settings = properties .Raw ('model_settings' )
74+ _response_results = properties .Mapping (
75+ properties .String ,
76+ properties .Object (ResponseMetrics ),
77+ "response_results"
78+ )
79+
80+ def __init__ (self ):
81+ pass # pragma: no cover
82+
83+ def __str__ (self ):
84+ return '<ModelEvaluationResult>' # pragma: no cover
85+
86+ def __getitem__ (self , item ):
87+ return self ._response_results [item ]
88+
89+ def __iter__ (self ):
90+ return iter (self .responses )
91+
92+ @property
93+ def responses (self ) -> Set [str ]:
94+ """Responses the model was evaluated on."""
95+ return set (self ._response_results .keys ())
96+
97+
98+ class ModelSelectionReport (Serializable ["ModelSelectionReport" ]):
99+ """[ALPHA] Summary of selection settings and model performance from AutoML model selection.
100+
101+ ModelSelectionReport objects are constructed from saved models and
102+ should not be user-instantiated.
103+ """
104+
105+ n_folds = properties .Integer ('n_folds' )
106+ evaluation_results = properties .List (
107+ properties .Object (ModelEvaluationResult ),
108+ "evaluation_results"
109+ )
110+
111+ def __init__ (self ):
112+ pass # pragma: no cover
113+
114+ def __str__ (self ):
115+ return '<ModelSelectionReport>' # pragma: no cover
116+
117+
65118class ModelSummary (Serializable ['ModelSummary' ]):
66119 """[ALPHA] Summary of information about a single model in a predictor.
67120
@@ -87,6 +140,10 @@ class ModelSummary(Serializable['ModelSummary']):
87140 feature_importances = properties .List (
88141 properties .Object (FeatureImportanceReport ), 'feature_importances' )
89142 """:List[FeatureImportanceReport]: feature importance reports for each output"""
143+ selection_summary = properties .Optional (
144+ properties .Object (ModelSelectionReport ), "selection_summary"
145+ )
146+ """:Optional[ModelSelectionReport]: optional results of AutoML model selection"""
90147 predictor_name = properties .String ('predictor_configuration_name' , default = '' )
91148 """:str: the name of the predictor that created this model"""
92149 predictor_uid = properties .Optional (properties .UUID (), 'predictor_configuration_uid' )
@@ -126,8 +183,13 @@ def __init__(self):
126183 def post_build (self ):
127184 """Modify a PredictorReport object in-place after deserialization."""
128185 self ._fill_out_descriptors ()
129- for _ , model in enumerate (self .model_summaries ):
130- self ._collapse_model_settings (model )
186+ for _ , summary in enumerate (self .model_summaries ):
187+ # Collapse settings on final trained model
188+ summary .model_settings = self ._collapse_model_settings (summary .model_settings )
189+ if summary .selection_summary is not None :
190+ # Collapse settings on any child model evaluation results
191+ for result in summary .selection_summary .evaluation_results :
192+ result .model_settings = self ._collapse_model_settings (result .model_settings )
131193
132194 def _fill_out_descriptors (self ):
133195 """Replace descriptor keys in `model_summaries` with full Descriptor objects."""
@@ -146,7 +208,7 @@ def _fill_out_descriptors(self):
146208 model .outputs [j ] = descriptor_map [output_key ]
147209 except KeyError :
148210 raise RuntimeError ("Model {} contains output \' {}\' , but no descriptor found "
149- "with that key" .format (model .name , input_key ))
211+ "with that key" .format (model .name , output_key ))
150212
151213 @staticmethod
152214 def _get_sole_descriptor (it : Iterable ):
@@ -170,7 +232,7 @@ def _get_sole_descriptor(it: Iterable):
170232 return as_list [0 ]
171233
172234 @staticmethod
173- def _collapse_model_settings (model : ModelSummary ):
235+ def _collapse_model_settings (raw_settings : Dict [ str , Any ] ):
174236 """Collapse a model's settings into a flat dictionary.
175237
176238 Model settings are returned as a dictionary with a "name" field, a "value" field,
@@ -187,6 +249,6 @@ def _recurse_model_settings(settings: Dict[str, str], list_or_dict):
187249 settings [list_or_dict ['name' ]] = list_or_dict ['value' ]
188250 _recurse_model_settings (settings , list_or_dict ['children' ])
189251
190- settings = dict ()
191- _recurse_model_settings (settings , model . model_settings )
192- model . model_settings = settings
252+ collapsed = dict ()
253+ _recurse_model_settings (collapsed , raw_settings )
254+ return collapsed
0 commit comments