h2o-py/h2o/h2o.py
def upload_custom_metric(func, func_file="metrics.py", func_name=None, class_name=None, source_provider=None):
    """
    Upload given metrics function into H2O cluster.

    The metrics can have different representation:
      - class: needs to implement map(pred, act, weight, offset, model), reduce(l, r) and metric(l) methods
      - string: the same as in class case, but the class is given as a string

    :param func:  metric representation: string, class
    :param func_file:  internal name of file to save given metrics representation
    :param func_name:  name for h2o key under which the given metric is saved
    :param class_name: name of class wrapping the metrics function (when supplied as string)
    :param source_provider: a function which provides a source code for given function
    :return: reference to uploaded metrics function

    :examples:
        >>> class CustomMaeFunc:
        >>>     def map(self, pred, act, w, o, model):
        >>>         return [abs(act[0] - pred[0]), 1]
        >>>
        >>>     def reduce(self, l, r):
        >>>         return [l[0] + r[0], l[1] + r[1]]
        >>>
        >>>     def metric(self, l):
        >>>         return l[0] / l[1]
        >>>
        >>>
        >>> h2o.upload_custom_metric(CustomMaeFunc, func_name="mae")
        >>>
        >>> custom_func_str = '''class CustomMaeFunc:
        >>>     def map(self, pred, act, w, o, model):
        >>>         return [abs(act[0] - pred[0]), 1]
        >>>
        >>>     def reduce(self, l, r):
        >>>         return [l[0] + r[0], l[1] + r[1]]
        >>>
        >>>     def metric(self, l):
        >>>         return l[0] / l[1]'''
        >>>
        >>>
        >>> h2o.upload_custom_metric(custom_func_str, class_name="CustomMaeFunc", func_name="mae")
    """
    import tempfile
    import inspect

    # Use default source provider
    if not source_provider:
        source_provider = _default_source_provider

    # The template wraps given metrics representation
    _CFUNC_CODE_TEMPLATE = """# Generated code
import water.udf.CMetricFunc as MetricFunc

# User given metric function as a class implementing
# 3 methods defined by interface CMetricFunc
{}

# Generated user metric which satisfies the interface
# of Java MetricFunc
class {}Wrapper({}, MetricFunc, object):
    pass

"""

    assert_satisfies(func, inspect.isclass(func) or isinstance(func, str),
                     "The argument func needs to be string or class !")
    assert_satisfies(func_file, func_file is not None,
                     "The argument func_file is missing!")
    assert_satisfies(func_file, func_file.endswith('.py'),
                     "The argument func_file needs to end with '.py'")
    code = None
    derived_func_name = None
    module_name = func_file[:-3]
    if isinstance(func, str):
        assert_satisfies(class_name, class_name is not None,
                         "The argument class_name is missing! " +
                         "It needs to reference the class in given string!")
        code = _CFUNC_CODE_TEMPLATE.format(func, class_name, class_name)
        derived_func_name = "metrics_{}".format(class_name)
        class_name = "{}.{}Wrapper".format(module_name, class_name)
    else:
        assert_satisfies(func, inspect.isclass(func), "The parameter `func` should be str or class")
        for method in ['map', 'reduce', 'metric']:
            assert_satisfies(func, method in func.__dict__, "The class `func` needs to define method `{}`".format(method))

        assert_satisfies(class_name, class_name is None,
                         "If class is specified then class_name parameter needs to be None")

        class_name = "{}.{}Wrapper".format(module_name, func.__name__)
        derived_func_name = "metrics_{}".format(func.__name__)
        code = _CFUNC_CODE_TEMPLATE.format(source_provider(func), func.__name__, func.__name__)

    # If the func name is not given, use whatever we can derived from given definition
    if not func_name:
        func_name = derived_func_name
    # Saved into jar file
    tmpdir = tempfile.mkdtemp(prefix="h2o-func")
    func_arch_file = _create_zip_file("{}/func.jar".format(tmpdir), (func_file, code))
    # Upload into K/V
    dest_key = _put_key(func_arch_file, dest_key=func_name)
    # Reference
    return "python:{}={}".format(dest_key, class_name)
