From d8b75aac3533c69dccb8de99fd63e5ff4a88443e Mon Sep 17 00:00:00 2001 From: "Matthew F. McEneaney" Date: Wed, 4 Feb 2026 18:05:12 -0500 Subject: [PATCH 1/4] feat: Update results and systematics plots to add a single plot label and to allow passing of title pad size and generic legend arguments. --- py/saga/plot.py | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/py/saga/plot.py b/py/saga/plot.py index 2cdd91f..7e3efed 100644 --- a/py/saga/plot.py +++ b/py/saga/plot.py @@ -704,7 +704,9 @@ def plot_systematics( watermark="CLAS12 Preliminary", watermark_kwargs=None, use_default_plt_settings=True, - legend_loc="upper left", + title_pad=20, + lg_kwargs={"loc":"upper left", "bbox_to_anchor":(1.05, 1.0), "frameon": False}, + plot_label_args=[0.95,0.15,None], axlinewidth=1.0, log=False, figsize=(16, 10), @@ -742,8 +744,12 @@ def plot_systematics( Optional key word arguments for :meth:`plot_watermark` use_default_plt_settings : bool, optional Option to use default font and tick parameter style settings - legend_loc : str, optional - Matplotlib.pyplot legend location string, will not be plotted if set to :obj:`None` or :obj:`''` + title_pad : int, optional + Title padding + lg_kwargs : dict, optional + Matplotlib.pyplot legend keyword arguments + plot_label_args : list, optional + List of positional arguments for plot label axlinewidth : float, optional Axis line and injected asymmetries line width log : bool, optional @@ -768,7 +774,7 @@ def plot_systematics( f1, ax1 = plt.subplots(figsize=figsize) plt.xlim(*xlims) plt.ylim(*ylims) - plt.title(title, usetex=True) + plt.title(title, usetex=True, pad=title_pad) plt.xlabel(xtitle, usetex=True) plt.ylabel(ytitle, usetex=True) @@ -797,8 +803,14 @@ def plot_systematics( plot_watermark(ax1, watermark=watermark, **watermark_kwargs) # Plot legend - if legend_loc is not None and legend_loc != "": - ax1.legend(loc=legend_loc) + if isinstance(legend_loc,dict): + ax1.legend(**lg_kwargs) + + # Plot label + if isinstance(plot_label_args,tuple) or isinstance(plot_label_args,list) \ + and len(plot_label_args)==3 and isinstance(plot_label_args[-1],str): + ax1.text(*plot_label_args, transform=ax1.transAxes, + fontsize=plt.rcParams['axes.titlesize'], fontweight='bold', va='top', ha='right') # Save figure f1.savefig(outpath) @@ -856,7 +868,9 @@ def plot_results( watermark="CLAS12 Preliminary", watermark_kwargs=None, show_injected_asymmetries=False, - legend_loc="upper left", + title_pad=20, + lg_kwargs={"loc":"best", "frameon": False}, + plot_label_args=[0.95,0.15,None], ecolor="black", elinewidth=2.0, capsize=18, @@ -956,8 +970,12 @@ def plot_results( Optional key word arguments for :meth:`plot_watermark` show_injected_asymmetries : bool, optional Option to show injected signal and background asymmetries - legend_loc : str, optional - Matplotlib.pyplot legend location string, will not be plotted if set to :obj:`None` or :obj:`''` + title_pad : int, optional + Title padding + lg_kwargs : dict, optional + Matplotlib.pyplot legend keyword arguments + plot_label_args : list, optional + List of positional arguments for plot label ecolor : str, optional Error line color ecolor : float, optional @@ -1093,7 +1111,7 @@ def plot_results( # Set up plot ax1.set_xlim(*xlims) ax1.set_ylim(*ylims) - ax1.set_title(title, usetex=True) + ax1.set_title(title, usetex=True, pad=title_pad) ax1.set_xlabel(xlabel, usetex=True) ax1.set_ylabel(ylabel, usetex=True) @@ -1207,8 +1225,14 @@ def plot_results( plot_watermark(ax1, watermark=watermark, **watermark_kwargs) # Plot legend - if legend_loc is not None and legend_loc != "": - ax1.legend(loc=legend_loc) + if isinstance(lg_kwargs,dict): + ax1.legend(**lg_kwargs) + + # Plot label + if isinstance(plot_label_args,tuple) or isinstance(plot_label_args,list) \ + and len(plot_label_args)==3 and isinstance(plot_label_args[-1],str): + ax1.text(*plot_label_args, transform=ax1.transAxes, + fontsize=plt.rcParams['axes.titlesize'], fontweight='bold', va='top', ha='right') # Check whether you have graph data to save to CSV if ct_mean is None: From 58c8d9f1cc4020a76c4241c1a0218a37dc152c51 Mon Sep 17 00:00:00 2001 From: "Matthew F. McEneaney" Date: Wed, 25 Feb 2026 15:26:05 -0500 Subject: [PATCH 2/4] feat: Updated how legends are plotted. --- py/saga/plot.py | 66 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/py/saga/plot.py b/py/saga/plot.py index 7e3efed..754e9e8 100644 --- a/py/saga/plot.py +++ b/py/saga/plot.py @@ -161,6 +161,40 @@ def plot_watermark( ) +def plot_label( + ax1, + plot_label_args=None, +): + """ + Parameters + ---------- + ax1 : matplotlib.axes._axes.Axes, required + Matplotlib.pyplot figure axis + plot_label_args : list, optional + List of positional arguments for plot label + + Description + ----------- + Plot a text label. + """ + if plot_label_args is None: + plot_label_args = [0.95, 0.15, None] + if ( + isinstance(plot_label_args, tuple) + or isinstance(plot_label_args, list) + and len(plot_label_args) == 3 + and isinstance(plot_label_args[-1], str) + ): + ax1.text( + *plot_label_args, + transform=ax1.transAxes, + fontsize=plt.rcParams["axes.titlesize"], + fontweight="bold", + va="top", + ha="right", + ) + + def plot_vlines( hist, binlims=None, @@ -705,8 +739,8 @@ def plot_systematics( watermark_kwargs=None, use_default_plt_settings=True, title_pad=20, - lg_kwargs={"loc":"upper left", "bbox_to_anchor":(1.05, 1.0), "frameon": False}, - plot_label_args=[0.95,0.15,None], + lg_kwargs=None, + plot_label_args=None, axlinewidth=1.0, log=False, figsize=(16, 10), @@ -763,6 +797,14 @@ def plot_systematics( Save systematics breakdowns to CSV in :obj:`.csv`. Note that this does **not** allow for asymmetric errors. """ + # Check arguments + if lg_kwargs is None: + lg_kwargs = { + "loc": "upper left", + "bbox_to_anchor": (1.05, 1.0), + "frameon": False, + } + # Set color palette sbn.set_palette(palette) @@ -803,14 +845,11 @@ def plot_systematics( plot_watermark(ax1, watermark=watermark, **watermark_kwargs) # Plot legend - if isinstance(legend_loc,dict): + if isinstance(lg_kwargs, dict): ax1.legend(**lg_kwargs) # Plot label - if isinstance(plot_label_args,tuple) or isinstance(plot_label_args,list) \ - and len(plot_label_args)==3 and isinstance(plot_label_args[-1],str): - ax1.text(*plot_label_args, transform=ax1.transAxes, - fontsize=plt.rcParams['axes.titlesize'], fontweight='bold', va='top', ha='right') + plot_label(ax1, plot_label_args) # Save figure f1.savefig(outpath) @@ -869,8 +908,8 @@ def plot_results( watermark_kwargs=None, show_injected_asymmetries=False, title_pad=20, - lg_kwargs={"loc":"best", "frameon": False}, - plot_label_args=[0.95,0.15,None], + lg_kwargs=None, + plot_label_args=None, ecolor="black", elinewidth=2.0, capsize=18, @@ -1067,6 +1106,8 @@ def plot_results( sgasyms = [0.10] if sg_colors is None: sg_colors = ["blue"] + if lg_kwargs is None: + lg_kwargs = {"loc": "best", "frameon": False} # Rescale graph scaling, acceptanceratio = None, None @@ -1225,14 +1266,11 @@ def plot_results( plot_watermark(ax1, watermark=watermark, **watermark_kwargs) # Plot legend - if isinstance(lg_kwargs,dict): + if isinstance(lg_kwargs, dict): ax1.legend(**lg_kwargs) # Plot label - if isinstance(plot_label_args,tuple) or isinstance(plot_label_args,list) \ - and len(plot_label_args)==3 and isinstance(plot_label_args[-1],str): - ax1.text(*plot_label_args, transform=ax1.transAxes, - fontsize=plt.rcParams['axes.titlesize'], fontweight='bold', va='top', ha='right') + plot_label(ax1, plot_label_args) # Check whether you have graph data to save to CSV if ct_mean is None: From dafd5891bd5c774e7b390fddb9e0addbb705ca1d Mon Sep 17 00:00:00 2001 From: "Matthew F. McEneaney" Date: Wed, 25 Feb 2026 15:26:27 -0500 Subject: [PATCH 3/4] fix: Format python code. --- py/saga/rescale.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/py/saga/rescale.py b/py/saga/rescale.py index 8e7fcf9..e3d50cf 100644 --- a/py/saga/rescale.py +++ b/py/saga/rescale.py @@ -135,8 +135,10 @@ def rescale_graph_data( y_mean = new_sim_graph["y"] # Set y values to constant and update scaled y errors if requested - scaled_y_mean = y_mean if yvalue < -1 else [np.abs(yvalue) for i in range(len(y_mean))] - if yvalue >= -1 and yvalue<0: + scaled_y_mean = ( + y_mean if yvalue < -1 else [np.abs(yvalue) for i in range(len(y_mean))] + ) + if yvalue >= -1 and yvalue < 0: scaled_yerr_mean = ( np.divide(1.0, np.sqrt(scaled_ct_mean)) * 1.0 / (tpol_factor * tdil_factor) ) * np.sqrt(1 - np.square(yvalue * tpol_factor * tdil_factor)) @@ -281,9 +283,11 @@ def rescale_csv_data( if yvalue < -1 else [np.abs(yvalue) for i in range(len(old_dat_df[y_key]))] ) - if yvalue >= -1 and yvalue<0: + if yvalue >= -1 and yvalue < 0: scaled_yerrs = ( - np.divide(1.0, np.sqrt(new_dat_df_count)) * 1.0 / (tpol_factor * tdil_factor) + np.divide(1.0, np.sqrt(new_dat_df_count)) + * 1.0 + / (tpol_factor * tdil_factor) ) * np.sqrt(1 - np.square(yvalue * tpol_factor * tdil_factor)) elif yvalue >= 0 and yvalue <= 1: scaled_yerrs *= np.sqrt(1 - np.square(yvalue * tpol_factor * tdil_factor)) From 2235503ec7c056e108d8793dbb65bf1263745dcd Mon Sep 17 00:00:00 2001 From: "Matthew F. McEneaney" Date: Wed, 25 Feb 2026 15:26:50 -0500 Subject: [PATCH 4/4] fix: Updated tutorial script. --- tutorials/aggregate_jobs_nested_kinematics2d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/aggregate_jobs_nested_kinematics2d.py b/tutorials/aggregate_jobs_nested_kinematics2d.py index 6eaa464..509c7e9 100644 --- a/tutorials/aggregate_jobs_nested_kinematics2d.py +++ b/tutorials/aggregate_jobs_nested_kinematics2d.py @@ -43,7 +43,7 @@ 'hist_density':False, 'axlinewidth':0, 'hist_dim':2, - 'legend_loc':None #NOTE: Do not plot a legend since you are using 2d hists. + 'lg_kwargs':None #NOTE: Do not plot a legend since you are using 2d hists. } # Set additional kwargs