Esempio n. 1
0
def plot_whpa(bel, base_dir):
    """
    Loads target pickle and plots all training WHPA
    :return:
    """

    h_training = bel.Y.reshape(bel.Y_shape)

    whpa_plot(
        whpa=h_training, highlight=True, halpha=0.5, lw=0.1, color="darkblue", alpha=0.5
    )

    h_pred = bel.Y_obs.reshape(bel.Y_shape)
    whpa_plot(
        whpa=h_pred,
        color="darkred",
        lw=1,
        alpha=1,
        annotation=[],
        xlabel="X(m)",
        ylabel="Y(m)",
        labelsize=11,
    )

    labels = ["Training", "Test"]
    legend = _proxy_annotate(annotation=[], loc=2, fz=14)
    _proxy_legend(
        legend1=legend,
        colors=["darkblue", "darkred"],
        labels=labels,
        fig_file=os.path.join(base_dir, "whpa_training.png"),
    )
Esempio n. 2
0
def curves_i(
    cols: list,
    tc: np.array,
    highlight: list = None,
    labelsize: float = 12,
    factor: float = 1,
    xlabel: str = None,
    ylabel: str = None,
    sdir: str = None,
    show: bool = False,
):
    """Shows every breakthrough individually for each observation point. Will
    produce n_well figures of n_sim curves each.

    :param cols: List of colors
    :param labelsize:
    :param factor:
    :param xlabel:
    :param ylabel:
    :param tc: Curves with shape (n_sim, n_wells, n_time_steps)
    :param highlight: list: List of indices of curves to highlight in the plot
    :param sdir: Directory in which to save figure
    :param show: Whether to show or not
    """
    if highlight is None:
        highlight = []
    title = "curves"
    n_sim, n_wels, nts = tc.shape
    for t in range(n_wels):
        for i in range(n_sim):
            if i in highlight:
                plt.plot(tc[i][t] * factor, color="k", linewidth=2, alpha=1)
            else:
                plt.plot(tc[i][t] * factor, color=cols[t], linewidth=0.2, alpha=0.5)
        colors = [cols[t], "k"]
        plt.grid(linewidth=0.3, alpha=0.4)
        plt.tick_params(labelsize=labelsize)
        # plt.title(f'Well {t + 1}')

        alphabet = string.ascii_uppercase
        legend_a = _proxy_annotate([f"{alphabet[t]}. Well {t + 1}"], fz=12, loc=2)

        labels = ["Training", "Test"]
        _proxy_legend(legend1=legend_a, colors=colors, labels=labels, loc=1)

        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        if sdir:
            utils.dirmaker(sdir)
            plt.savefig(jp(sdir, f"{title}_{t + 1}.png"), dpi=300, transparent=False)
            if show:
                plt.show()
                plt.close()
        elif show:
            plt.show()
            plt.close()
        plt.close()
Esempio n. 3
0
def plot_target(
    Y: np.array = None,
    Y_obs: np.array = None,
    root: str = None,
    base_dir: str = None,
    folder: str = None,
    annotation: list = None,
    show: bool = False,
):
    if root is None:
        root = ""
    if folder is None:
        folder = ""
    if annotation is None:
        annotation = ["A"]
    # Wells
    # WHP - h test + training
    fig_dir = jp(base_dir, root)
    ff = jp(fig_dir, "whpa.png")  # figure name
    Y = check_array(Y, allow_nd=True)
    try:
        Y_obs = check_array(Y_obs, allow_nd=True)
    except ValueError:
        Y_obs = check_array(Y_obs.to_numpy().reshape(1, -1))
    h_test = Y_obs.reshape((Setup.DataSet.Y_shape[1], Setup.DataSet.Y_shape[2]))
    h_training = Y.reshape((-1,) + (Setup.DataSet.Y_shape[1], Setup.DataSet.Y_shape[2]))
    # Plots target training + prediction
    whpa_plot(whpa=h_training, color="blue", alpha=0.5)
    whpa_plot(
        whpa=h_test,
        color="r",
        lw=2,
        alpha=0.8,
        title="Target training and test set",
        xlabel="X(m)",
        ylabel="Y(m)",
        labelsize=11,
    )
    colors = ["blue", "red"]
    labels = ["Training", "Test"]
    legend = _proxy_annotate(annotation=annotation, loc=2, fz=14)
    _proxy_legend(legend1=legend, colors=colors, labels=labels)

    plt.savefig(ff, bbox_inches="tight", dpi=300, transparent=False)
    if show:
        plt.show()
    plt.close()
