Source code for ctdcal.ctd_plots

import logging
from pathlib import Path

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
import pandas as pd

log = logging.getLogger(__name__)


def _apply_default_fmt(xlim, ylim, xlabel, ylabel, title, grid, fontsize=12):
    plt.xlim(xlim)
    plt.xticks(rotation=45)
    plt.ylim(ylim)
    plt.xlabel(xlabel, fontsize=fontsize)
    plt.ylabel(ylabel, fontsize=fontsize)
    plt.title(title, fontsize=fontsize)
    plt.grid(grid)
    plt.tight_layout()


def _save_fig(ax, f_out):
    if f_out is not None:
        f_parent = Path(f_out).parent
        if not f_parent.exists():
            log.info(f"Parent folder '{f_parent.as_posix()}' doesn't exist... creating")
            Path(f_out).parent.mkdir(parents=True)
        plt.savefig(f_out)
        plt.close()
    else:
        return ax


[docs]def residual_vs_pressure( param, ref, prs, stn=None, xlim=(-0.02, 0.02), ylim=(6000, 0), xlabel="Residual", ylabel="Pressure (dbar)", auto_title=True, grid=False, deep=False, f_out=None, ): """ Plot residuals (ref - param) as a function of pressure. Parameters ---------- param : pd.Series or array-like Input variable ref : pd.Series or array-like Reference variable prs : pd.Series or array-like Pressure variable stn : pd.Series or array-like, optional Station variable xlim : tuple, optional Lower and upper x-limits ylim : tuple, optional Lower and upper y-limits xlabel : str, optional Label for the x-axis ylabel : str, optional Label for the y-axis auto_title : bool, optional Generate title from input attributes (iff dtype is pd.Series) grid : bool, optional Overlay grid on figure deep : bool, optional Whether to plot only values >2000 dbar f_out : path-like, optional Path to save figure Returns ------- ax : matplotlib.axes, optional Formatted scatter plot (if f_out is not given) """ diff = ref - param deep_text = " (>2000 dbar)" if deep else "" if deep: deep_rows = prs > 2000 diff, prs, stn = diff[deep_rows], prs[deep_rows], stn[deep_rows] # initialize figure plt.figure(figsize=(7, 6)) ax = plt.axes() # color scatter by stations if given if stn is not None: idx, uniques = pd.factorize(stn) # find unique stations #s and index them sc = ax.scatter(diff, prs, c=idx, marker="+") cbar = plt.colorbar(sc, ax=ax, pad=0.1) # set cbar ticks to station names tick_inds = cbar.get_ticks().astype(int) cbar.ax.yaxis.set_major_locator(ticker.FixedLocator(tick_inds)) cbar.ax.set_yticklabels(uniques[tick_inds]) cbar.ax.set_title("Station") else: sc = ax.scatter(diff, prs, marker="+") # formatting title = None if auto_title: try: title = f"{ref.name}-{param.name}{deep_text} vs. {prs.name}" except AttributeError: log.warning( "Failed to set title from variable names (requires dtype pd.Series)" ) log.info("Set afterward using 'ax.set_title(\"title\")'") _apply_default_fmt(xlim, ylim, xlabel, ylabel, title, grid) # save to path or return axis return _save_fig(ax, f_out)
[docs]def residual_vs_station( param, ref, prs, stn, ylim=(-0.02, 0.02), xlabel="Station Number", ylabel="Residual", grid=False, deep=False, f_out=None, ): """ Plot residuals (ref - param) as a function of station number. Parameters ---------- param : pd.Series or array-like Input variable ref : pd.Series or array-like Reference variable prs : pd.Series or array-like Pressure variable stn : pd.Series or array-like, optional Station variable ylim : tuple, optional Lower and upper y-limits xlabel : str, optional Label for the x-axis ylabel : str, optional Label for the y-axis grid : bool, optional Overlay grid on figure deep : bool, optional Whether to plot only values >2000 dbar f_out : path-like, optional Path to save figure Returns ------- ax : matplotlib.axes, optional Formatted scatter plot (if f_out is not given) """ diff = ref - param deep_text = " (>2000 dbar)" if deep else "" if deep: deep_rows = prs > 2000 diff, prs, stn = diff[deep_rows], prs[deep_rows], stn[deep_rows] plt.figure(figsize=(7, 6)) ax = plt.axes() sc = ax.scatter(stn, diff, c=prs, marker="+") cbar = plt.colorbar(sc, pad=0.1) cbar.set_label("Pressure (dbar)") # formatting try: title = f"{ref.name}-{param.name}{deep_text} vs. {stn.name}" except AttributeError: title = None log.warning( "Failed to set title from variable names (requires dtype pd.Series)" ) log.info('Set afterward using \'ax.set_title("title")`') _apply_default_fmt(None, ylim, xlabel, ylabel, title, grid) # save to path or return axis return _save_fig(ax, f_out)
def _intermediate_residual_plot( diff, prs, stn, xlim=(-0.02, 0.02), xlabel="Residual", show_thresh=False, f_out=None, ): """ Internal function to make figures at intermediate processing stages for debugging. """ ax = residual_vs_pressure( 0, diff, prs, stn=stn, xlim=xlim, xlabel=xlabel, auto_title=False, grid=True ) if show_thresh: thresh = np.array([0.002, 0.005, 0.010, 0.020]) p_range = np.array([6000, 2000, 1000, 500]) thresh = np.append(thresh, thresh[-1]) # this should still work fine even when p_range = np.append(p_range, 0) # thresh/p_range are defined elsewhere plt.step(thresh, p_range, ":k") plt.step(-thresh, p_range, ":k") mean = np.round(np.nanmean(diff), 4) stdev = np.round(np.nanstd(diff), 4) ax.set_title(f"Mean: {mean} / Stdev: {stdev}") # save to path or return axis (primarily for testing) return _save_fig(ax, f_out) # TODO: more plots! what does ODV have? # section plots (w/ PyDiva) # parameter-parameter plots