def reduce_fit(interface, state, label, inp):
    """
    Function separates aggregation of continuous and discrete features.
    For continuous features it aggregates partially calculated means and variances and returns them. For discrete
    features it aggregates pairs and returns them. Pairs with label occurrences are used to calculate prior probabilities
    """
    from disco.util import kvgroup  # function for grouping values by key
    import numpy as np

    out = interface.output(0)  # all outputted pairs have the same output label

    # model of naive Bayes stores label names, sum of all label occurrences and pairs
    # (feature index, feature values) for discrete features which are needed to optimize predict phase.
    fit_model = {"y_labels": [], "y_sum": 0, "iv": set()}
    combiner = {}  # combiner maintains correct order of means and variances.
    means, variances = [], []
    k_prev = ""

    for key, value in kvgroup(inp):  # input pairs are sorted and grouped by key
        k_split = key.split(state["delimiter"])  # pair is split

        if len(k_split) == 3:  # discrete features
            # store pair (feature index, feature value)
            fit_model["iv"].add(tuple(k_split[1:]))
            # aggregate and output occurrences of a pair
            out.add(tuple(k_split), sum(value))

        elif len(k_split) == 2:  # continuous features

            # if label is different than previous.
            # This enables calculation of all variances and means for every feature for current label.
            if k_split[0] != k_prev and k_prev != "":
                mean, var = zip(*[combiner[key] for key in sorted(combiner.keys())])
                means.append(mean)
                variances.append(var)

            # number of elements, partial mean, partial variance.
            n_a = mean_a = var_a = 0
            # code aggregates partially calculated means and variances
            for n_b, mean_b, var_b in value:
                n_ab = n_a + n_b
                var_a = ((n_a * var_a + n_b * var_b) / float(n_ab)) + (
                    n_a * n_b * ((mean_b - mean_a) / float(n_ab)) ** 2)
                mean_a = (n_a * mean_a + n_b * mean_b) / float(n_ab)
                n_a = n_ab

            # maintains correct order of statistics for every feature
            combiner[int(k_split[1])] = (mean_a, var_a + 1e-9)
            k_prev = k_split[0]

        else:  # aggregates label occurrences
            fit_model[key] = np.sum(value)
            fit_model["y_sum"] += fit_model[key]  # sum of all label occurrences
            fit_model["y_labels"].append(key)

    # if statistics for continuous features were not output in last iteration
    if len(means) > 0:
        mean, var = zip(*[combiner[key] for key in sorted(combiner.keys())])
        out.add("mean", np.array(means + [mean], dtype=np.float32))
        variances = np.array(variances + [var], dtype=np.float32)
        out.add("var", variances)
        out.add("var_log", np.log(np.pi * variances))

    # calculation of prior probabilities
    prior = [fit_model[y_label] / float(fit_model["y_sum"]) for y_label in fit_model["y_labels"]]
    out.add("prior", np.array(prior, dtype=np.float32))
    out.add("prior_log", np.log(prior))
    out.add("iv", list(fit_model["iv"]))
    out.add("y_labels", fit_model["y_labels"])
dataset/ETHPy150Open romanorac/discomll/discomll/classification/naivebayes.py/reduce_fit/original