Esempio n. 4
0
def whpa_plot(
    grf: float = None,
    well_comb: list = None,
    whpa: np.array = None,
    alpha: float = 0.4,
    halpha: float = None,
    lw: float = 0.5,
    bkg_field_array: np.array = None,
    vmin: float = None,
    vmax: float = None,
    x_lim: list = None,
    y_lim: list = None,
    xlabel: str = None,
    ylabel: str = None,
    cb_title: str = None,
    labelsize: float = 5,
    cmap: str = "coolwarm",
    color: str = "white",
    grid: bool = True,
    show_wells: bool = False,
    well_ids: list = None,
    title: str = None,
    annotation: list = None,
    fig_file: str = None,
    highlight: bool = False,
    show: bool = False,
):
    """Produces the WHPA plot, i.e. the zero-contour of the signed distance
    array.

    :param grid: Whether to plot the grid
    :param grf: Grid cell size
    :param well_comb: List of well combination
    :param highlight: Boolean to display lines on top of filling between contours or not.
    :param annotation: List of annotations (str)
    :param xlabel: X axis label
    :param ylabel: Y axis label
    :param cb_title: Colorbar title
    :param well_ids: List of well ids
    :param labelsize: Label size
    :param title: str: plot title
    :param show_wells: bool: whether to plot well coordinates or not
    :param cmap: str: colormap name for the background array
    :param vmax: float: max value to plot for the background array
    :param vmin: float: max value to plot for the background array
    :param bkg_field_array: np.array: 2D array whose values will be plotted on the grid
    :param whpa: np.array: Array containing grids of values whose 0 contour will be computed and plotted
    :param alpha: float: opacity of the 0 contour lines
    :param halpha: Alpha value for line plots if highlight is True
    :param lw: float: Line width
    :param color: str: Line color
    :param fig_file: str: File name to save the figure
    :param show: bool: Whether to show the figure or not
    :param x_lim: [x_min, x_max] For the figure
    :param y_lim: [y_min, y_max] For the figure
    """

    # Get basic settings
    focus = Setup.Focus
    wells = Setup.Wells

    if well_comb is not None:
        wells.combination = well_comb

    if y_lim is None:
        ylim = focus.y_range
    else:
        ylim = y_lim
    if x_lim is None:
        xlim = focus.x_range
    else:
        xlim = x_lim
    if grf is None:
        grf = focus.cell_dim
    else:
        grf = grf

    nrow, ncol, x, y = refine_machine(focus.x_range, focus.y_range, grf)

    # Plot background
    if bkg_field_array is not None:
        plt.imshow(
            bkg_field_array,
            extent=np.concatenate([focus.x_range, focus.y_range]),
            vmin=vmin,
            vmax=vmax,
            cmap=cmap,
        )
        cb = plt.colorbar(fraction=0.046, pad=0.04)
        cb.ax.set_title(cb_title)

    if halpha is None:
        halpha = alpha

    # Plot results
    if whpa is None:
        whpa = []

    if whpa.ndim > 2:  # New approach is to plot filled contours
        new_grf = 1  # Refine grid
        _, _, new_x, new_y = refine_machine(xlim, ylim, new_grf=new_grf)
        xys, nrow, ncol = grid_parameters(x_lim=xlim, y_lim=ylim, grf=new_grf)
        vertices = contours_vertices(x=x, y=y, arrays=whpa)
        b_low = binary_stack(xys=xys, nrow=nrow, ncol=ncol, vertices=vertices)
        contour = plt.contourf(
            new_x,
            new_y,
            1 - b_low,  # Trick to be able to fill contours
            # Use machine epsilon
            [np.finfo(float).eps, 1 - np.finfo(float).eps],
            colors=color,
            alpha=alpha,
        )
        if highlight:  # Also display curves
            for z in whpa:
                contour = plt.contour(
                    z,
                    [0],
                    extent=(xlim[0], xlim[1], ylim[0], ylim[1]),
                    colors=color,
                    linewidths=lw,
                    alpha=halpha,
                )

    else:  # If only one WHPA to display
        contour = plt.contour(
            whpa,
            [0],
            extent=np.concatenate([focus.x_range, focus.y_range]),
            colors=color,
            linewidths=lw,
            alpha=halpha,
        )

    # Grid
    if grid:
        plt.grid(color="c", linestyle="-", linewidth=0.5, alpha=0.2)

    # Plot wells
    well_legend = None
    if show_wells:
        plot_wells(wells, well_ids=well_ids, markersize=7)
        well_legend = plt.legend(fontsize=11)

    # Plot limits
    if x_lim is None:
        plt.xlim(xlim[0], xlim[1])
    else:
        plt.xlim(x_lim[0], x_lim[1])
    if y_lim is None:
        plt.ylim(ylim[0], ylim[1])
    else:
        plt.ylim(y_lim[0], y_lim[1])

    if title:
        plt.title(title)

    plt.xlabel(xlabel, fontsize=labelsize)
    plt.ylabel(ylabel, fontsize=labelsize)

    # Tick size
    plt.tick_params(labelsize=labelsize)

    if annotation:
        legend = _proxy_annotate(annotation=annotation, fz=14, loc=2)
        plt.gca().add_artist(legend)

    if fig_file:
        utils.dirmaker(os.path.dirname(fig_file))
        plt.savefig(fig_file, bbox_inches="tight", dpi=300, transparent=False)
        plt.close()
    elif show:
        plt.show()
        plt.close()

    return contour, well_legend