def upload_custom_metric(func, func_file="metrics.py", func_name=None, class_name=None, source_provider=None):
    """
    Upload given metrics function into H2O cluster.

    The metrics can have different representation:
      - class: needs to implement map(pred, act, weight, offset, model), reduce(l, r) and metric(l) methods
      - string: the same as in class case, but the class is given as a string

    :param func:  metric representation: string, class
    :param func_file:  internal name of file to save given metrics representation
    :param func_name:  name for h2o key under which the given metric is saved
    :param class_name: name of class wrapping the metrics function (when supplied as string)
    :param source_provider: a function which provides a source code for given function
    :return: reference to uploaded metrics function

    :examples:
        >>> class CustomMaeFunc:
        >>>     def map(self, pred, act, w, o, model):
        >>>         return [abs(act[0] - pred[0]), 1]
        >>>
        >>>     def reduce(self, l, r):
        >>>         return [l[0] + r[0], l[1] + r[1]]
        >>>
        >>>     def metric(self, l):
        >>>         return l[0] / l[1]
        >>>
        >>>
        >>> h2o.upload_custom_metric(CustomMaeFunc, func_name="mae")
        >>>
        >>> custom_func_str = '''class CustomMaeFunc:
        >>>     def map(self, pred, act, w, o, model):
        >>>         return [abs(act[0] - pred[0]), 1]
        >>>
        >>>     def reduce(self, l, r):
        >>>         return [l[0] + r[0], l[1] + r[1]]
        >>>
        >>>     def metric(self, l):
        >>>         return l[0] / l[1]'''
        >>>
        >>>
        >>> h2o.upload_custom_metric(custom_func_str, class_name="CustomMaeFunc", func_name="mae")
    """
    import tempfile
    import inspect

    # Use default source provider
    if not source_provider:
        source_provider = _default_source_provider

    # The template wraps given metrics representation
    _CFUNC_CODE_TEMPLATE = """# Generated code
import water.udf.CMetricFunc as MetricFunc

# User given metric function as a class implementing
# 3 methods defined by interface CMetricFunc
{}

# Generated user metric which satisfies the interface
# of Java MetricFunc
class {}Wrapper({}, MetricFunc, object):
    pass

"""

    assert_satisfies(func, inspect.isclass(func) or isinstance(func, str),
                     "The argument func needs to be string or class !")
    assert_satisfies(func_file, func_file is not None,
                     "The argument func_file is missing!")
    assert_satisfies(func_file, func_file.endswith('.py'),
                     "The argument func_file needs to end with '.py'")
    code = None
    derived_func_name = None
    module_name = func_file[:-3]
    if isinstance(func, str):
        assert_satisfies(class_name, class_name is not None,
                         "The argument class_name is missing! " +
                         "It needs to reference the class in given string!")
        code = _CFUNC_CODE_TEMPLATE.format(func, class_name, class_name)
        derived_func_name = "metrics_{}".format(class_name)
        class_name = "{}.{}Wrapper".format(module_name, class_name)
    else:
        assert_satisfies(func, inspect.isclass(func), "The parameter `func` should be str or class")
        for method in ['map', 'reduce', 'metric']:
            assert_satisfies(func, method in func.__dict__, "The class `func` needs to define method `{}`".format(method))

        assert_satisfies(class_name, class_name is None,
                         "If class is specified then class_name parameter needs to be None")

        class_name = "{}.{}Wrapper".format(module_name, func.__name__)
        derived_func_name = "metrics_{}".format(func.__name__)
        code = _CFUNC_CODE_TEMPLATE.format(source_provider(func), func.__name__, func.__name__)

    # If the func name is not given, use whatever we can derived from given definition
    if not func_name:
        func_name = derived_func_name
    # Saved into jar file
    tmpdir = tempfile.mkdtemp(prefix="h2o-func")
    func_arch_file = _create_zip_file("{}/func.jar".format(tmpdir), (func_file, code))
    # Upload into K/V
    dest_key = _put_key(func_arch_file, dest_key=func_name)
    # Reference
    return "python:{}={}".format(dest_key, class_name)
Upload given metrics function into H2O cluster.

    The metrics can have different representation:
      - class: needs to implement map(pred, act, weight, offset, model), reduce(l, r) and metric(l) methods
      - string: the same as in class case, but the class is given as a string

    :param func:  metric representation: string, class
    :param func_file:  internal name of file to save given metrics representation
    :param func_name:  name for h2o key under which the given metric is saved
    :param class_name: name of class wrapping the metrics function (when supplied as string)
    :param source_provider: a function which provides a source code for given function
    :return: reference to uploaded metrics function

    :examples:
        >>> class CustomMaeFunc:
        >>>     def map(self, pred, act, w, o, model):
        >>>         return [abs(act[0] - pred[0]), 1]
        >>>
        >>>     def reduce(self, l, r):
        >>>         return [l[0] + r[0], l[1] + r[1]]
        >>>
        >>>     def metric(self, l):
        >>>         return l[0] / l[1]
        >>>
        >>>
        >>> h2o.upload_custom_metric(CustomMaeFunc, func_name="mae")
        >>>
        >>> custom_func_str = '''class CustomMaeFunc:
        >>>     def map(self, pred, act, w, o, model):
        >>>         return [abs(act[0] - pred[0]), 1]
        >>>
        >>>     def reduce(self, l, r):
        >>>         return [l[0] + r[0], l[1] + r[1]]
        >>>
        >>>     def metric(self, l):
        >>>         return l[0] / l[1]'''
        >>>
        >>>
        >>> h2o.upload_custom_metric(custom_func_str, class_name="CustomMaeFunc", func_name="mae")