def test_parse_eligibilities(self): event = { "classifier_set": ["a", "?"], "age_eligibility": [], "eligibility": [1], "target": [1.0, 1, "A", "1", "dist1", "1", 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() e = self.assemble_disturbance_events_table([event]) sit_events = sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), None, separate_eligibilities=True) elgibilities_input = pd.DataFrame( columns=["id", "pool_filter", "state_filter"], data=[[1, "pool_expression_1", "state_expression_1"]]) sit_eligibilities = sit_disturbance_event_parser.parse_eligibilities( sit_events, elgibilities_input) self.assertTrue( list(sit_eligibilities.columns) == [ x["name"] for x in sit_format.get_disturbance_eligibility_format() ])
def test_undefined_sort_type_error(self): """check if an error is raised when an invalid sort type is specified """ event = { "classifier_set": ["a", "?"], "age_eligibility": ["True", -1, -1, -1, -1], "eligibility": [-1] * self.get_num_eligibility_cols(), "target": [1.0, "UNDEFINED", "A", 100, "dist1", 2, 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() with self.assertRaises(ValueError): e = self.assemble_disturbance_events_table([event]) sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), self.get_mock_age_classes())
def test_incorrect_number_of_classifiers_error(self): """checks that the format has the correct number of columns according to the defined classifiers """ event = { "classifier_set": ["a", "?", "EXTRA"], "age_eligibility": ["False", -1, -1, -1, -1], "eligibility": [-1] * self.get_num_eligibility_cols(), "target": [1.0, "1", "A", 100, "dist1", 2, 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() with self.assertRaises(ValueError): e = self.assemble_disturbance_events_table([event]) sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), self.get_mock_age_classes())
def test_undefined_age_class_error(self): """check that an error is raised if the using_age_class is set to true and any of the age class ids are not valid """ event = { "classifier_set": ["a", "?"], "age_eligibility": ["True", -1, -1, -1, "UNDEFINED"], "eligibility": [-1] * self.get_num_eligibility_cols(), "target": [1.0, "1", "A", 100, "dist1", 2, 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() with self.assertRaises(ValueError): e = self.assemble_disturbance_events_table([event]) sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), self.get_mock_age_classes())
def test_differing_hw_sw_age_criteria_error(self): """check that an error is raised if hw age and sw age criteria differ (CBM only has stand age) """ event = { "classifier_set": ["a", "?"], "age_eligibility": ["False", -1, 10, -1, 20], "eligibility": [-1] * self.get_num_eligibility_cols(), "target": [1.0, "1", "A", 100, "dist1", 2, 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() with self.assertRaises(ValueError): e = self.assemble_disturbance_events_table([event]) sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), self.get_mock_age_classes())
def test_undefined_classifier_value_error(self): """checks that the format has values that are either wildcards or classifier sets drawn from the set of defined classifiers values and aggregates """ event = { "classifier_set": ["UNDEFINED", "?"], "age_eligibility": ["False", -1, -1, -1, -1], "eligibility": [-1] * self.get_num_eligibility_cols(), "target": [1.0, "1", "A", 100, "dist1", 2, 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() with self.assertRaises(ValueError): e = self.assemble_disturbance_events_table([event]) sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), self.get_mock_age_classes())
def test_expected_value_with_numeric_classifier_values(self): """Checks that numeric classifiers that appear in events data are parsed as strings """ event = { "classifier_set": [1, 2.0], "age_eligibility": ["False", -1, -1, -1, -1], "eligibility": [-1] * self.get_num_eligibility_cols(), "target": [1.0, "1", "A", 100, "dist1", 2, 100] } classifiers = pd.DataFrame(data=[(1, "classifier1"), (2, "classifier2")], columns=["id", "name"]) classifier_values = pd.DataFrame( data=[(1, "1", "a"), (1, "b", "b"), (2, "a", "a")], columns=["classifier_id", "name", "description"]) aggregates = [{ 'classifier_id': 1, 'name': 'agg1', 'description': 'agg2', 'classifier_values': ['a', 'b'] }, { 'classifier_id': 1, 'name': 'agg2', 'description': 'agg2', 'classifier_values': ['a', 'b'] }, { 'classifier_id': 2, 'name': '2.0', 'description': 'agg1', 'classifier_values': ['a'] }] e = self.assemble_disturbance_events_table([event]) result = sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), self.get_mock_age_classes()) self.assertTrue(list(result.classifier1) == ["1"]) self.assertTrue(list(result.classifier2) == ["2.0"])
def test_parse_eligibilities_error_on_missing_id(self): event = { "classifier_set": ["a", "?"], "age_eligibility": [], "eligibility": [2], # missing "target": [1.0, 1, "A", "1", "dist1", "1", 100] } classifiers, classifier_values, aggregates = \ self.get_mock_classifiers() e = self.assemble_disturbance_events_table([event]) sit_events = sit_disturbance_event_parser.parse( e, classifiers, classifier_values, aggregates, self.get_mock_disturbance_types(), None, separate_eligibilities=True) elgibilities_input = pd.DataFrame( columns=["id", "pool_filter", "state_filter"], data=[[1, "", ""]]) with self.assertRaises(ValueError): sit_disturbance_event_parser.parse_eligibilities( sit_events, elgibilities_input)
def parse(sit_classifiers, sit_disturbance_types, sit_age_classes, sit_inventory, sit_yield, sit_events=None, sit_transitions=None, sit_eligibilities=None): """Parses and validates CBM Standard import tool formatted data including the complicated interdependencies in the SIT format. Returns an object containing the validated result. The returned object has the following properties: - classifiers: a pandas.DataFrame of classifiers in the sit_classifiers input - classifier_values: a pandas.DataFrame of the classifier values in the sit_classifiers input - classifier_aggregates: a dictionary of the classifier aggregates in the sit_classifiers input - disturbance_types: a pandas.DataFrame based on the disturbance types in the sit_disturbance_types input - age_classes: a pandas.DataFrame of the age classes based on sit_age_classes - inventory: a pandas.DataFrame of the inventory based on sit_inventory - yield_table: a pandas.DataFrame of the merchantable volume yield curves in the sit_yield input - disturbance_events: a pandas.DataFrame of the disturbance events based on sit_events. If the sit_events parameter is None this field is None. - transition_rules: a pandas.DataFrame of the transition rules based on sit_transitions. If the sit_transitions parameter is None this field is None. - disturbance_eligibilities: a pandas.DataFrame of the disturbance event eligibilities based on sit_eligibilities. If the sit_events parameter is None this field is None. Args: sit_classifiers (pandas.DataFrame): SIT formatted classifiers sit_disturbance_types (pandas.DataFrame): SIT formatted disturbance types sit_age_classes (pandas.DataFrame): SIT formatted age classes sit_inventory (pandas.DataFrame): SIT formatted inventory sit_yield (pandas.DataFrame): SIT formatted yield curves sit_events (pandas.DataFrame, optional): SIT formatted disturbance events sit_transitions (pandas.DataFrame, optional): SIT formatted transition rules. Defaults to None. sit_eligibilities (pandas.DataFrame, optional): SIT formatted disturbance eligibilities. Defaults to None. Returns: object: an object containing parsed and validated SIT dataset """ s = SimpleNamespace() classifiers, classifier_values, classifier_aggregates = \ sit_classifier_parser.parse(sit_classifiers) s.classifiers = classifiers s.classifier_values = classifier_values s.classifier_aggregates = classifier_aggregates s.disturbance_types = sit_disturbance_type_parser.parse( sit_disturbance_types) s.age_classes = sit_age_class_parser.parse(sit_age_classes) s.inventory = sit_inventory_parser.parse(sit_inventory, classifiers, classifier_values, s.disturbance_types, s.age_classes) s.yield_table = sit_yield_parser.parse(sit_yield, s.classifiers, s.classifier_values, s.age_classes) if sit_events is not None: separate_eligibilities = False if sit_eligibilities is not None: separate_eligibilities = True s.disturbance_events = sit_disturbance_event_parser.parse( sit_events, s.classifiers, s.classifier_values, s.classifier_aggregates, s.disturbance_types, s.age_classes, separate_eligibilities) if sit_eligibilities is not None: s.disturbance_eligibilities = \ sit_disturbance_event_parser.parse_eligibilities( s.disturbance_events, sit_eligibilities) else: s.disturbance_eligibilities = None else: s.disturbance_events = None s.disturbance_eligibilities = None if sit_transitions is not None: s.transition_rules = sit_transition_rule_parser.parse( sit_transitions, s.classifiers, s.classifier_values, s.classifier_aggregates, s.disturbance_types, s.age_classes) else: s.transition_rules = None return s
def create_sit_rule_based_processor( sit, cbm, random_func=np.random.rand, reset_parameters=True, sit_events=None, sit_disturbance_eligibilities=None, sit_transition_rules=None, event_sort: EventSort = EventSort.disturbance_type): """initializes a class for processing SIT rule based disturbances. Args: sit (object): sit instance as returned by :py:func:`load_sit` cbm (object): initialized instance of the CBM model random_func (func, optional): A function of a single integer that returns a numeric 1d array whose length is the integer argument. Defaults to np.random.rand. reset_parameters (bool): if set to true, cbm_vars.parameters.disturbance_type and cbm_vars.parameters.reset_age will be reset prior to computing new disturbances and transition rules. sit_events (pandas.DataFrame, optional): if specified the returned rule base processor is based on the specified sit_events input. The value will be parsed and validated (sit_classifiers, sit_disturbance_type etc.) based on the values in the specified sit object. sit_disturbance_eligibilities (pandas.DataFrame, optional): SIT formatted disturbance eligibilities. Cannot be specified without also specified sit_events using the disturbance-eligibility formatting. Defaults to None. sit_transition_rules (pandas.DataFrame, optional): if specified the returned rule base processor is based on the specified sit_transition_rules input. The value will be parsed and validated (sit_classifiers, sit_disturbance_type etc.) based on the values in the specified sit object. Note if the sit_disturbance_events parameter is set, but this parameter is not set the transition_rules (if any) attached to the specified sit object will be used by default. If null transition rules are required with non-null sit_events set this parameter to a dataframe with zero rows `pandas.DataFrame()`. Defaults to None. event_sort (EventSort): one of the EventSort values, which determines the order in which the supplied sit_events are applied within a given timestep. Raises: ValueError: cannot specify sit_disturbance_eligibilities with no specified sit_events Returns: SITRuleBasedProcessor: an object for processing SIT rule based disturbances """ separate_eligibilities = sit_disturbance_eligibilities is not None disturbance_events = None disturbance_eligibilities = None transition_rules = None if sit_events is not None: disturbance_events = sit_disturbance_event_parser.parse( sit_events, sit.sit_data.classifiers, sit.sit_data.classifier_values, sit.sit_data.classifier_aggregates, sit.sit_data.disturbance_types, sit.sit_data.age_classes, separate_eligibilities=separate_eligibilities) if sit_disturbance_eligibilities is not None: disturbance_eligibilities = \ sit_disturbance_event_parser.parse_eligibilities( sit_events, sit_disturbance_eligibilities) else: disturbance_events = sit.sit_data.disturbance_events disturbance_eligibilities = sit.sit_data.disturbance_eligibilities if separate_eligibilities: raise ValueError( "cannot specify sit_disturbance_eligibilities with no " "specified sit_events") if sit_transition_rules is not None: if len(sit_transition_rules.index) == 0: transition_rules = None else: transition_rules = sit_transition_rule_parser.parse( sit_transition_rules, sit.sit_data.classifiers, sit.sit_data.classifier_values, sit.sit_data.classifier_aggregates, sit.sit_data.disturbance_types, sit.sit_data.age_classes) else: transition_rules = sit.sit_data.transition_rules classifiers_config = get_classifiers(sit.sit_data.classifiers, sit.sit_data.classifier_values) tr_constants = SimpleNamespace( group_err_max=sit_transition_rule_parser.GROUPED_PERCENT_ERR_MAX, classifier_value_postfix=sit_format.get_tr_classifier_set_postfix(), wildcard=sit_classifier_parser.get_wildcard_keyword()) return sit_rule_based_processor.sit_rule_based_processor_factory( cbm=cbm, random_func=random_func, classifiers_config=classifiers_config, classifier_aggregates=sit.sit_data.classifier_aggregates, sit_events=_initialize_events(disturbance_events, sit.sit_mapping, sit.sit_data.disturbance_types, event_sort), sit_transitions=_initialize_transition_rules(transition_rules, sit.sit_mapping), tr_constants=tr_constants, sit_disturbance_eligibilities=disturbance_eligibilities, reset_parameters=reset_parameters)