Esempio n. 5
0
def mode_histo(
    colors: list,
    an_i: int,
    wm: np.array,
    title: str = None,
    fig_name: str = "average",
    directory: str = None,
):
    """

    :param directory: Path to the directory where the figure will be saved.
    :param title: Title of the figure.
    :param colors: List of colors to use for the histogram.
    :param an_i: Figure annotation
    :param wm: Arrays of metric
    :param fig_name: Name of the figure.
    :return:
    """
    if directory is None:
        directory = Setup.Directories.forecasts_dir
    alphabet = string.ascii_uppercase
    wid = list(map(str, Setup.Wells.combination))  # Wel identifiers (n)

    pipeline = Pipeline(
        [
            ("s_scaler", StandardScaler()),
        ]
    )
    wm = pipeline.fit_transform(wm)

    modes = []  # Get MHD corresponding to each well's mode
    for i, m in enumerate(wm):  # For each well, look up its MHD distribution
        count, values = np.histogram(m, bins="fd")
        # (Freedman Diaconis Estimator)
        # Robust (resilient to outliers) estimator that takes into account data variability and data size.
        # https://numpy.org/doc/stable/reference/generated/numpy.histogram_bin_edges.html#numpy.histogram_bin_edges
        idm = np.argmax(count)
        mode = values[idm]
        modes.append(mode)

    modes = np.array(modes)  # Scale modes
    modes -= np.mean(modes)

    # Bar plot
    plt.bar(np.arange(1, 7), -modes, color=colors)
    # plt.title("Amount of information of each well")
    plt.title(f"{fig_name}")
    plt.xlabel("Well ID")
    plt.ylabel("Opposite deviation from mode's mean")
    plt.grid(color="#95a5a6", linestyle="-", linewidth=0.5, axis="y", alpha=0.7)

    legend_a = _proxy_annotate(annotation=[alphabet[an_i + 1]], loc=2, fz=14)
    plt.gca().add_artist(legend_a)

    plt.savefig(
        os.path.join(directory, f"{fig_name}_well_mode.png"),
        dpi=300,
        transparent=False,
    )
    plt.close()

    # Plot BOX
    columns = ["1", "2", "3", "4", "5", "6"]
    wmd = pd.DataFrame(columns=columns, data=wm.T)
    palette = {columns[i]: colors[i] for i in range(len(columns))}
    # palette = {'b', 'g', 'r', 'c', 'm', 'y'}
    fig, ax1 = plt.subplots()
    sns.boxplot(data=wmd, palette=palette, order=columns, linewidth=1, ax=ax1)
    [line.set_color("white") for line in ax1.get_lines()[4::6]]
    plt.ylim([-2.5, 3])
    plt.xlabel("Well ID")
    plt.ylabel("Metric value")
    if title is None:
        title = "Box-plot of the metric values for each data source"
    plt.title(title)
    plt.grid(color="saddlebrown", linestyle="--", linewidth=0.7, axis="y", alpha=0.5)

    try:
        an_i = int(directory.split("split")[-1])
    except ValueError:
        pass
    legend_a = _proxy_annotate(annotation=[alphabet[an_i]], loc=2, fz=14)
    plt.gca().add_artist(legend_a)

    legend_b = _proxy_annotate(annotation=[f"Fold {an_i + 1}"], loc=1, fz=14)
    plt.gca().add_artist(legend_b)

    plt.savefig(
        os.path.join(directory, f"{fig_name}_well_box.png"),
        dpi=300,
        transparent=False,
    )
    plt.close()

    # Plot histogram
    for i, m in enumerate(wm):
        sns.kdeplot(m, color=f"{colors[i]}", shade=True, linewidth=2)
    # plt.title("Summed metric distribution for each well")
    plt.title(f"{fig_name}")
    plt.xlabel("Summed metric")
    plt.ylabel("KDE")
    legend_1 = plt.legend(wid, loc=2)
    plt.gca().add_artist(legend_1)
    plt.grid(alpha=0.2)

    legend_a = _proxy_annotate(annotation=[alphabet[an_i]], loc=2, fz=14)
    plt.gca().add_artist(legend_a)

    plt.savefig(
        os.path.join(directory, f"{fig_name}_hist.png"),
        dpi=300,
        transparent=False,
    )
    plt.close()
