jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def linkage_decay_plot(db, chunkSize=5, min_vals=5, title=''):
    COLS = ['r2', 'r2_normalized', 'd_prime', 'd_prime_normalized']

    # Make chunks
    max_d = db['distance'].max()
    table = defaultdict(list)
    numberChunks = max_d // chunkSize + 1
    db['distance'] = db['distance'].astype(int)
    for i in range(numberChunks):
        d = db[(db['distance'] >= int(i*chunkSize)) & (db['distance'] < int((i+1)*chunkSize))]

        table['d_start'].append(int(i*chunkSize))
        table['d_end'].append(int((i+1)*chunkSize))
        table['values'].append(len(d))
        for col in COLS:
            table[col].append(d[col].mean())
            table[col + '_values'].append(len(d[~d[col].isna()]))
    df = pd.DataFrame(table)
    df['distance'] = [np.mean([x, y]) for x, y in zip(df['d_start'], df['d_end'])]
    df = df.sort_values('distance')

    for col in COLS:
        df[col] = [c if v >= min_vals else np.nan for c, v in zip(df[col], df[col + '_values'])]

    for col in COLS:
        sns.lineplot(df['distance'], df[col], label=col, marker='o')

    plt.title(title)
    plt.xlabel('Distance between SNPs (bp)\nAveraged over {0}bp windows; plotting windows with at least {1} values'.format(
                chunkSize, min_vals))
    plt.ylabel("SNP linkage")
    return df
//  [python]
def linkage_decay_type(Odb, chunkSize=5, min_vals=2, title=''):
    COLS = ['r2', 'r2_normalized', 'd_prime', 'd_prime_normalized']

    # Make chunks
    max_d = Odb['distance'].max()
    table = defaultdict(list)
    numberChunks = max_d // chunkSize + 1
    for lt in ['S=S', 'N-N', 'all']:
        if lt == 'all':
            db = Odb
        else:
            db = Odb[Odb['link_type'] == lt]

        db['distance'] = db['distance'].astype(int)
        for i in range(numberChunks):
            d = db[(db['distance'] >= int(i*chunkSize)) & (db['distance'] < int((i+1)*chunkSize))]

            table['d_start'].append(int(i*chunkSize))
            table['d_end'].append(int((i+1)*chunkSize))
            table['values'].append(len(d))
            table['link_type'].append(lt)
            for col in COLS:
                table[col].append(d[col].mean())
                table[col + '_values'].append(len(d[~d[col].isna()]))
    df = pd.DataFrame(table)
    df['distance'] = [np.mean([x, y]) for x, y in zip(df['d_start'], df['d_end'])]
    df = df.sort_values('distance')

    for thing in ['S-S', 'N-N', 'all']:
        if thing == 'all':
            fdb = df
        else:
            fdb = df[df['link_type'] == thing]
        plt.plot(fdb['distance'], fdb['r2'], label=thing, marker='o')

    plt.title(title)
    plt.xlabel('Distance between SNPs (bp)\nAveraged over {0}bp windows; plotting windows with at least {1} values'.format(
                chunkSize, min_vals))
    plt.ylabel("SNP linkage")
    return df
//  [python]
def linkage_decay_type(Odb, chunkSize=5, min_vals=2, title=''):
    COLS = ['r2', 'r2_normalized', 'd_prime', 'd_prime_normalized']

    # Make chunks
    max_d = Odb['distance'].max()
    table = defaultdict(list)
    numberChunks = max_d // chunkSize + 1
    for lt in ['S=S', 'N-N', 'all']:
        if lt == 'all':
            db = Odb
        else:
            db = Odb[Odb['link_type'] == lt]

        db['distance'] = db['distance'].astype(int)
        for i in range(numberChunks):
            d = db[(db['distance'] >= int(i*chunkSize)) & (db['distance'] < int((i+1)*chunkSize))]

            table['d_start'].append(int(i*chunkSize))
            table['d_end'].append(int((i+1)*chunkSize))
            table['values'].append(len(d))
            table['link_type'].append(lt)
            for col in COLS:
                table[col].append(d[col].mean())
                table[col + '_values'].append(len(d[~d[col].isna()]))
    df = pd.DataFrame(table)
    df['distance'] = [np.mean([x, y]) for x, y in zip(df['d_start'], df['d_end'])]
    df = df.sort_values('distance')

    for thing in ['S-S', 'N-N', 'all']:
        if thing == 'all':
            fdb = df
        else:
            fdb = df[df['link_type'] == thing]
        sns.lineplot(data=fdb, x='distance', y='r2', label=thing, marker='o')

    plt.title(title)
    plt.xlabel('Distance between SNPs (bp)\nAveraged over {0}bp windows; plotting windows with at least {1} values'.format(
                chunkSize, min_vals))
    plt.ylabel("SNP linkage")
    return df
//  [python]
def vis_training_curves(history=None, output_dir: str = None) -> None:
    """
    Visualizes the training curves of the model.

    Args:
        history: The history object returned by the model.fit() method.
        output_dir (Optional[str]): The output directory to save the training curves plot.

    Returns:
        None
    """
    # Extract the accuracy and loss values for training and validation data
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs_range = range(len(acc))

    # Create dataframes for the training and validation data
    df_val = pd.DataFrame(
        {'run': 'validation', 'step': epochs_range, 'epoch_accuracy': val_acc, 'epoch_loss': val_loss})
    df_train = pd.DataFrame({'run': 'train', 'step': epochs_range, 'epoch_accuracy': acc, 'epoch_loss': loss})

    # Concatenate the dataframes
    frames = [df_val, df_train]
    df = pd.concat(frames)
    df = df.reset_index()

    # Plot the training curves
    plt.figure(figsize=(16, 6))
    plt.subplot(1, 2, 1)
    sns.pointplot(data=df, x="step", y="epoch_accuracy", hue="run").set_title("accuracy")
    plt.grid()
    plt.subplot(1, 2, 2)
    sns.pointplot(data=df, x="step", y="epoch_loss", hue="run").set_title("loss")
    plt.grid()
    plt.savefig(os.path.join(output_dir, 'Training_curves.png'))
    plt.plot()
    plt.pause(0.01)
