Example #1
0
    def __init__(self, wxtree='high_resolution'):
        """
        Define a decision tree for determining weather symbols based upon
        the input diagnostics. Use this decision tree to allocate a weather
        symbol to each point.

        Key Args:
            wxtree (str):
                Used to choose weather symbol decision tree.
                Default is 'high_resolution'
                'global' will load the global weather symbol decision tree.

        float_tolerance defines the tolerance when matching thresholds to allow
        for the difficulty of float comparisons.
        float_abs_tolerance defines the tolerance for when the threshold
        is zero. It has to be sufficiently small that a valid rainfall rate
        or snowfall rate could not trigger it.
        """
        self.wxtree = wxtree
        if wxtree == 'global':
            self.queries = wxcode_decision_tree_global()
            self.start_node = START_NODE_GLOBAL
        else:
            self.queries = wxcode_decision_tree()
            self.start_node = START_NODE
        self.float_tolerance = 0.01
        self.float_abs_tolerance = 1e-12
        # flag to indicate whether to expect "threshold" as a coordinate name
        # (defaults to False, checked on reading input cubes)
        self.coord_named_threshold = False
        # dictionary to contain names of threshold coordinates that do not
        # match expected convention
        self.threshold_coord_names = {}
Example #2
0
 def test_keywords(self):
     """Test that the only permissible keywords are used."""
     tree = wxcode_decision_tree()
     all_key_words = REQUIRED_KEY_WORDS + OPTIONAL_KEY_WORDS
     for node in tree:
         for entry in tree[node]:
             self.assertEqual(entry in all_key_words, True)
Example #3
0
    def __init__(self, wxtree='high_resolution'):
        """
        Define a decision tree for determining weather symbols based upon
        the input diagnostics. Use this decision tree to allocate a weather
        symbol to each point.

        Key Args:
            wxtree (str):
                Choose weather symbol decision tree.
                Default is 'high_resolution'
                'global' will load the global weather symbol decision tree.

        float_tolerance defines the tolerance when matching thresholds to allow
        for the difficulty of float comparisons.
        """
        self.wxtree = wxtree
        if wxtree == 'global':
            self.queries = wxcode_decision_tree_global()
        else:
            self.queries = wxcode_decision_tree()
        self.float_tolerance = 0.01
        # flag to indicate whether to expect "threshold" as a coordinate name
        # (defaults to False, checked on reading input cubes)
        self.coord_named_threshold = False
        # dictionary to contain names of threshold coordinates that do not
        # match expected convention
        self.threshold_coord_names = {}
Example #4
0
 def test_keywords_diagnostic_missing(self):
     """Test only set keywords are used in diagnostic_missing_action."""
     tree = wxcode_decision_tree()
     all_key_words = KEYWORDS_DIAGNOSTIC_MISSING_ACTION
     for items in tree.values():
         if 'diagnostic_missing_action' in items:
             entry = items['diagnostic_missing_action']
             self.assertEqual(entry in all_key_words, True)
Example #5
0
 def test_condition_combination(self):
     """Test only permissible values are used in condition_combination."""
     tree = wxcode_decision_tree()
     for node in tree:
         combination = tree[node]['condition_combination']
         num_diagnostics = len(tree[node]['diagnostic_fields'])
         if num_diagnostics == 2:
             self.assertEqual(combination in CONDITION_COMBINATIONS, True)
         else:
             self.assertEqual(combination, '')
Example #6
0
 def test_node_points_to_valid_value(self):
     """Test that succeed and fail point to valid values or nodes."""
     tree = wxcode_decision_tree()
     for node in tree:
         succeed = tree[node]['succeed']
         if isinstance(succeed, str):
             self.assertEqual(succeed in tree.keys(), True)
         fail = tree[node]['fail']
         if isinstance(fail, str):
             self.assertEqual(fail in tree, True)
Example #7
0
 def test_diagnostic_len_match(self):
     """Test diagnostic fields, thresholds and conditions are same len."""
     tree = wxcode_decision_tree()
     for node in tree:
         query = tree[node]
         diag_len = len(expand_nested_lists(query, 'diagnostic_fields'))
         thres_len = len(expand_nested_lists(query,
                                             'diagnostic_thresholds'))
         cond_len = len(expand_nested_lists(query, 'diagnostic_conditions'))
         self.assertEqual(diag_len, thres_len)
         self.assertEqual(diag_len, cond_len)
Example #8
0
 def test_diagnostic_condition(self):
     """Test only permissible values are used in diagnostic_conditions."""
     tree = wxcode_decision_tree()
     for node in tree:
         diagnostic = tree[node]['diagnostic_conditions']
         tests_diagnostic = diagnostic
         if isinstance(diagnostic[0], list):
             tests_diagnostic = [item for sublist in diagnostic
                                 for item in sublist]
         for value in tests_diagnostic:
             self.assertEqual(value in DIAGNOSTIC_CONDITIONS, True)
    def __init__(self):
        """
        Define a decision tree for determining weather symbols based upon
        the input diagnostics. Use this decision tree to allocate a weather
        symbol to each point.

        float_tolerance defines the tolerance when matching thresholds to allow
        for the difficulty of float comparisons.
        """
        self.queries = wxcode_decision_tree()
        self.float_tolerance = 0.01