Esempio n. 6
0
def plot_results(
    bel,
    y_predicted: np.array = None,
    X: np.array = None,
    X_obs: np.array = None,
    Y: np.array = None,
    Y_obs: np.array = None,
    root: str = None,
    base_dir: str = None,
    folder: str = None,
    annotation: list = None,
    show: bool = False,
):
    """Plots forecasts results in the 'uq' folder.

    :param bel: BEL model
    :param y_predicted: Predicted values
    :param X: Training set
    :param X_obs: Observed training set
    :param Y: Test set
    :param Y_obs: Observed test set
    :param annotation: List of annotations
    :param X: Predictor array
    :param X_obs: "Observed" predictor array
    :param Y: Target array
    :param Y_obs: "True" target array
    :param root: str: Forward ID
    :param base_dir: str: Base directory
    :param folder: str: Well combination. '123456', '1'...
    :param show: bool: Show or not
    :return:
    """
    if root is None:
        root = ""
    if folder is None:
        folder = ""
    # Directory
    md = jp(base_dir, root, folder)
    # Wells
    wells = Setup.Wells
    wells_id = list(wells.wells_data.keys())
    cols = [wells.wells_data[w]["color"] for w in wells_id if "pumping" not in w]

    if X is not None:
        # Curves - d
        # Plot curves
        sdir = jp(md, "data")

        X = check_array(X, allow_nd=True)
        try:
            X_obs = check_array(X_obs, allow_nd=True)
        except ValueError:
            try:
                X_obs = check_array(X_obs.to_numpy().reshape(1, -1))
            except AttributeError:
                X_obs = check_array(X_obs.reshape(1, -1))

        tc = X.reshape((Setup.HyperParameters.n_posts,) + bel.X_shape)
        tcp = X_obs.reshape((-1,) + bel.X_shape)
        tc = np.concatenate((tc, tcp), axis=0)

        # Plot parameters for predictor
        xlabel = "Observation index number"
        ylabel = "Concentration ($g/m^{3})$"
        factor = 1000
        labelsize = 11

        curves(
            cols=cols,
            tc=tc,
            sdir=sdir,
            xlabel=xlabel,
            ylabel=ylabel,
            factor=factor,
            labelsize=labelsize,
            highlight=[len(tc) - 1],
            show=show,
        )

        curves(
            cols=cols,
            tc=tc,
            sdir=sdir,
            xlabel=xlabel,
            ylabel=ylabel,
            factor=factor,
            labelsize=labelsize,
            highlight=[len(tc) - 1],
            ghost=True,
            title="curves_ghost",
            show=show,
        )

        curves_i(
            cols=cols,
            tc=tc,
            xlabel=xlabel,
            ylabel=ylabel,
            factor=factor,
            labelsize=labelsize,
            sdir=sdir,
            highlight=[len(tc) - 1],
            show=show,
        )

    if Y is not None:
        # WHP - h test + training
        fig_dir = jp(base_dir, root)
        ff = jp(fig_dir, "whpa.png")  # figure name
        Y = check_array(Y, allow_nd=True)
        try:
            Y_obs = check_array(Y_obs, allow_nd=True)
        except ValueError:
            Y_obs = check_array(Y_obs.to_numpy().reshape(1, -1))
        h_test = Y_obs.reshape((bel.Y_shape[0], bel.Y_shape[1]))
        h_training = Y.reshape((-1,) + (bel.Y_shape[0], bel.Y_shape[1]))
        # Plots target training + prediction
        whpa_plot(whpa=h_training, color="blue", alpha=0.5)
        whpa_plot(
            whpa=h_test,
            color="r",
            lw=2,
            alpha=0.8,
            xlabel="X(m)",
            ylabel="Y(m)",
            labelsize=11,
        )
        colors = ["blue", "red"]
        labels = ["Training", "Test"]
        legend = _proxy_annotate(annotation=[], loc=2, fz=14)
        _proxy_legend(legend1=legend, colors=colors, labels=labels, fig_file=ff)
        if show:
            plt.show()
        plt.close()

        # WHPs
        ff = jp(md, "uq", f"{root}_cca_{bel.cca.n_components}.png")
        forecast_posterior = y_predicted.reshape(
            (-1,) + (bel.Y_shape[0], bel.Y_shape[1])
        )

        # I display here the prior h behind the forecasts sampled from the posterior.
        well_ids = [0] + list(map(int, list(folder)))
        labels = ["Training", "Samples", "True test"]
        colors = ["darkblue", "darkred", "k"]

        # Training
        _, well_legend = whpa_plot(
            whpa=h_training,
            alpha=0.5,
            lw=0.5,
            color=colors[0],
            show_wells=True,
            well_ids=well_ids,
            show=False,
        )

        # Samples
        whpa_plot(
            whpa=forecast_posterior,
            color=colors[1],
            lw=1,
            alpha=0.8,
            highlight=True,
            show=False,
        )

        # True test
        whpa_plot(
            whpa=h_test,
            color=colors[2],
            lw=0.8,
            alpha=1,
            x_lim=[800, 1200],
            xlabel="X(m)",
            ylabel="Y(m)",
            labelsize=11,
            show=False,
        )

        # Other tricky operation to add annotation
        legend_an = _proxy_annotate(annotation=annotation, loc=2, fz=14)

        # Tricky operation to add a second legend:
        _proxy_legend(
            legend1=well_legend,
            extra=[legend_an],
            colors=colors,
            labels=labels,
            fig_file=ff,
        )
        if show:
            plt.show()
        plt.close()