//  [python]
def vis_training_curves(history=None, output_dir: str = None) -> None:
    """
    Visualizes the training curves of the model.

    Args:
        history: The history object returned by the model.fit() method.
        output_dir (Optional[str]): The output directory to save the training curves plot.

    Returns:
        None
    """
    # Extract the accuracy and loss values for training and validation data
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs_range = range(len(acc))

    # Create dataframes for the training and validation data
    df_val = pd.DataFrame(
        {'run': 'validation', 'step': epochs_range, 'epoch_accuracy': val_acc, 'epoch_loss': val_loss})
    df_train = pd.DataFrame({'run': 'train', 'step': epochs_range, 'epoch_accuracy': acc, 'epoch_loss': loss})

    # Concatenate the dataframes
    frames = [df_val, df_train]
    df = pd.concat(frames)
    df = df.reset_index()

    # Plot the training curves
    plt.figure(figsize=(16, 6))
    plt.subplot(1, 2, 1)
    sns.lineplot(data=df, x="step", y="epoch_accuracy", hue="run").set_title("accuracy")
    plt.grid()
    plt.subplot(1, 2, 2)
    sns.lineplot(data=df, x="step", y="epoch_loss", hue="run").set_title("loss")
    plt.grid()
    plt.savefig(os.path.join(output_dir, 'Training_curves.png'))
    plt.plot()
    plt.pause(0.01)
//  [python]
def print_params(self, include = [], it = 0):
               
        params = self.parameters["0"].keys()

        sel_trees = include

        data_params = pd.DataFrame({"tree": itertools.chain.from_iterable(zip(*itertools.repeat(sel_trees, self.num_iter))),
                                    "num_iter": [*range(self.num_iter)]*len(sel_trees)})
        for p in params:

            if p.startswith("cf_conc"):

                data_params[p+"_0"] = [i[0] for key in sel_trees for i in self.parameters[str(key)][p]]

            else:
                data_params[p] = [i.item() for key in sel_trees for i in self.parameters[str(key)][p]]

        rows = math.ceil(len(params)/4)+1

        fig, ax = plt.subplots(nrows = rows, ncols = 4, figsize = (35, math.ceil(rows)*6))
        count = 0
        
        if it == 0:
            for index, p in enumerate(params):

                count += 1
                row = divmod(count, 4)[0]
                col = divmod(count, 4)[1]

                if p.startswith("cf_conc"):
                    
                    name = p+ "_0"
                    
                    sns.pointplot(data = data_params, x = "num_iter", y = name, hue="tree", palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = name, xlabel = "iterations")

                else:
                    sns.pointplot(data = data_params, x = "num_iter", y = p, hue="tree", palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = p, xlabel = "iterations")
                    
        else:
            for index, p in enumerate(params):

                count += 1
                row = divmod(count, 4)[0]
                col = divmod(count, 4)[1]
                
                if p.startswith("cf_conc"):
                    
                    name = p + "_0"
                    
                    sns.pointplot(data = data_params[data_params["num_iter"] > it], x = "num_iter", y = name, hue="tree", 
                                 palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = name, xlabel = "iterations")

                else:
                    sns.pointplot(data = data_params[data_params["num_iter"] > it], x = "num_iter", y = p, hue="tree", 
                                 palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = p, xlabel = "iterations")
//  [python]
def print_params(self, include = [], it = 0):
               
        params = self.parameters["0"].keys()

        sel_trees = include

        data_params = pd.DataFrame({"tree": itertools.chain.from_iterable(zip(*itertools.repeat(sel_trees, self.num_iter))),
                                    "num_iter": [*range(self.num_iter)]*len(sel_trees)})
        for p in params:

            if p.startswith("cf_conc"):

                data_params[p+"_0"] = [i[0] for key in sel_trees for i in self.parameters[str(key)][p]]

            else:
                data_params[p] = [i.item() for key in sel_trees for i in self.parameters[str(key)][p]]

        rows = math.ceil(len(params)/4)+1

        fig, ax = plt.subplots(nrows = rows, ncols = 4, figsize = (35, math.ceil(rows)*6))
        count = 0
        
        if it == 0:
            for index, p in enumerate(params):

                count += 1
                row = divmod(count, 4)[0]
                col = divmod(count, 4)[1]

                if p.startswith("cf_conc"):
                    
                    name = p+ "_0"
                    
                    sns.lineplot(data = data_params, x = "num_iter", y = name, hue="tree", palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = name, xlabel = "iterations")

                else:
                    sns.lineplot(data = data_params, x = "num_iter", y = p, hue="tree", palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = p, xlabel = "iterations")
                    
        else:
            for index, p in enumerate(params):

                count += 1
                row = divmod(count, 4)[0]
                col = divmod(count, 4)[1]
                
                if p.startswith("cf_conc"):
                    
                    name = p + "_0"
                    
                    sns.lineplot(data = data_params[data_params["num_iter"] > it], x = "num_iter", y = name, hue="tree", 
                                 palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = name, xlabel = "iterations")

                else:
                    sns.lineplot(data = data_params[data_params["num_iter"] > it], x = "num_iter", y = p, hue="tree", 
                                 palette = sns.color_palette("tab10", len(sel_trees)), ax=ax[row, col])
                    ax[row, col].set(title = p, xlabel = "iterations")
//  [python]
def plot(dftrain, dfval, metric, title, outpath, smoothing=False, skip=None, include_val=False, ylim=(None, None)):
    """
    Simple plotting function. Given train/val data and options, saves fig
    outpath.
    """
    # TODO Increase figure size
    dftrain_new = dftrain.iloc[skip:]
    dfval = dfval.loc[skip:]

    sns.plt.plot(dftrain_new.index, dftrain_new[metric], label=metric, alpha=0.6)
    if smoothing:
        sns.plt.plot(dftrain_new.index, smooth(dftrain_new[metric]), color="lightblue")
    if include_val:
        sns.plt.plot(dfval.index, dfval[metric], label=metric+"-val")

    plt.ylabel("Loss Value")
    plt.xlabel("Iteration Number")
    plt.ylim(ylim)
    plt.title(title)
    plt.savefig(outpath + title_to_fn(title) + ".png")
    plt.figure()