Example #10
0
def interrogate_decision_tree(wxtree):
    """
    Obtain a list of necessary inputs from the decision tree as it is currently
    defined. Return a list of the diagnostic names, the thresholds needed,
    and whether they are thresholded above or below these values. This output
    is used to create the CLI help, informing the user of the necessary inputs.

    Returns:
        list of str:
            Returns a list of strings, an entry for each diagnostic required,
            including threshold details.
    """

    # Get current weather symbol decision tree and populate a list of
    # required inputs for printing.
    if wxtree == 'high_resolution':
        queries = wxcode_decision_tree()
    elif wxtree == 'global':
        queries = wxcode_decision_tree_global()

    # Diagnostic names and threshold values.
    requirements = {}
    # How the data has been thresholded relative to these thresholds.
    relative = {}

    for query in queries.values():
        diagnostics = expand_nested_lists(query, 'diagnostic_fields')
        for index, diagnostic in enumerate(diagnostics):
            if diagnostic not in requirements:
                requirements[diagnostic] = []
                relative[diagnostic] = []
            requirements[diagnostic].extend(
                expand_nested_lists(query, 'diagnostic_thresholds')[index])
            relative[diagnostic].append(
                expand_nested_lists(query, 'diagnostic_conditions')[index])

    # Create a list of formatted strings that will be printed as part of the
    # CLI help.
    output = []
    for requirement in requirements:
        entries = np.array([entry for entry in requirements[requirement]])
        relations = np.array([entry for entry in relative[requirement]])
        _, thresholds = np.unique(np.array(
            [item.points.item() for item in entries]),
                                  return_index=True)
        output.append('{}; thresholds: {}'.format(
            requirement, ', '.join([
                '{} ({})'.format(str(threshold.points.item()),
                                 str(threshold.units)) for threshold, relation
                in zip(entries[thresholds], relations[thresholds])
            ])))
    return output
Example #11
0
def interrogate_decision_tree(wxtree):
    """
    Obtain a list of necessary inputs from the decision tree as it is currently
    defined. Return a formatted string that contains the diagnostic names, the
    thresholds needed, and whether they are thresholded above or below these
    values. This output is used to create the CLI help, informing the user of
    the necessary inputs.

    Args:
        wxtree (str):
            The weather symbol tree that is to be interrogated.

    Returns:
        list of str:
            Returns a formatted string descring the diagnostics required,
            including threshold details.
    """

    # Get current weather symbol decision tree and populate a list of
    # required inputs for printing.
    if wxtree == "high_resolution":
        queries = wxcode_decision_tree()
    elif wxtree == "global":
        queries = wxcode_decision_tree_global()
    else:
        raise ValueError("Unknown decision tree name provided.")

    # Diagnostic names and threshold values.
    requirements = {}
    for query in queries.values():
        diagnostics = get_parameter_names(
            expand_nested_lists(query, "diagnostic_fields")
        )
        thresholds = expand_nested_lists(query, "diagnostic_thresholds")
        for diagnostic, threshold in zip(diagnostics, thresholds):
            requirements.setdefault(diagnostic, set()).add(threshold)

    # Create a list of formatted strings that will be printed as part of the
    # CLI help.
    output = []
    for requirement, uniq_thresh in sorted(requirements.items()):
        (units,) = set(u for (_, u) in uniq_thresh)  # enforces same units
        thresh_str = ", ".join(map(str, sorted(v for (v, _) in uniq_thresh)))
        output.append("{} ({}): {}".format(requirement, units, thresh_str))

    n_files = len(output)
    formatted_string = "{}\n" * n_files
    formatted_output = formatted_string.format(*output)

    return formatted_output