Esempio n. 7
0
def plot_posterior(
    forecast_posterior: np.array = None,
    Y: np.array = None,
    Y_obs: np.array = None,
    root: str = None,
    base_dir: str = None,
    folder: str = None,
    annotation: list = None,
    show: bool = False,
):
    if root is None:
        root = ""
    if folder is None:
        folder = ""
    if annotation is None:
        annotation = ["A"]
    # I display here the prior h behind the forecasts sampled from the posterior.
    well_ids = [0, 1, 2, 3, 4, 5, 6]
    labels = ["Training", "Samples", "True test"]
    colors = ["darkblue", "darkred", "k"]
    Y_obs = Y_obs.to_numpy().reshape(
        (Setup.DataSet.Y_shape[1], Setup.DataSet.Y_shape[2])
    )
    Y = Y.to_numpy().reshape(
        (-1,) + (Setup.DataSet.Y_shape[1], Setup.DataSet.Y_shape[2])
    )
    # Training
    _, well_legend = whpa_plot(
        whpa=Y,
        alpha=0.5,
        lw=0.5,
        color=colors[0],
        show_wells=True,
        well_ids=well_ids,
        show=False,
    )

    # Samples
    whpa_plot(
        whpa=forecast_posterior,
        color=colors[1],
        lw=1,
        alpha=0.8,
        highlight=True,
        show=False,
    )

    # True test
    whpa_plot(
        whpa=Y_obs,
        color=colors[2],
        lw=0.8,
        alpha=1,
        x_lim=[800, 1200],
        xlabel="X(m)",
        ylabel="Y(m)",
        labelsize=11,
        show=False,
    )

    # Other tricky operation to add annotation
    legend_an = _proxy_annotate(annotation=annotation, loc=2, fz=14)

    # Tricky operation to add a second legend:
    md = jp(base_dir, root, folder)
    ff = jp(md, "uq", f"{root}_cca_{Setup.HyperParameters.n_cca}.png")

    _proxy_legend(
        legend1=well_legend,
        extra=[legend_an],
        colors=colors,
        labels=labels,
    )
    plt.savefig(ff, bbox_inches="tight", dpi=300, transparent=False)
    if show:
        plt.show()
    plt.close()
