//  [python]
def plot_early_stoping(
    hparams, exp_results, filter_ids=["01.8", "03.6", "08.7", "16.9", "51.3", "100.0"]
):

    # Builds a list of tuples (sparsity, experiment, early_stop_iteration)
    # for creating the DataFrame
    early_stop_iter = []
    for experiment, results in exp_results.items():
        for key, value in sorted(results.items()):
            if filter_ids is not None and value["sparsity"] not in filter_ids:
                continue
            label = experiment
            sparsity = value["sparsity"]
            target_iteration = get_early_stop_iteration(value)
            early_stop_iteration = value["valid_acc_log"]["batch"][target_iteration]
            early_stop_adv_acc = value["valid_acc_log"]["adv_acc"][target_iteration]
            early_stop_acc = value["valid_acc_log"]["acc"][target_iteration]
            early_stop_iter.append((sparsity, label, early_stop_iteration, early_stop_adv_acc, early_stop_acc))

    data_frame = pd.DataFrame(
        early_stop_iter, columns=["Sparsity", "Experiment", "Iteration", "Adversarial_Accuracy", "Test_Accuracy"]
    )
    sorted_index = pd.Series.argsort(data_frame["Sparsity"].astype(float))[::-1]
    data_frame = data_frame.iloc[sorted_index]

    fig, axes = plt.subplots(1, 3, figsize=(21, 5))

    left = sns.lineplot(
        x="Sparsity",
        y="Iteration",
        hue="Experiment",
        ax=axes[0],
        ci="sd",
        sort=False,
        data=data_frame,
    )
    left.set(
        xlabel="Percent of Weights Remaining", ylabel="Early Stop Iteration (Val.)"
    )
    left.get_legend().remove()
    
    right = sns.lineplot(
        x="Sparsity",
        y="Adversarial_Accuracy",
        hue="Experiment",
        ax=axes[1],
        ci="sd",
        sort=False,
        data=data_frame,
    )
    right.set(
        xlabel="Percent of Weights Remaining", ylabel="Early Stop Adv. Acc. (Val.)"
    )
    right.set(ylim=YLIMS[hparams["attack"]][hparams["dataset"]]["adv_acc"])
    right.get_legend().remove()

    third = sns.lineplot(
        x="Sparsity",
        y="Test_Accuracy",
        hue="Experiment",
        ax=axes[2],
        ci="sd",
        sort=False,
        data=data_frame,
    )
    third.set(
        xlabel="Percent of Weights Remaining", ylabel="Early Stop Test Acc. (Val.)"
    )
    third.set(ylim=YLIMS[hparams["attack"]][hparams["dataset"]]["adv_acc"])
    third.get_legend().remove()

    left_handles, left_labels = left.get_legend_handles_labels()
    right_handles, right_labels = right.get_legend_handles_labels()
    third_handles, third_labels = third.get_legend_handles_labels()
    handles = left_handles[1:] + right_handles[1:] + third_handles[1:]
    legend_labels = left_labels[1:] + right_labels[1:] + third_labels[1:]

    def update_label(legend_label):
        if "orig" in legend_label:
            legend_label = "original"
        elif "rand" in legend_label:
            legend_label = "random"
        elif "none" in legend_label:
            legend_label = "continued"
        return legend_label

    legend_labels = [update_label(label) for label in legend_labels]

    by_label = dict(
        zip(legend_labels, handles)
    )
    
    fig.legend(
        by_label.values(),
        by_label.keys(),
        bbox_to_anchor=(0, 1.0, 1.0, 0.05),
        loc="lower center",
        ncol=3,
        borderaxespad=0.0,
        frameon=False,
    )

    plt.tight_layout()

    file_name = "test_{}.svg".format("early_stop_iteration")
    file_path = os.path.join(hparams["analysis_dir"], file_name)

    fig.savefig(file_path, format="svg", bbox_inches="tight")
    plt.clf()
    print("Saving figure to ", file_path)