//  [python]
def plot(dftrain, dfval, metric, title, outpath, smoothing=False, skip=None, include_val=False, ylim=(None, None)):
    """
    Simple plotting function. Given train/val data and options, saves fig
    outpath.
    """
    # TODO Increase figure size
    dftrain_new = dftrain.iloc[skip:]
    dfval = dfval.loc[skip:]

    sns.lineplot(x=dftrain_new.index, y=metric, data=dftrain_new, label=metric, alpha=0.6)
    if smoothing:
        sns.lineplot(x=dftrain_new.index, y=smooth(dftrain_new[metric]), color="lightblue")
    if include_val:
        sns.lineplot(x=dfval.index, y=metric, data=dfval, label=metric+"-val")

    plt.ylabel("Loss Value")
    plt.xlabel("Iteration Number")
    plt.ylim(ylim)
    plt.title(title)
    plt.savefig(outpath + title_to_fn(title) + ".png")
    plt.figure()
//  [python]
def feature_importance(self, top_x_feats=None):
        if top_x_feats is not None:
            y_axis = np.arange(top_x_feats)
            x_axis = self.feat_imps_[:top_x_feats]
        else:
            y_axis = np.arange(len(self.columns_))
            x_axis = self.feat_imps_

        # This dashed line indicates the cut-off point for your features.
        # All features below this line have been pruned
        plt.plot(x_axis, [self.k for i in range(len(y_axis))], linestyle='--')
        # The feature importance scores in the form of a horizontal bar chart
        sns.barplot(x=x_axis, y=y_axis, orient="h")
        # Use feature names if already provided or generate synthetic ones
        if type(self.columns_[0]) == str:
            plt.yticks(y_axis, self.columns_[:len(y_axis)], size='small')
        else:
            plt.yticks(y_axis, ["Feature "+str(i) for i in self.columns_[:len(y_axis)]], size='small')
        plt.xlabel("Importance Scores")
        plt.ylabel("Features")
        sns.despine()
        plt.show()
//  [python]
def feature_importance(self, top_x_feats=None):
        if top_x_feats is not None:
            y_axis = np.arange(top_x_feats)
            x_axis = self.feat_imps_[:top_x_feats]
        else:
            y_axis = np.arange(len(self.columns_))
            x_axis = self.feat_imps_

        # This dashed line indicates the cut-off point for your features.
        # All features below this line have been pruned
        sns.lineplot(x=x_axis, y=[self.k for i in range(len(y_axis))], linestyle='--')
        # The feature importance scores in the form of a horizontal bar chart
        sns.barplot(x=x_axis, y=y_axis, orient="h")
        # Use feature names if already provided or generate synthetic ones
        if type(self.columns_[0]) == str:
            plt.yticks(y_axis, self.columns_[:len(y_axis)], size='small')
        else:
            plt.yticks(y_axis, ["Feature "+str(i) for i in self.columns_[:len(y_axis)]], size='small')
        plt.xlabel("Importance Scores")
        plt.ylabel("Features")
        sns.despine()
        plt.show()
//  [python]
def plot_cumulative_hit_times(
    hitting_time_vecs: Union[np.ndarray, List[np.ndarray]],
    names: List[Union[str, int, float]],
    max_episode_steps: int,
    out_directory: str,
    plt_filename: str,
    name_key: str = "Legend",
    title: Optional[str] = None,
    wandb_run: Optional[Run] = None,
) -> None:
    """Create a plot of the cumulative hitting times."""
    df = pd.DataFrame()
    for name, hitting_times in zip(names, hitting_time_vecs):
        times, accumulated_probs = calculate_cdf_curve(hitting_times, max_episode_steps)
        if times[-1] != max_episode_steps - 1:
            times = np.concatenate((times, [max_episode_steps - 1]), axis=0)
            accumulated_probs = np.concatenate(
                (accumulated_probs, [accumulated_probs[-1]]),
                axis=0,
            )
        d = pd.DataFrame(
            {
                name_key: name,
                "Hitting Time": times,
                "Cumulative Probability": accumulated_probs,
            },
        )
        df = df.append(d, ignore_index=True)

    sns.pointplot(
        x="Hitting Time",
        y="Cumulative Probability",
        hue=name_key,
        data=df,
        palette="viridis",
        n_boot=10000,
    )
    plt.grid()
    if title is not None:
        plt.title(title)
    plt_filename_root, _ = os.path.splitext(plt_filename)
    plt_filename = os.path.join(out_directory, plt_filename)
    plt.savefig(plt_filename)
    log_plt_as_image(plt_filename_root, wandb_run=wandb_run)
    plt.close()

    save_df_with_plot(
        df,
        plt_filename,
        wandb_run=wandb_run,
        wandb_table_key=plt_filename_root + "_table",
    )

    print(f"Visualized hitting times located at {out_directory}")
//  [python]
def plot_cumulative_hit_times(
    hitting_time_vecs: Union[np.ndarray, List[np.ndarray]],
    names: List[Union[str, int, float]],
    max_episode_steps: int,
    out_directory: str,
    plt_filename: str,
    name_key: str = "Legend",
    title: Optional[str] = None,
    wandb_run: Optional[Run] = None,
) -> None:
    """Create a plot of the cumulative hitting times."""
    df = pd.DataFrame()
    for name, hitting_times in zip(names, hitting_time_vecs):
        times, accumulated_probs = calculate_cdf_curve(hitting_times, max_episode_steps)
        if times[-1] != max_episode_steps - 1:
            times = np.concatenate((times, [max_episode_steps - 1]), axis=0)
            accumulated_probs = np.concatenate(
                (accumulated_probs, [accumulated_probs[-1]]),
                axis=0,
            )
        d = pd.DataFrame(
            {
                name_key: name,
                "Hitting Time": times,
                "Cumulative Probability": accumulated_probs,
            },
        )
        df = df.append(d, ignore_index=True)

    sns.lineplot(
        x="Hitting Time",
        y="Cumulative Probability",
        hue=name_key,
        style=name_key,
        data=df,
        palette="viridis",
        n_boot=10000,
    )
    plt.grid()
    if title is not None:
        plt.title(title)
    plt_filename_root, _ = os.path.splitext(plt_filename)
    plt_filename = os.path.join(out_directory, plt_filename)
    plt.savefig(plt_filename)
    log_plt_as_image(plt_filename_root, wandb_run=wandb_run)
    plt.close()

    save_df_with_plot(
        df,
        plt_filename,
        wandb_run=wandb_run,
        wandb_table_key=plt_filename_root + "_table",
    )

    print(f"Visualized hitting times located at {out_directory}")