Esempio n. 8
0
def h_pca_inverse_plot(bel, fig_dir: str = None, show: bool = False):
    """Plot used to compare the reproduction of the original physical space
    after PCA transformation.

    :param bel: BEL model
    :param fig_dir: str: directory where to save the figure
    :param show: bool: if True, show the figure
    :return:
    """

    shape = bel.Y_shape

    if bel.Y_obs_pc is not None:
        v_pc = check_array(bel.Y_obs_pc.reshape(1, -1))
    else:
        try:
            Y_obs = check_array(bel.Y_obs, allow_nd=True)
        except ValueError:
            Y_obs = check_array(bel.Y_obs.to_numpy().reshape(1, -1))

        v_pc = bel.Y_pre_processing.transform(Y_obs)[
            :, : Setup.HyperParameters.n_pc_target
        ]

    nc = bel.Y_pre_processing["pca"].n_components_
    dummy = np.zeros((1, nc))
    dummy[:, : v_pc.shape[1]] = v_pc
    v_pred = bel.Y_pre_processing.inverse_transform(dummy)

    h_to_plot = np.copy(Y_obs.reshape(1, shape[1], shape[2]))

    whpa_plot(whpa=h_to_plot, color="red", alpha=1, lw=2)

    whpa_plot(
        whpa=v_pred.reshape(1, shape[1], shape[2]),
        color="blue",
        alpha=1,
        lw=2,
        labelsize=11,
        xlabel="X(m)",
        ylabel="Y(m)",
        x_lim=[850, 1100],
        y_lim=[350, 650],
    )

    # Add title inside the box
    an = ["B"]

    legend_a = _proxy_annotate(annotation=an, loc=2, fz=14)

    _proxy_legend(
        legend1=legend_a,
        colors=["red", "blue"],
        labels=["Physical", "Back transformed"],
        marker=["-", "-"],
    )

    if fig_dir is not None:
        utils.dirmaker(fig_dir)
        plt.savefig(
            jp(fig_dir, f"h_pca_inverse_transform.png"), dpi=300, transparent=False
        )
        if show:
            plt.show()
            plt.close()
    elif show:
        plt.show()
        plt.close()
Esempio n. 9
0
def d_pca_inverse_plot(
    bel,
    root,
    factor: float = 1.0,
    xlabel: str = None,
    ylabel: str = None,
    labelsize: float = 11.0,
    fig_dir: str = None,
    show: bool = False,
):
    """Plot used to compare the reproduction of the original physical space
    after PCA transformation.

    :param bel: BEL model
    :param xlabel:
    :param ylabel:
    :param labelsize:
    :param factor:
    :param fig_dir: str:
    :param show: bool:
    :return:
    """

    shape = bel.X_shape
    v_pc = bel.X_obs_pc

    nc = bel.X_pre_processing["pca"].n_components_
    dummy = np.zeros((1, nc))
    dummy[:, : v_pc.shape[1]] = v_pc

    v_pred = bel.X_pre_processing.inverse_transform(dummy).reshape((-1,) + shape)
    to_plot = np.copy(bel.X_obs).reshape((-1,) + shape)

    cols = ["r" for _ in range(shape[1])]
    highlights = [i for i in range(shape[1])]
    curves(cols=cols, tc=to_plot, factor=factor, highlight=highlights, conc=True)

    cols = ["b" for _ in range(shape[1])]
    curves(cols=cols, tc=v_pred, factor=factor, highlight=highlights, conc=True)

    # Add title inside the box
    an = ["A"]
    legend_a = _proxy_annotate(annotation=an, loc=2, fz=14)
    _proxy_legend(
        legend1=legend_a,
        colors=["red", "blue"],
        labels=["Physical", "Back transformed"],
        marker=["-", "-"],
        loc=1,
    )
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.tick_params(labelsize=labelsize)

    # Increase y axis by a small percentage for annotation in upper left corner
    yrange = np.max(to_plot * factor) * 1.15
    plt.ylim([0, yrange])

    if fig_dir is not None:
        utils.dirmaker(fig_dir)
        plt.savefig(jp(fig_dir, f"{root}_d.png"), dpi=300, transparent=False)
        if show:
            plt.show()
            plt.close()
    elif show:
        plt.show()
        plt.close()