Source code for komanawa.basgra_nz_py.supporting_functions.plotting

"""
 Author: Matt Hanson
 Created: 1/09/2020 9:22 AM
 """
import matplotlib.pyplot as plt
import numpy as np
try:
    from matplotlib.cm import get_cmap
except ImportError:
    from matplotlib.pyplot import get_cmap
from matplotlib.patches import Patch
import pandas as pd
import os
from komanawa.basgra_nz_py.supporting_functions.output_metadata import get_output_metadata

_outvars = (
    'WAL',
    'WCLM',
    'WCL',
    'RAIN',
    'IRRIG',
    'DRAIN',
    'RUNOFF',
    'EVAP',
    'TRAN',
    'DM',
    'YIELD',
    'BASAL',
    'ROOTD',
    'WAFC',
    'IRR_TARG',
    'IRR_TRIG',
    'IRRIG_DEM',
    'RYE_YIELD',
    'WEED_YIELD',
    'DM_RYE_RM',
    'DM_WEED_RM',
)


[docs] def plot_multiple_results(data, outdir=None, out_vars=_outvars, fig_size=(10, 8), title_str='', rolling=None, main_kwargs={}, rolling_kwargs={}, label_rolling=False, label_main=True, show=True): """ plot multiple basgra results against each other :param data: dictionary of key: outputs of run_basgra() :param outdir: none or directory, if not None then makes outdir and saves plots :param out_vars: variables to make figures for, default is: ('WAL', 'WCLM', 'WCL', 'RAIN', 'IRRIG', 'DRAIN', 'RUNOFF', 'EVAP', 'TRAN', 'DM', 'YIELD', 'BASAL', 'ROOTD', 'WAFC') :param title_str: a string to append to the front of the title :param rolling: None or int, generate a rolling mean of rolling days :param main_kwargs: other kwargs passed directly to the plot function for the main plot :param rolling_kwargs: other kwargs passed directly to the plot function for the rolling average :param label_rolling: bool if True labels are created for the rolling plot if either is true then creates a legend :param label_main: bool if True labels are created for the main plot if either is true then creates a legend :param show: bool if true call plt.show before function return :return: axs: dict(data.keys():plt.ax) """ metadata = get_output_metadata() assert isinstance(data, dict) if outdir is not None: if not os.path.exists(outdir): os.makedirs(outdir) cmap = get_cmap('tab20') n_scens = len(data.keys()) colors = [cmap(e / n_scens) for e in range(n_scens)] # pick from color map axs = {} for v in out_vars: fig, ax = plt.subplots(figsize=fig_size) fig.autofmt_xdate() ax.set_title(title_str + ' ' + v) axs[v] = ax ax.set_xlabel('date') if v in metadata.keys(): ax.set_ylabel(metadata[v]['unit']) for i, (k, out) in enumerate(data.items()): for v in out_vars: ax = axs[v] if label_main: ax.plot(out.index, out[v], c=colors[i], label=k, **main_kwargs) else: ax.plot(out.index, out[v], c=colors[i], **main_kwargs) if rolling is not None: assert isinstance(rolling, int), 'rolling must be None or int' temp = out[v].rolling(rolling).mean() if label_rolling: ax.plot(out.index, temp, c=colors[i], label=k, **rolling_kwargs) else: ax.plot(out.index, temp, c=colors[i], **rolling_kwargs) for ax in axs.values(): if label_main or label_rolling: ax.legend() fig = ax.figure if outdir is not None: fig.savefig(os.path.join(outdir, '{}.png'.format(ax.title.get_text()))) if show: plt.show() return axs
[docs] def plot_multiple_monthly_violin_box(data, outdir=None, out_vars=_outvars, fig_size=(10, 8), title_str='', main_kwargs={}, label_main=True, show=True, violin_plot=False): """ plot multiple basgra results as a violin plot each other shifts january to be in the middle. :param data: dictionary of key: outputs of run_basgra() grouped by the month, index=range(1,13) :param outdir: none or directory, if not None then makes outdir and saves plots :param out_vars: variables to make figures for, default is: ('WAL', 'WCLM', 'WCL', 'RAIN', 'IRRIG', 'DRAIN', 'RUNOFF', 'EVAP', 'TRAN', 'DM', 'YIELD', 'BASAL', 'ROOTD', 'WAFC') :param title_str: a string to append to the front of the title :param main_kwargs: other kwargs passed directly to the plot function for the main plot :param label_main: bool if True labels are created for the main plot if either is true then creates a legend :param show: bool if true call plt.show before function return :param violin_plot: bool if True plot violin plots if False plot boxplots :return: axs: dict(data.keys():plt.ax) """ metadata = get_output_metadata() assert isinstance(data, dict) plot_months = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6] for k, v in data.items(): assert isinstance(v, pd.DataFrame), 'data items must be dataframes, instead was {}'.format(type(v)) v.loc[:, 'month'] = v.index.month if outdir is not None: if not os.path.exists(outdir): os.makedirs(outdir) cmap = get_cmap('tab20') n_scens = len(data.keys()) colors = [cmap(e / n_scens) for e in range(n_scens)] # pick from color map axs = {} for v in out_vars: fig, ax = plt.subplots(figsize=fig_size) fig.autofmt_xdate() ax.set_title(title_str + ' ' + v) axs[v] = ax ax.set_xlabel('month') if v in metadata.keys(): ax.set_ylabel(metadata[v]['unit']) handles = {e: [] for e in out_vars} labels = {e: [] for e in out_vars} initialpositions = np.arange(0, 12 * n_scens, n_scens) for i, (k, out) in enumerate(data.items()): use_positions = initialpositions + i + 0.5 for v in out_vars: c = colors[i] ax = axs[v] plot_data = [] for m in plot_months: plot_data.append(out.loc[out.month == m, v].values) if violin_plot: parts = ax.violinplot(plot_data, positions=use_positions, showmeans=False, showmedians=True, quantiles=[[0.25, 0.75] for e in plot_data], **main_kwargs) for pc in parts['bodies']: pc.set_facecolor(c) parts['cmedians'].set_color(c) parts['cquantiles'].set_color(c) parts['cmins'].set_color(c) parts['cmaxes'].set_color(c) parts['cbars'].set_color(c) else: bp = ax.boxplot(plot_data, positions=use_positions, patch_artist=True, **main_kwargs) for element in ['boxes', 'whiskers', 'means', 'medians', 'caps']: plt.setp(bp[element], color='k') plt.setp(bp['fliers'], markeredgecolor=c) for patch in bp['boxes']: patch.set(facecolor=c) if label_main: labels[v].append(k) handles[v].append(Patch(facecolor=c)) ax.legend(handles=handles[v], labels=labels[v]) for k, ax in axs.items(): ax.set_xticks(initialpositions + n_scens // 2) ax.set_xticklabels(['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']) # set verticle lines for i in np.concatenate((initialpositions, initialpositions + n_scens)): ax.axvline(x=i, ymin=0, ymax=1, linestyle=':', color='k', alpha=0.5 ) fig = ax.figure if outdir is not None: fig.savefig(os.path.join(outdir, '{}.png'.format(ax.title.get_text()))) if show: plt.show() return axs
[docs] def plot_multiple_monthly_results(data, outdir=None, out_vars=_outvars, fig_size=(10, 8), title_str='', main_kwargs={}, label_main=True, show=True): """ plot multiple basgra results against each other shifts january to be in the middle. :param data: dictionary of key: outputs of run_basgra() grouped by the month, index=range(1,13) :param outdir: none or directory, if not None then makes outdir and saves plots :param out_vars: variables to make figures for, default is: ('WAL', 'WCLM', 'WCL', 'RAIN', 'IRRIG', 'DRAIN', 'RUNOFF', 'EVAP', 'TRAN', 'DM', 'YIELD','BASAL', 'ROOTD', 'WAFC') :param title_str: a string to append to the front of the title :param main_kwargs: other kwargs passed directly to the plot function for the main plot :param label_main: bool if True labels are created for the main plot if either is true then creates a legend :param show: bool if true call plt.show before function return :return: axs: dict(data.keys():plt.ax) """ metadata = get_output_metadata() assert isinstance(data, dict) for k, v in data.items(): assert isinstance(v, pd.DataFrame), 'data items must be dataframes, instead was {}'.format(type(v)) assert list(v.index) == list(range(1, 13)), 'data index must be range(1,13), instead was {}'.format(v.index) v.index = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6] v.sort_index(inplace=True) if outdir is not None: if not os.path.exists(outdir): os.makedirs(outdir) cmap = get_cmap('tab20') n_scens = len(data.keys()) colors = [cmap(e / n_scens) for e in range(n_scens)] # pick from color map axs = {} for v in out_vars: fig, ax = plt.subplots(figsize=fig_size) fig.autofmt_xdate() ax.set_title(title_str + ' ' + v) axs[v] = ax ax.set_xlabel('month') if v in metadata.keys(): ax.set_ylabel(metadata[v]['unit']) for i, (k, out) in enumerate(data.items()): for v in out_vars: ax = axs[v] if label_main: ax.plot(out.index, out[v], c=colors[i], label=k, **main_kwargs) else: ax.plot(out.index, out[v], c=colors[i], **main_kwargs) for ax in axs.values(): ax.set_xticks(range(1, 13)) ax.set_xticklabels(['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']) if label_main: ax.legend() fig = ax.figure if outdir is not None: fig.savefig(os.path.join(outdir, '{}.png'.format(ax.title.get_text()))) if show: plt.show() return axs
[docs] def plot_multiple_date_range(data, start_date, end_date, outdir=None, out_vars=_outvars, fig_size=(10, 8), title_str='', rolling=None, main_kwargs={}, rolling_kwargs={}, label_rolling=False, label_main=True, show=True): """ as per plot multiple results but for a specific date range. :param data: dictionary of key: outputs of run_basgra() :param start_date: the start date for the range to be plotted :param end_date: the end date for the rnage to be plotted :param outdir: none or directory, if not None then makes outdir and saves plots :param out_vars: variables to make figures for, default is: ('WAL', 'WCLM', 'WCL', 'RAIN', 'IRRIG', 'DRAIN', 'RUNOFF', 'EVAP', 'TRAN', 'DM', 'YIELD', 'BASAL', 'ROOTD', 'WAFC') :param title_str: a string to append to the front of the title :param rolling: None or int, generate a rolling mean of rolling days :param main_kwargs: other kwargs passed directly to the plot function for the main plot :param rolling_kwargs: other kwargs passed directly to the plot function for the rolling average :param label_rolling: bool if True labels are created for the rolling plot if either is true then creates a legend :param label_main: bool if True labels are created for the main plot if either is true then creates a legend :param show: bool if true call plt.show before function return :return: axs: dict(data.keys():plt.ax) """ assert isinstance(data, dict) use_data = {} start_date = pd.to_datetime(start_date) end_date = pd.to_datetime(end_date) for k, v in data.items(): use_data[k] = v.loc[(v.index >= start_date) & (v.index <= end_date)] use_title = f'{start_date.date().isoformat()}_{end_date.date().isoformat()} {title_str}' axs = plot_multiple_results(data=use_data, outdir=outdir, out_vars=out_vars, fig_size=fig_size, title_str=use_title, rolling=rolling, main_kwargs=main_kwargs, rolling_kwargs=rolling_kwargs, label_rolling=label_rolling, label_main=label_main, show=show, ) return axs