//  [python]
def gen_cutoff_plot(df_image, eps=0.0, ood=False, plot=True):
    print(f"Generating cutoff plot with eps={eps}, ood={ood}")

    df = df_image[(df_image["Epsilon"]==eps) & (df_image["OOD"]==ood)]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma"])

    df_cutoff = pd.DataFrame(
        columns=["Method", "Model Path", "Percentile", "Error"])

    for method, model_path_list in trained_models.items():
        for model_i, model_path in enumerate(tqdm(model_path_list)):

            df_model = df_pixel[(df_pixel["Method"]==method.value) & (df_pixel["Model Path"]==model_path)]
            df_model = df_model.sort_values("Sigma", ascending=False)
            percentiles = np.arange(100)/100.
            cutoff_inds = (percentiles * df_model.shape[0]).astype(int)

            df_model["Error"] = np.abs(df_model["Mu"] - df_model["Target"])
            mean_error = [df_model[cutoff:]["Error"].mean()
                for cutoff in cutoff_inds]
            df_single_cutoff = pd.DataFrame({'Method': method.value, 'Model Path': model_path,
                'Percentile': percentiles, 'Error': mean_error})

            df_cutoff = df_cutoff.append(df_single_cutoff)

    df_cutoff["Epsilon"] = eps

    if plot:
        print("Plotting cutoffs")
        sns.pointplot(x="Percentile", y="Error", hue="Method", data=df_cutoff)
        plt.savefig(os.path.join(output_dir, f"cutoff_eps-{eps}_ood-{ood}.pdf"))
        plt.show()

        sns.pointplot(x="Percentile", y="Error", hue="Model Path", style="Method", data=df_cutoff)
        plt.savefig(os.path.join(output_dir, f"cutoff_eps-{eps}_ood-{ood}_trial.pdf"))
        plt.show()

        g = sns.FacetGrid(df_cutoff, col="Method", legend_out=False)
        g = g.map_dataframe(sns.pointplot, x="Percentile", y="Error", hue="Model Path")#.add_legend()
        plt.savefig(os.path.join(output_dir, f"cutoff_eps-{eps}_ood-{ood}_trial_panel.pdf"))
        plt.show()


    return df_cutoff
//  [python]
def gen_cutoff_plot(df_image, eps=0.0, ood=False, plot=True):
    print(f"Generating cutoff plot with eps={eps}, ood={ood}")

    df = df_image[(df_image["Epsilon"]==eps) & (df_image["OOD"]==ood)]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma"])

    df_cutoff = pd.DataFrame(
        columns=["Method", "Model Path", "Percentile", "Error"])

    for method, model_path_list in trained_models.items():
        for model_i, model_path in enumerate(tqdm(model_path_list)):

            df_model = df_pixel[(df_pixel["Method"]==method.value) & (df_pixel["Model Path"]==model_path)]
            df_model = df_model.sort_values("Sigma", ascending=False)
            percentiles = np.arange(100)/100.
            cutoff_inds = (percentiles * df_model.shape[0]).astype(int)

            df_model["Error"] = np.abs(df_model["Mu"] - df_model["Target"])
            mean_error = [df_model[cutoff:]["Error"].mean()
                for cutoff in cutoff_inds]
            df_single_cutoff = pd.DataFrame({'Method': method.value, 'Model Path': model_path,
                'Percentile': percentiles, 'Error': mean_error})

            df_cutoff = df_cutoff.append(df_single_cutoff)

    df_cutoff["Epsilon"] = eps

    if plot:
        print("Plotting cutoffs")
        sns.lineplot(x="Percentile", y="Error", hue="Method", data=df_cutoff)
        plt.savefig(os.path.join(output_dir, f"cutoff_eps-{eps}_ood-{ood}.pdf"))
        plt.show()

        sns.lineplot(x="Percentile", y="Error", hue="Model Path", style="Method", data=df_cutoff)
        plt.savefig(os.path.join(output_dir, f"cutoff_eps-{eps}_ood-{ood}_trial.pdf"))
        plt.show()

        g = sns.FacetGrid(df_cutoff, col="Method", legend_out=False)
        g = g.map_dataframe(sns.lineplot, x="Percentile", y="Error", hue="Model Path")#.add_legend()
        plt.savefig(os.path.join(output_dir, f"cutoff_eps-{eps}_ood-{ood}_trial_panel.pdf"))
        plt.show()


    return df_cutoff
//  [python]
def plot_loss(df):
    sns.set(color_codes=True)
    sns.set_context("notebook", font_scale=1.0)
    plt.figure(figsize=(15, 10))
    plt.subplot(2, 2, 1)
    plt.subplots_adjust(hspace=0.4)
    fig = sns.pointplot(
        x="iter_loss",
        y="best_hr",
        hue="model",
        data=df.round(4),
    )
    fig.set(ylabel="HR@10")
    fig.set(xlabel="BCE Loss")
    plt.subplot(2, 2, 2)
    fig = sns.pointplot(
        x="iter_loss",
        y="best_ndcg",
        hue="model",
        data=df.round(4),
    )
    fig.set(ylabel="NDCG@10")
    fig.set(xlabel="BCE Loss")
//  [python]
def plot_loss(df):
    sns.set(color_codes=True)
    sns.set_context("notebook", font_scale=1.0)
    plt.figure(figsize=(15, 10))
    plt.subplot(2, 2, 1)
    plt.subplots_adjust(hspace=0.4)
    fig = sns.lineplot(
        x="iter_loss",
        y="best_hr",
        hue="model",
        style="model",
        markers=True,
        markersize=10,
        linewidth=2,
        data=df.round(4),
    )
    fig.set(ylabel="HR@10")
    fig.set(xlabel="BCE Loss")
    plt.subplot(2, 2, 2)
    fig = sns.lineplot(
        x="iter_loss",
        y="best_ndcg",
        hue="model",
        style="model",
        markers=True,
        markersize=10,
        linewidth=2,
        data=df.round(4),
    )
    fig.set(ylabel="NDCG@10")
    fig.set(xlabel="BCE Loss")
