def dimension_subset(hyperparameters, dimensions): """Return only the values of `hyperparameters` specified by `dimensions`, in the same order as `dimensions` Parameters ---------- hyperparameters: Dict Dict of hyperparameters containing at least the following keys: ['model_init_params', 'model_extra_params', 'feature_engineer', 'feature_selector'] dimensions: List of: (strings, or tuples) Locations and order of the values to return from `hyperparameters`. If a value is a string, it is assumed to belong to `model_init_params`, and its path will be adjusted accordingly Returns ------- List of hyperparameter values""" dimensions = [("model_init_params", _) if isinstance(_, str) else _ for _ in dimensions] values = [ get_path(hyperparameters, _, default=RejectedOptional()) for _ in dimensions ] return values
def validate_fe_steps(candidate: Union[list, FeatureEngineer], template: Union[list, FeatureEngineer]) -> list: """Check `candidate` "feature_engineer" `steps` compatibility with `template` and sanitize `candidate` Parameters ---------- candidate: List, or FeatureEngineer Candidate "feature_engineer" `steps` to compare to `template`. If compatible with `template`, a sanitized version of `candidate` will be returned (described below) template: List, or FeatureEngineer Template "feature_engineer" `steps` to which `candidate` will be compared. `template` is also used to sanitize `candidate` (described below) Returns ------- List If `candidate` is compatible with `template`, returns a list resembling `candidate`, with the following changes: 1) all step dicts in `candidate` are reinitialized to proper `EngineerStep` instances; and 2) wherever `candidate` was missing a step that was tagged as `optional` in `template`, `RejectedOptional` is added. In the end, if a list is returned, it is built from `candidate`, guaranteed to be the same length as `template` and guaranteed to contain only `EngineerStep` and `RejectedOptional` instances Raises ------ IncompatibleCandidateError If `candidate` is incompatible with `template`. `candidate` may be incompatible with `template` for any of the following reasons: 1. `candidate` has more steps than `template` 2. `candidate` has a step that differs from a concrete (non-`Categorical`) `template` step 2. `candidate` has a step that differs from a concrete (non-`Categorical`) `template` step 3. `candidate` has a step that does not fit in a `Categorical` `template` step 4. `candidate` is missing a concrete step in `template` 5. `candidate` is missing a non-`optional` `Categorical` step in `template`""" # Extract `steps` if given `FeatureEngineer` if isinstance(candidate, FeatureEngineer): candidate = candidate.steps if isinstance(template, FeatureEngineer): template = template.steps if len(template) == 0: if len(candidate) == 0: # All steps have been exhausted and passed, so `candidate` was a match return [] # `candidate` steps remain while `template` is empty, so `candidate` is incompatible raise IncompatibleCandidateError(candidate, template) #################### Categorical Template Step #################### if isinstance(template[-1], Categorical): #################### Optional Categorical Template Step #################### if template[-1].optional is True: if len(candidate) == 0 or (candidate[-1] not in template[-1]): # `candidate` is either empty or doesn't match an `optional` template step candidate.append(RejectedOptional()) #################### Non-Optional Categorical Template Step #################### elif len(candidate) == 0: # `candidate` is empty while non-`optional` `template` steps remain - Incompatible raise IncompatibleCandidateError(candidate, template) elif candidate[-1] not in template[-1]: raise IncompatibleCandidateError(candidate, template) elif len(candidate) == 0: raise IncompatibleCandidateError(candidate, template) #################### Concrete Template Step #################### elif candidate[-1] != template[-1]: raise IncompatibleCandidateError(candidate, template) #################### Reinitialize EngineerStep Dict #################### if isinstance(candidate[-1], dict): if isinstance(template[-1], Categorical): candidate[-1] = EngineerStep.honorary_step_from_dict( candidate[-1], template[-1]) elif isinstance(template[-1], EngineerStep) and template[-1] == candidate[-1]: candidate[-1] = template[ -1] # Adopt template value if `EngineerStep` equivalent return validate_fe_steps(candidate[:-1], template[:-1]) + [candidate[-1]]
), pytest.param( [es_a, es_c, es_e], [CAT(es_a, es_b), CAT(es_c, es_d), es_e], [es_a, es_c, es_e], id="cat+cat+concrete", ), pytest.param( [es_a, es_c, es_e], [es_a, CAT(es_b, es_c), CAT(es_d, es_e)], [es_a, es_c, es_e], id="concrete+cat+cat", ), #################### Exclusively Optional Scenarios #################### pytest.param([es_a], [CAT(es_a, ...)], [es_a], id="opt_single_0"), pytest.param([], [CAT(es_a, ...)], [RejectedOptional()], id="opt_single_1"), pytest.param( [], [CAT(es_a, ...), CAT(es_b, ...), CAT(es_c, ...)], [RejectedOptional(), RejectedOptional(), RejectedOptional()], id="opt+opt+opt_0", ), pytest.param( [es_a], [CAT(es_a, ...), CAT(es_b, ...), CAT(es_c, ...)], [es_a, RejectedOptional(),
def test_rejected_optional_repr(): assert "{!r}".format(RejectedOptional()) == "RejectedOptional()"