Source code for supernodes.operations

"""
This is a module that transforms a string to an inequality.
"""
import operator
import re
from decimal import Decimal


operators = {"<": operator.lt,
          "<=": operator.le,
          "==": operator.eq,
          "!=": operator.ne,
          ">=": operator.ge,
          ">": operator.gt}

def _is_int(string_num):
    return string_num.isdigit()


def _is_decimal(string_num):
    if _is_int(string_num): return False
    try:
        float(string_num)
        return True
    except ValueError:
        return False


[docs]class InEquality: """ Runs an inequality operation from an inequality string. This is helpful when running a tree as a decision tree. Examples -------- Running an inequality: >>> inequality = InEquality("x == 10") >>> inequality(x=7) False >>> inequality = InEquality("x == -10") >>> inequality(x=-10) True >>> inequality = InEquality("y != 9.01") >>> inequality(y=9.01) False Including a list in the inequality: >>> inequality = InEquality("x[0] > x[1]") >>> inequality(x=[10, 20]) False Note that slicing is not yet supported when using lists. Including two variables in the inequality: >>> inequality = InEquality("x == y") >>> inequality(x=10, y=10) True Including an inequality in a binary decision tree: >>> from supernodes import SuperNode >>> inequality = InEquality("x < 100") >>> main_node = SuperNode(name="main-node", function=inequality) >>> main_node['first-child'] = SuperNode() >>> main_node['second-child'] = SuperNode() >>> main_node.child_name_if_true = "first-child" >>> main_node.child_name_if_false = "second-child" Running the decision tree: >>> main_node.run_as_binary_tree(x=101) (name=second-child, value: NoneType) """ def __init__(self, inequality: str, strings_to_numbers=True): """ Parameters ---------- inequality: str The inequality represented as a string. strings_to_numbers: bool If ``True``, each string that can be converted to an ``int`` or ``float`` will be converted. """ inequality_list = inequality.split() if len(inequality_list) != 3: raise ValueError(f"Splitting the inequality resulted in {len(inequality_list)} parts instead of 3 parts.") self.left = inequality_list[0] self.middle = inequality_list[1] self.right = inequality_list[2] self.strings_to_numbers = strings_to_numbers def __call__(self, **kwargs): left = self.left right = self.right left_indices = re.findall(r"\w+\[(.+)\]", left) right_indices = re.findall(r"\w+\[(.+)\]", right) for key, value in kwargs.items(): if left == key or re.search(f"{key}" + r"\[(.+)\]", left): left = value break for key, value in kwargs.items(): if right == key or re.search(f"{key}" + r"\[(.+)\]", right): right = value break if self.strings_to_numbers: if type(left) is str: if _is_int(left): left = int(left) elif _is_decimal(left): left = Decimal(left) if type(right) is str: if _is_int(right): right = int(right) elif _is_decimal(right): right = float(right) for index in left_indices: if _is_int(index): index = int(index) elif _is_decimal(index): index = float(index) left = left[index] for index in right_indices: if _is_int(index): index = int(index) elif _is_decimal(index): index = float(index) right = right[index] oper = operators[self.middle] out = oper(left, right) return out
if __name__ == "__main__": import doctest doctest.testmod()