//  [python]
def plot_emb(df):
    sns.set(color_codes=True)
    sns.set_context("notebook", font_scale=1.0)
    plt.figure(figsize=(15, 10))
    plt.subplot(2, 2, 1)
    plt.subplots_adjust(hspace=0.4)
    fig = sns.pointplot(
        x="n_emb",
        y="best_hr",
        hue="model",
        markers=["o", "x", "s", "d"],
        linestyles=["-", "--", "-.", ":"],
        data=df,
    )
    fig.set(ylabel="HR@10")
    fig.set(xlabel="Number of Embeddings")
    plt.xticks(df.n_emb.unique())
    plt.subplot(2, 2, 2)
    fig = sns.pointplot(
        x="n_emb",
        y="best_ndcg",
        hue="model",
        markers=["o", "x", "s", "d"],
        linestyles=["-", "--", "-.", ":"],
        data=df,
    )
    fig.set(ylabel="NDCG@10")
    fig.set(xlabel="Number of Embeddings")
    plt.xticks(df.n_emb.unique())
    plt.subplot(2, 2, 3)
    fig = sns.pointplot(
        x="n_emb",
        y="iter_loss",
        hue="model",
        markers=["o", "x", "s", "d"],
        linestyles=["-", "--", "-.", ":"],
        data=df,
    )
    fig.set(ylabel="BCELoss")
    fig.set(xlabel="Number of Embeddings")
    plt.xticks(df.n_emb.unique())
//  [python]
def plot_emb(df):
    sns.set(color_codes=True)
    sns.set_context("notebook", font_scale=1.0)
    plt.figure(figsize=(15, 10))
    plt.subplot(2, 2, 1)
    plt.subplots_adjust(hspace=0.4)
    fig = sns.lineplot(
        x="n_emb",
        y="best_hr",
        hue="model",
        style="model",
        markers=True,
        markersize=10,
        linewidth=2,
        data=df,
    )
    fig.set(ylabel="HR@10")
    fig.set(xlabel="Number of Embeddings")
    plt.xticks(df.n_emb.unique())
    plt.subplot(2, 2, 2)
    fig = sns.lineplot(
        x="n_emb",
        y="best_ndcg",
        hue="model",
        style="model",
        markers=True,
        markersize=10,
        linewidth=2,
        data=df,
    )
    fig.set(ylabel="NDCG@10")
    fig.set(xlabel="Number of Embeddings")
    plt.xticks(df.n_emb.unique())
    plt.subplot(2, 2, 3)
    fig = sns.lineplot(
        x="n_emb",
        y="iter_loss",
        hue="model",
        style="model",
        markers=True,
        markersize=10,
        linewidth=2,
        data=df,
    )
    fig.set(ylabel="BCELoss")
    fig.set(xlabel="Number of Embeddings")
    plt.xticks(df.n_emb.unique())
