def plot_combined(name, ax, runs, methods, args, agg, lo=None, hi=None, clip=False):
    tasks = sorted(set((run.task for run in runs if (run.xs is not None))))
    seeds = list(set((run.seed for run in runs)))
    runs = [r for r in runs if (r.task in tasks)]
    borders = sorted([r.xs for r in runs if (r.xs is not None)], key=(lambda x: np.nanmax(x)))[(- 1)]
    for (index, run) in enumerate(runs):
        if (run.xs is None):
            continue
        (xs, ys) = bin_scores(run.xs, run.ys, borders)
        runs[index] = run._replace(xs=xs, ys=ys)
    if (lo or hi):
        mins = collections.defaultdict(list)
        maxs = collections.defaultdict(list)
        [mins[r.task].append(r.ys) for r in load_baselines([re.compile(lo)])]
        [maxs[r.task].append(r.ys) for r in load_baselines([re.compile(hi)])]
        mins = {task: min(ys) for (task, ys) in mins.items() if (task in tasks)}
        maxs = {task: max(ys) for (task, ys) in maxs.items() if (task in tasks)}
        missing_baselines = []
        for task in tasks:
            if ((task not in mins) or (task not in maxs)):
                missing_baselines.append(task)
        if (set(missing_baselines) == set(tasks)):
            print(f'No baselines found to normalize any tasks in {name} plot.')
        else:
            for task in missing_baselines:
                print(f'No baselines found to normalize {task} in {name} plot.')
        for (index, run) in enumerate(runs):
            if ((run.task not in mins) or (run.task not in maxs)):
                continue
            ys = ((run.ys - mins[run.task]) / (maxs[run.task] - mins[run.task]))
            if clip:
                ys = np.minimum(ys, 1.0)
            runs[index] = run._replace(ys=ys)
    combined = []
    for (method, seed) in itertools.product(methods, seeds):
        relevant = [r for r in runs if ((r.method == method) and (r.seed == seed))]
        if (not relevant):
            continue
        if (relevant[0].xs is None):
            (xs, ys) = (None, np.array([r.ys for r in relevant]))
        else:
            (xs, ys) = stack_scores(*zip(*[(r.xs, r.ys) for r in relevant]))
        with warnings.catch_warnings():
            warnings.simplefilter('ignore', category=RuntimeWarning)
            combined.append(Run('combined', method, seed, xs, agg(ys)))
    plot(name, ax, combined, methods, args)
def plot_combined(name, ax, runs, methods, args, agg, lo=None, hi=None, clip=False):
    tasks = sorted(set((run.task for run in runs if (run.xs is not None))))
    seeds = list(set((run.seed for run in runs)))
    runs = [r for r in runs if (r.task in tasks)]
    borders = sorted([r.xs for r in runs if (r.xs is not None)], key=(lambda x: np.nanmax(x)))[(- 1)]
    for (index, run) in enumerate(runs):
        if (run.xs is None):
            continue
        (xs, ys) = bin_scores(run.xs, run.ys, borders)
        runs[index] = run._replace(xs=xs, ys=ys)
    if (lo or hi):
        mins = collections.defaultdict(list)
        maxs = collections.defaultdict(list)
        [mins[r.task].append(r.ys) for r in load_baselines([re.compile(lo)])]
        [maxs[r.task].append(r.ys) for r in load_baselines([re.compile(hi)])]
        mins = {task: min(ys) for (task, ys) in mins.items() if (task in tasks)}
        maxs = {task: max(ys) for (task, ys) in maxs.items() if (task in tasks)}
        missing_baselines = []
        for task in tasks:
            if ((task not in mins) or (task not in maxs)):
                missing_baselines.append(task)
        if (set(missing_baselines) == set(tasks)):
            print(f'No baselines found to normalize any tasks in {name} plot.')
        else:
            for task in missing_baselines:
                print(f'No baselines found to normalize {task} in {name} plot.')
        for (index, run) in enumerate(runs):
            if ((run.task not in mins) or (run.task not in maxs)):
                continue
            ys = ((run.ys - mins[run.task]) / (maxs[run.task] - mins[run.task]))
            if clip:
                ys = np.minimum(ys, 1.0)
            runs[index] = run._replace(ys=ys)
    combined = []
    for (method, seed) in itertools.product(methods, seeds):
        relevant = [r for r in runs if ((r.method == method) and (r.seed == seed))]
        if (not relevant):
            continue
        if (relevant[0].xs is None):
            (xs, ys) = (None, np.array([r.ys for r in relevant]))
        else:
            (xs, ys) = stack_scores(*zip(*[(r.xs, r.ys) for r in relevant]))
        with warnings.catch_warnings():
            warnings.simplefilter('ignore', category=RuntimeWarning)
            combined.append(Run('combined', method, seed, xs, agg(ys)))
    plot(name, ax, combined, methods, args)