Example #12
0
    def __init__(self,
                 wxtree: str = "high_resolution",
                 model_id_attr: Optional[str] = None) -> None:
        """
        Define a decision tree for determining weather symbols based upon
        the input diagnostics. Use this decision tree to allocate a weather
        symbol to each point.

        Args:
            wxtree:
                Used to choose weather symbol decision tree.
                Default is "high_resolution"
                "global" will load the global weather symbol decision tree.
            model_id_attr:
                Name of attribute recording source models that should be
                inherited by the output cube. The source models are expected as
                a space-separated string.

        float_tolerance defines the tolerance when matching thresholds to allow
        for the difficulty of float comparisons.
        float_abs_tolerance defines the tolerance for when the threshold
        is zero. It has to be sufficiently small that a valid rainfall rate
        or snowfall rate could not trigger it.
        """
        def make_thresholds_with_units(items):
            if isinstance(items, list):
                return [make_thresholds_with_units(item) for item in items]
            values, units = items
            return iris.coords.AuxCoord(values, units=units)

        self.wxtree = wxtree
        self.model_id_attr = model_id_attr
        if wxtree == "global":
            self.queries = wxcode_decision_tree_global()
            self.start_node = START_NODE_GLOBAL
        else:
            self.queries = wxcode_decision_tree()
            self.start_node = START_NODE
        for query in self.queries.values():
            query["diagnostic_thresholds"] = make_thresholds_with_units(
                query["diagnostic_thresholds"])
        self.float_tolerance = 0.01
        self.float_abs_tolerance = 1e-12
        # flag to indicate whether to expect "threshold" as a coordinate name
        # (defaults to False, checked on reading input cubes)
        self.coord_named_threshold = False
Example #13
0
    def __init__(self, wxtree="high_resolution"):
        """
        Define a decision tree for determining weather symbols based upon
        the input diagnostics. Use this decision tree to allocate a weather
        symbol to each point.

        Key Args:
            wxtree (str):
                Used to choose weather symbol decision tree.
                Default is "high_resolution"
                "global" will load the global weather symbol decision tree.

        float_tolerance defines the tolerance when matching thresholds to allow
        for the difficulty of float comparisons.
        float_abs_tolerance defines the tolerance for when the threshold
        is zero. It has to be sufficiently small that a valid rainfall rate
        or snowfall rate could not trigger it.
        """
        def make_thresholds_with_units(items):
            if isinstance(items, list):
                return [make_thresholds_with_units(item) for item in items]
            values, units = items
            return iris.coords.AuxCoord(values, units=units)

        self.wxtree = wxtree
        if wxtree == "global":
            self.queries = wxcode_decision_tree_global()
            self.start_node = START_NODE_GLOBAL
        else:
            self.queries = wxcode_decision_tree()
            self.start_node = START_NODE
        for query in self.queries.values():
            query["diagnostic_thresholds"] = make_thresholds_with_units(
                query["diagnostic_thresholds"])
        self.float_tolerance = 0.01
        self.float_abs_tolerance = 1e-12
        # flag to indicate whether to expect "threshold" as a coordinate name
        # (defaults to False, checked on reading input cubes)
        self.coord_named_threshold = False
        # dictionary to contain names of threshold coordinates that do not
        # match expected convention
        self.threshold_coord_names = {}
Example #14
0
    def __init__(self, wxtree='high_resolution'):
        """
        Define a decision tree for determining weather symbols based upon
        the input diagnostics. Use this decision tree to allocate a weather
        symbol to each point.

        Key Args:
            wxtree (str):
                Choose weather symbol decision tree.
                Default is 'high_resolution'
                'global' will load the global weather symbol decision tree.

        float_tolerance defines the tolerance when matching thresholds to allow
        for the difficulty of float comparisons.
        """
        self.wxtree = wxtree
        if wxtree == 'global':
            self.queries = wxcode_decision_tree_global()
        else:
            self.queries = wxcode_decision_tree()
        self.float_tolerance = 0.01
Example #15
0
 def test_basic(self):
     """Test that the wxcode_decision_tree returns a dictionary."""
     result = wxcode_decision_tree()
     self.assertIsInstance(result, dict)
Example #16
0
 def test_threshold_condition(self):
     """Test only permissible values are used in threshold_condition."""
     tree = wxcode_decision_tree()
     for node in tree:
         threshold = tree[node]['threshold_condition']
         self.assertEqual(threshold in THRESHOLD_CONDITIONS, True)
Example #17
0
 def test_start_node_in_tree(self):
     """Test that the start node is in the tree"""
     tree = wxcode_decision_tree()
     self.assertTrue(START_NODE in tree)
Example #18
0
"""Unit tests for Weather Symbols Trees."""

import pytest

from improver.wxcode.utilities import WX_DICT, get_parameter_names
from improver.wxcode.wxcode_decision_tree import START_NODE, wxcode_decision_tree
from improver.wxcode.wxcode_decision_tree_global import (
    START_NODE_GLOBAL,
    wxcode_decision_tree_global,
)

from . import check_diagnostic_lists_consistency

TREE_NAMES = ["high_resolution", "global"]
TREES = {
    "high_resolution": wxcode_decision_tree(),
    "global": wxcode_decision_tree_global(),
}
START_NODES = {"high_resolution": START_NODE, "global": START_NODE_GLOBAL}

REQUIRED_KEY_WORDS = [
    "succeed",
    "fail",
    "probability_thresholds",
    "threshold_condition",
    "condition_combination",
    "diagnostic_fields",
    "diagnostic_thresholds",
    "diagnostic_conditions",
]