//  [python]
def plot_prices(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    ax.plot(df.index, df['liquid_exchange_prices'], label='Liquid Exchange Prices', linewidth=2)
    ax.plot(df.index, df['portfolio_prices'], label='Portfolio Prices', linewidth=2)
    customize_plot(ax, 'Prices Over Time', 'Index', 'Price')
//  [python]
def plot_prices(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    sns.lineplot(x=df.index, y='liquid_exchange_prices', data=df, label='Liquid Exchange Prices', ax=ax, linewidth=2)
    sns.lineplot(x=df.index, y='portfolio_prices', data=df, label='Portfolio Prices', ax=ax, linewidth=2)
    customize_plot(ax, 'Prices Over Time', 'Index', 'Price')
//  [python]
def gen_adv_plots(df_image, ood=False):
    print(f"Generating calibration plot with ood={ood}")
    df = df_image[df_image["OOD"]==ood]
    # df = df.iloc[::10]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma", "Epsilon"])
    df_pixel["Error"] = np.abs(df_pixel["Mu"] - df_pixel["Target"])
    df_pixel["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_pixel["Sigma"]**2))
    print (df_pixel[df_pixel["Method"]=="Laplace"].head() )
    df_pixel["Entropy"].mask(df_pixel["Method"] == "Laplace", np.log(2*df_pixel["Sigma"]*np.exp(1.)), inplace=True) #  entropy for laplace distirbution
    print (df_pixel[df_pixel["Method"]=="Laplace"].head() )

    ### Plot epsilon vs error per method
    df = df_pixel.groupby([df_pixel.index, "Method", "Model Path", "Epsilon"]).mean().reset_index()
    df_by_method = df_pixel.groupby(["Method", "Model Path", "Epsilon"]).mean().reset_index()
    sns.pointplot(x="Epsilon", y="Error", hue="Method", data=df_by_method)
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_error.pdf"))
    plt.show()

    ### Plot epsilon vs uncertainty per method
    sns.pointplot(x="Epsilon", y="Sigma", hue="Method", data=df_by_method)
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_sigma.pdf"))
    plt.show()
    # df_by_method["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_by_method["Sigma"]**2))
    # sns.pointplot(x="Epsilon", y="Entropy", hue="Method", data=df_by_method)
    # plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_entropy.pdf"))
    # plt.show()


    ### Plot entropy cdf for different epsilons
    df_cumdf = pd.DataFrame(columns=["Method", "Model Path", "Epsilon", "Entropy", "CDF"])
    unc_ = np.linspace(df["Entropy"].min(), df["Entropy"].max(), 100)

    for method in df["Method"].unique():
        for model_path in df["Model Path"].unique():
            for eps in df["Epsilon"].unique():
                df_subset = df[
                    (df["Method"]==method) &
                    (df["Model Path"]==model_path) &
                    (df["Epsilon"]==eps)]
                if len(df_subset) == 0:
                    continue
                unc = np.sort(df_subset["Entropy"])
                prob = np.linspace(0,1,unc.shape[0])
                f_cdf = scipy.interpolate.interp1d(unc, prob, fill_value=(0.,1.), bounds_error=False)
                prob_ = f_cdf(unc_)

                df_single = pd.DataFrame({'Method': method, 'Model Path': model_path,
                    'Epsilon': eps, "Entropy": unc_, 'CDF': prob_})
                df_cumdf = df_cumdf.append(df_single)

    g = sns.FacetGrid(df_cumdf, col="Method")
    g = g.map_dataframe(sns.pointplot, x="Entropy", y="CDF", hue="Epsilon", ci=None).add_legend()
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_cdf_method.pdf"))
    plt.show()
//  [python]
def gen_adv_plots(df_image, ood=False):
    print(f"Generating calibration plot with ood={ood}")
    df = df_image[df_image["OOD"]==ood]
    # df = df.iloc[::10]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma", "Epsilon"])
    df_pixel["Error"] = np.abs(df_pixel["Mu"] - df_pixel["Target"])
    df_pixel["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_pixel["Sigma"]**2))
    print (df_pixel[df_pixel["Method"]=="Laplace"].head() )
    df_pixel["Entropy"].mask(df_pixel["Method"] == "Laplace", np.log(2*df_pixel["Sigma"]*np.exp(1.)), inplace=True) #  entropy for laplace distirbution
    print (df_pixel[df_pixel["Method"]=="Laplace"].head() )

    ### Plot epsilon vs error per method
    df = df_pixel.groupby([df_pixel.index, "Method", "Model Path", "Epsilon"]).mean().reset_index()
    df_by_method = df_pixel.groupby(["Method", "Model Path", "Epsilon"]).mean().reset_index()
    sns.lineplot(x="Epsilon", y="Error", hue="Method", data=df_by_method)
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_error.pdf"))
    plt.show()

    ### Plot epsilon vs uncertainty per method
    sns.lineplot(x="Epsilon", y="Sigma", hue="Method", data=df_by_method)
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_sigma.pdf"))
    plt.show()
    # df_by_method["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_by_method["Sigma"]**2))
    # sns.lineplot(x="Epsilon", y="Entropy", hue="Method", data=df_by_method)
    # plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_entropy.pdf"))
    # plt.show()


    ### Plot entropy cdf for different epsilons
    df_cumdf = pd.DataFrame(columns=["Method", "Model Path", "Epsilon", "Entropy", "CDF"])
    unc_ = np.linspace(df["Entropy"].min(), df["Entropy"].max(), 100)

    for method in df["Method"].unique():
        for model_path in df["Model Path"].unique():
            for eps in df["Epsilon"].unique():
                df_subset = df[
                    (df["Method"]==method) &
                    (df["Model Path"]==model_path) &
                    (df["Epsilon"]==eps)]
                if len(df_subset) == 0:
                    continue
                unc = np.sort(df_subset["Entropy"])
                prob = np.linspace(0,1,unc.shape[0])
                f_cdf = scipy.interpolate.interp1d(unc, prob, fill_value=(0.,1.), bounds_error=False)
                prob_ = f_cdf(unc_)

                df_single = pd.DataFrame({'Method': method, 'Model Path': model_path,
                    'Epsilon': eps, "Entropy": unc_, 'CDF': prob_})
                df_cumdf = df_cumdf.append(df_single)

    g = sns.FacetGrid(df_cumdf, col="Method")
    g = g.map_dataframe(sns.lineplot, x="Entropy", y="CDF", hue="Epsilon", ci=None).add_legend()
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_cdf_method.pdf"))
    plt.show()
//  [python]
def plot_results(
    model, X_valid, Y_valid, X_train, X_labeled, Y_labeled, batch_size, dims
):

    # get embedding
    nbatches = np.floor(len(X_valid) / batch_size).astype("int")
    valid_x = (
        X_valid[: nbatches * batch_size].reshape([nbatches, batch_size] + list([dims]))
    ).astype("float32")

    embedding_train = np.vstack(
        model.project_epoch(tf.data.Dataset.from_tensor_slices((valid_x)))
    )
    embedding = embedding_train
    projection_type = "training_data"
    fig, axs = plt.subplots(ncols=3, nrows=2, figsize=(20, 10))

    # plot embedding
    ax = axs.flatten()[0]
    ax.scatter(
        embedding[:, 0],
        embedding[:, 1],
        c=Y_valid.astype(int)[: len(embedding)],
        cmap="coolwarm",  # "tab10"
        s=2,
        alpha=0.25,
        rasterized=True,
    )
    ax.set_title(projection_type, fontsize=24)
    ax.axis("equal")

    # plot loss
    for i, element in zip(
        range(1, 5),
        ["attraction_loss", "repellant_loss", "umap_loss", "classifier_loss"],
    ):
        ax = axs.flatten()[i]

        sns.pointplot(
            x="epoch", y=element, hue="type_", data=model.loss_df, ax=ax, ci=None
        )
        ax.set_title(element, fontsize=24)
        ax.set_yscale("log")
    ax = get_decision_contour(
        axs.flatten()[5], model, X_valid, X_train, X_labeled, Y_labeled
    )
    plt.show()
//  [python]
def plot_results(
    model, X_valid, Y_valid, X_train, X_labeled, Y_labeled, batch_size, dims
):

    # get embedding
    nbatches = np.floor(len(X_valid) / batch_size).astype("int")
    valid_x = (
        X_valid[: nbatches * batch_size].reshape([nbatches, batch_size] + list([dims]))
    ).astype("float32")

    embedding_train = np.vstack(
        model.project_epoch(tf.data.Dataset.from_tensor_slices((valid_x)))
    )
    embedding = embedding_train
    projection_type = "training_data"
    fig, axs = plt.subplots(ncols=3, nrows=2, figsize=(20, 10))

    # plot embedding
    ax = axs.flatten()[0]
    ax.scatter(
        embedding[:, 0],
        embedding[:, 1],
        c=Y_valid.astype(int)[: len(embedding)],
        cmap="coolwarm",  # "tab10"
        s=2,
        alpha=0.25,
        rasterized=True,
    )
    ax.set_title(projection_type, fontsize=24)
    ax.axis("equal")

    # plot loss
    for i, element in zip(
        range(1, 5),
        ["attraction_loss", "repellant_loss", "umap_loss", "classifier_loss"],
    ):
        ax = axs.flatten()[i]

        sns.lineplot(
            x="epoch", y=element, hue="type_", data=model.loss_df, ax=ax, ci=None
        )
        ax.set_title(element, fontsize=24)
        ax.set_yscale("log")
    ax = get_decision_contour(
        axs.flatten()[5], model, X_valid, X_train, X_labeled, Y_labeled
    )
    plt.show()
//  [python]
def plot_prmsd(
    sequences,
    prmsd,
    out_file=None,
    shade_cdr=False,
    pdb_file=None,
):
    seq_dict = get_sequence_dict(sequences, None)
    delims = np.cumsum([len(s) for s in seq_dict.values()]).tolist()

    res_rmsd = prmsd.cpu().square().mean(dim=-1).sqrt().squeeze(0)
    chain_res_rmsd = np.split(res_rmsd, delims)

    if shade_cdr and exists(pdb_file):
        heavy_only = len(sequences) == 1 and "H" in sequences
        light_only = len(sequences) == 1 and "L" in sequences
        cdr_range_dict = get_cdr_range_dict(
            pdb_file,
            heavy_only=heavy_only,
            light_only=light_only,
            offset_heavy=False,
        )
        cdr_ranges = np.split(np.array(list(cdr_range_dict.values())), [3])

    plt.figure(figsize=(8, 4))
    for i, (chain, rmsd) in enumerate(zip(seq_dict.keys(), chain_res_rmsd)):
        plt.subplot(1, len(seq_dict), i + 1)

        res_nums = torch.arange(1, len(rmsd) + 1)
        plt.plot(res_nums, rmsd)

        if shade_cdr and exists(pdb_file):
            for r in cdr_ranges[i]:
                plt.axvspan(r[0], r[1], color="gray", alpha=0.5)

        plt.xlabel("Residue Number")
        plt.ylabel("Predicted RMSD (A)")
        plt.title(f"Chain {chain}")

    plt.tight_layout()

    if exists(out_file):
        plt.savefig(out_file, dpi=400)
//  [python]
def plot_prmsd(
    sequences,
    prmsd,
    out_file=None,
    shade_cdr=False,
    pdb_file=None,
):
    seq_dict = get_sequence_dict(sequences, None)
    delims = np.cumsum([len(s) for s in seq_dict.values()]).tolist()

    res_rmsd = prmsd.cpu().square().mean(dim=-1).sqrt().squeeze(0)
    chain_res_rmsd = np.split(res_rmsd, delims)

    if shade_cdr and exists(pdb_file):
        heavy_only = len(sequences) == 1 and "H" in sequences
        light_only = len(sequences) == 1 and "L" in sequences
        cdr_range_dict = get_cdr_range_dict(
            pdb_file,
            heavy_only=heavy_only,
            light_only=light_only,
            offset_heavy=False,
        )
        cdr_ranges = np.split(np.array(list(cdr_range_dict.values())), [3])

    plt.figure(figsize=(8, 4))
    for i, (chain, rmsd) in enumerate(zip(seq_dict.keys(), chain_res_rmsd)):
        plt.subplot(1, len(seq_dict), i + 1)

        res_nums = torch.arange(1, len(rmsd) + 1)
        sns.lineplot(x=res_nums, y=rmsd)

        if shade_cdr and exists(pdb_file):
            for r in cdr_ranges[i]:
                plt.axvspan(r[0], r[1], color="gray", alpha=0.5)

        plt.xlabel("Residue Number")
        plt.ylabel("Predicted RMSD (A)")
        plt.title(f"Chain {chain}")

    plt.tight_layout()

    if exists(out_file):
        plt.savefig(out_file, dpi=400)
//  [python]
def plot_arbitrageur_relative_balances(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    sns.lineplot(x=df.index, y='arbitrageur_relative_balances_x', data=df, label='Relative Arbitrageur Balances X', ax=ax, linewidth=2)
    sns.lineplot(x=df.index, y='arbitrageur_relative_balances_y', data=df, label='Relative Arbitrageur Balances Y', ax=ax, linewidth=2)
    customize_plot(ax, 'Arbitrageur Balances Over Time', 'Index', 'Balance in WAD')
//  [python]
def gen_adv_plots(df_image, ood=False):
    print(f"Generating calibration plot with ood={ood}")
    df = df_image[df_image["OOD"]==ood]
    # df = df.iloc[::10]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma", "Epsilon"])
    df_pixel["Error"] = np.abs(df_pixel["Mu"] - df_pixel["Target"])
    df_pixel["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_pixel["Sigma"]**2))

    ### Plot epsilon vs error per method
    df = df_pixel.groupby([df_pixel.index, "Method", "Model Path", "Epsilon"]).mean().reset_index()
    df_by_method = df_pixel.groupby(["Method", "Model Path", "Epsilon"]).mean().reset_index()
    sns.plt.plot(x="Epsilon", y="Error", data=df_by_method, hue="Method")
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_error.pdf"))
    plt.show()

    ### Plot epsilon vs uncertainty per method
    sns.plt.plot(x="Epsilon", y="Sigma", data=df_by_method, hue="Method")
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_sigma.pdf"))
    plt.show()
    # df_by_method["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_by_method["Sigma"]**2))
    # sns.plt.plot(x="Epsilon", y="Entropy", data=df_by_method, hue="Method")
    # plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_entropy.pdf"))
    # plt.show()


    ### Plot entropy cdf for different epsilons
    df_cumdf = pd.DataFrame(columns=["Method", "Model Path", "Epsilon", "Entropy", "CDF"])
    unc_ = np.linspace(df["Entropy"].min(), df["Entropy"].max(), 100)

    for method in df["Method"].unique():
        for model_path in df["Model Path"].unique():
            for eps in df["Epsilon"].unique():
                df_subset = df[
                    (df["Method"]==method) &
                    (df["Model Path"]==model_path) &
                    (df["Epsilon"]==eps)]
                if len(df_subset) == 0:
                    continue
                unc = np.sort(df_subset["Entropy"])
                prob = np.linspace(0,1,unc.shape[0])
                f_cdf = scipy.interpolate.interp1d(unc, prob, fill_value=(0.,1.), bounds_error=False)
                prob_ = f_cdf(unc_)

                df_single = pd.DataFrame({'Method': method, 'Model Path': model_path,
                    'Epsilon': eps, "Entropy": unc_, 'CDF': prob_})
                df_cumdf = df_cumdf.append(df_single)

    g = sns.FacetGrid(df_cumdf, col="Method")
    g = g.map_dataframe(sns.plt.plot, x="Entropy", y="CDF", hue="Epsilon", ci=None).add_legend()
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_cdf_method.pdf"))
    plt.show()
//  [python]
def gen_adv_plots(df_image, ood=False):
    print(f"Generating calibration plot with ood={ood}")
    df = df_image[df_image["OOD"]==ood]
    # df = df.iloc[::10]
    df_pixel = df_image_to_pixels(df, keys=["Target", "Mu", "Sigma", "Epsilon"])
    df_pixel["Error"] = np.abs(df_pixel["Mu"] - df_pixel["Target"])
    df_pixel["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_pixel["Sigma"]**2))

    ### Plot epsilon vs error per method
    df = df_pixel.groupby([df_pixel.index, "Method", "Model Path", "Epsilon"]).mean().reset_index()
    df_by_method = df_pixel.groupby(["Method", "Model Path", "Epsilon"]).mean().reset_index()
    sns.lineplot(x="Epsilon", y="Error", hue="Method", data=df_by_method)
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_error.pdf"))
    plt.show()

    ### Plot epsilon vs uncertainty per method
    sns.lineplot(x="Epsilon", y="Sigma", hue="Method", data=df_by_method)
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_sigma.pdf"))
    plt.show()
    # df_by_method["Entropy"] = 0.5*np.log(2*np.pi*np.exp(1.)*(df_by_method["Sigma"]**2))
    # sns.lineplot(x="Epsilon", y="Entropy", hue="Method", data=df_by_method)
    # plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_method_entropy.pdf"))
    # plt.show()


    ### Plot entropy cdf for different epsilons
    df_cumdf = pd.DataFrame(columns=["Method", "Model Path", "Epsilon", "Entropy", "CDF"])
    unc_ = np.linspace(df["Entropy"].min(), df["Entropy"].max(), 100)

    for method in df["Method"].unique():
        for model_path in df["Model Path"].unique():
            for eps in df["Epsilon"].unique():
                df_subset = df[
                    (df["Method"]==method) &
                    (df["Model Path"]==model_path) &
                    (df["Epsilon"]==eps)]
                if len(df_subset) == 0:
                    continue
                unc = np.sort(df_subset["Entropy"])
                prob = np.linspace(0,1,unc.shape[0])
                f_cdf = scipy.interpolate.interp1d(unc, prob, fill_value=(0.,1.), bounds_error=False)
                prob_ = f_cdf(unc_)

                df_single = pd.DataFrame({'Method': method, 'Model Path': model_path,
                    'Epsilon': eps, "Entropy": unc_, 'CDF': prob_})
                df_cumdf = df_cumdf.append(df_single)

    g = sns.FacetGrid(df_cumdf, col="Method")
    g = g.map_dataframe(sns.lineplot, x="Entropy", y="CDF", hue="Epsilon", ci=None).add_legend()
    plt.savefig(os.path.join(output_dir, f"adv_ood-{ood}_cdf_method.pdf"))
    plt.show()
//  [python]
def test_extremum_labels():
    data = [10, 30, 50, 70, 90, 70, 50, 30, 9]
    data = [10, 40, 30, 70, 90, 50, 60, 30, 9]
    sr = pd.Series(data)
    sr = pd.Series(data * 2)
    level_frac = 0.5
    tolerance_frac = 0.1

    maximums = find_all_extremums(sr, True, level_frac, tolerance_frac)
    minimums = find_all_extremums(sr, False, level_frac, tolerance_frac)

    # Merge into a sequence of interleaving minimums and maximums
    all = list()
    all.extend(maximums)
    all.extend(minimums)

    all.sort(key=lambda x: x[1])

    # We have indexes (x coordinates) and need to find their y values
    extr_x = [x[1] for x in all]
    extr_y = [sr[x] for x in extr_x]
    extr_df = pd.DataFrame({'x': extr_x, 'y': extr_y})

    import matplotlib.pyplot as plt
    # https://matplotlib.org/stable/api/markers_api.html
    # '-', '--', '-.', ':', 'None', ' ', '', 'solid', 'dashed', 'dashdot', 'dotted'
    plt.plot(sr)
    plt.plot(extr_df['x'], extr_df['y'], marker="o", markersize=10, linestyle='dotted')  # "^" 'v'

    pass
//  [python]
def test_extremum_labels():
    data = [10, 30, 50, 70, 90, 70, 50, 30, 9]
    data = [10, 40, 30, 70, 90, 50, 60, 30, 9]
    sr = pd.Series(data)
    sr = pd.Series(data * 2)
    level_frac = 0.5
    tolerance_frac = 0.1

    maximums = find_all_extremums(sr, True, level_frac, tolerance_frac)
    minimums = find_all_extremums(sr, False, level_frac, tolerance_frac)

    # Merge into a sequence of interleaving minimums and maximums
    all = list()
    all.extend(maximums)
    all.extend(minimums)

    all.sort(key=lambda x: x[1])

    # We have indexes (x coordinates) and need to find their y values
    extr_x = [x[1] for x in all]
    extr_y = [sr[x] for x in extr_x]
    extr_df = pd.DataFrame({'x': extr_x, 'y': extr_y})

    import seaborn as sns
    # https://matplotlib.org/stable/api/markers_api.html
    # '-', '--', '-.', ':', 'None', ' ', '', 'solid', 'dashed', 'dashdot', 'dotted'
    sns.lineplot(data=sr)
    sns.lineplot(data=extr_df, x="x", y="y", marker="o", markersize=10, linestyle='dotted')  # "^" 'v'

    pass
//  [python]
def plot_portfolio_reserves(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    ax.plot(df.index, df['portfolio_reserves_x'], label='Portfolio Reserves X', linewidth=2)
    ax.plot(df.index, df['portfolio_reserves_y'], label='Portfolio Reserves Y', linewidth=2)
    customize_plot(ax, 'Portfolio Reserves Over Time', 'Index', 'Reserve in WAD')
//  [python]
def plot_portfolio_reserves(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    sns.lineplot(x=df.index, y='portfolio_reserves_x', data=df, label='Portfolio Reserves X', ax=ax, linewidth=2)
    sns.lineplot(x=df.index, y='portfolio_reserves_y', data=df, label='Portfolio Reserves Y', ax=ax, linewidth=2)
    customize_plot(ax, 'Portfolio Reserves Over Time', 'Index', 'Reserve in WAD')
//  [python]
def plot_lp_portfolio_values(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    sns.tsplot(x=df.index, y='lp_portfolio_value', data=df, label='LP Portfolio Value', ax=ax, linewidth=2)
    customize_plot(ax, 'LP Portfolio Values Over Time', 'Index', 'Value in WAD')
//  [python]
def plot_lp_portfolio_values(ax, df, start_index=None, end_index=None):
    if start_index is not None and end_index is not None:
        df = df[start_index:end_index]
    sns.lineplot(x=df.index, y='lp_portfolio_value', data=df, label='LP Portfolio Value', ax=ax, linewidth=2)
    customize_plot(ax, 'LP Portfolio Values Over Time', 'Index', 'Value in WAD')