Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
33db77c
Support for line annotations
Jul 6, 2021
5abec7e
plotting for nonstandard dataset types
Jul 6, 2021
3fd31af
clean up
Jul 6, 2021
a0976d3
pep8 compliance
Jul 7, 2021
cfd2c38
pep8 compliance
Jul 7, 2021
e4600ca
Optional autoscaling of axes
Jul 7, 2021
a1043b4
pep8 compliance
Jul 7, 2021
8cffd4e
pep8, fix xscaling
Jul 7, 2021
0298d90
Cleaning up
Jul 9, 2021
2d593d9
poissonian error for 0 yield
Jul 9, 2021
18064d1
Minimum error is 1.15 for n >=0
Jul 9, 2021
25d87bb
pep8
Jul 9, 2021
4e1dc4a
clean up
Jul 9, 2021
8f5663d
Oversight in parsing of colours
Jul 15, 2021
e1b3132
syntax
Jul 15, 2021
71d46dd
Merge branch 'autoscale' into merged
Jul 19, 2021
103d491
Merge branch 'line_annotations' into merged
Jul 19, 2021
78746f1
combine branches
Jul 19, 2021
c88e7ed
flake8
Jul 23, 2021
d3172c6
flake8
Jul 23, 2021
540bac6
Add 'apply if' function to postproc stages
DBAnthony Jul 29, 2021
8bea473
Merge branch 'master' of https://github.com/DBAnthony/fast-plotter
DBAnthony Jul 29, 2021
7ef9551
Revert "Add 'apply if' function to postproc stages"
DBAnthony Jul 29, 2021
31459f5
Merge branch 'FAST-HEP:master' into all_changes
DBAnthony Aug 10, 2021
f716259
Merge branch 'master' of https://github.com/DBAnthony/fast-plotter in…
Nov 5, 2021
5b81400
Hack for MR plots
Nov 5, 2021
1493562
Update __main__.py
DBAnthony Nov 5, 2021
1f37ab9
rm print
Nov 5, 2021
3c16e66
Merge branch 'all_changes' of https://github.com/DBAnthony/fast-plott…
Nov 5, 2021
81568d5
ratio plot grid options
Nov 5, 2021
5574ba4
Alternative regex for MR labels
Nov 9, 2021
72deea9
Update __main__.py
DBAnthony Apr 12, 2022
e040b8a
Update plotting.py
DBAnthony Apr 12, 2022
293f158
Update plotting.py
DBAnthony Apr 13, 2022
19cc838
Customisation from config updates
DBAnthony Aug 16, 2022
a5383bc
Update plotting.py
DBAnthony Aug 16, 2022
9004b36
Update __main__.py
DBAnthony Aug 16, 2022
9c65f73
Update plotting.py
DBAnthony Aug 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 88 additions & 8 deletions fast_plotter/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import six
import logging
import matplotlib
import numpy as np
import numbers
matplotlib.use('Agg')
matplotlib.rcParams.update({'figure.autolayout': True})
from .version import __version__ # noqa
from .utils import read_binned_df, weighting_vars # noqa
from .utils import read_binned_df, weighting_vars, binning_vars # noqa
from .utils import decipher_filename, mask_rows # noqa
from .plotting import plot_all, add_annotations # noqa
from .plotting import plot_all, add_annotations, is_intervals, annotate_xlabel_vals # noqa


logger = logging.getLogger("fast_plotter")
Expand Down Expand Up @@ -44,6 +46,7 @@ def arg_parser(args=None):
help="Scale the MC yields by this lumi")
parser.add_argument("-y", "--yscale", default="log", choices=["log", "linear"],
help="Use this scale for the y-axis")
parser.add_argument("-a", "--annotate_xlabel", action="store_true", help="Split x-axis information onto plot")
parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)

def split_equals(arg):
Expand Down Expand Up @@ -94,17 +97,84 @@ def recursive_replace(value, replacements):
if isinstance(value, six.string_types):
return Template(value).safe_substitute(replacements)
return value

replacements = dict(args.variables)
args = Namespace(**recursive_replace(vars(args), replacements))

return args


def autoscale_values(args, df_filtered, weight, ylim_lower=0.5, legend_size=2):
if hasattr(args, "autoscale"):
legend_size = int(legend_size)
data_rows = mask_rows(df_filtered,
regex=args.data,
level=args.dataset_col)
mc_rows = mask_rows(df_filtered,
regex="^((?!"+args.data+").)*$",
level=args.dataset_col)
if len(df_filtered.index.names) > 2:
logger.warn("Autoscaling not supported for multi-index dataframes")
limits = args.limits if 'limits' in args else {}
else:
if 'y' in args.autoscale:
if weight == "n":
max_y = df_filtered['sumw'].max()
else:
max_mc = df_filtered.loc[mc_rows, 'sumw'].max()*args.lumi
max_data = df_filtered.loc[data_rows, 'n'].max() if 'n' in df_filtered.columns else 0.1
max_y = max(max_mc, max_data)
max_y = max_y if max_y >= 1 else 1
if args.yscale == 'log':
ylim_upper_floor = int(np.floor(np.log10(max_y)))
y_buffer = (legend_size + 1 if ylim_upper_floor > 3
else legend_size if ylim_upper_floor > 2
else legend_size) # Buffer for legend
ylim_upper = float('1e'+str(ylim_upper_floor+y_buffer))
else:
buffer_factor = 1 + 0.5*legend_size
ylim_upper = round(max_y*buffer_factor, -int(np.floor(np.log10(abs(max_y))))) # Buffer for legend
ylim = [ylim_lower, ylim_upper]
df_aboveMin = df_filtered.loc[df_filtered['sumw'] > ylim_lower/args.lumi]
else:
if 'limits' in args:
ylim = args.limits['y'] if 'y' in args.limits else None
else:
ylim = None
if 'x' in args.autoscale:
df_aboveMin = df_filtered.loc[df_filtered['sumw'] > ylim_lower/args.lumi]
else:
df_aboveMin = df_filtered.copy()
xcol = df_aboveMin.index.get_level_values(1)
if 'x' in args.autoscale: # Determine x-axis limits
if is_intervals(xcol): # If x-axis is interval, take right and leftmost intervals unless they are inf
max_x = xcol.right.max() if np.isfinite(xcol.right.max()) else xcol.left.max()
min_x = xcol.left.min() if np.isfinite(xcol.left.min()) else xcol.right.min()
if not np.isfinite(max_x) and hasattr(args, "show_over_underflow") and args.show_over_underflow:
logger.warn("Cannot autoscale overflow bin for x-axis. Removing.")
xlim = [min_x, max_x]
elif isinstance(xcol, numbers.Number):
xlim = [xcol.min, xcol.max]
else:
xlim = [-0.5, len(xcol.unique()) - 0.5] # For non-numeric x-axis (e.g. mtn range)
else:
if 'limits' in args:
xlim = args.limits['x'] if 'x' in args.limits else None
else:
xlim = None

xlim = None if xlim is not None and np.NaN in xlim else xlim
ylim = None if ylim is not None and np.NaN in ylim else ylim
limits = {"x": xlim, "y": ylim}
else:
limits = args.limits if 'limits' in args else {}
return limits


def process_one_file(infile, args):
logger.info("Processing: " + infile)
df = read_binned_df(infile, dtype={args.dataset_col: str})
weights = weighting_vars(df)
legend_size = args.legend_size if hasattr(args, "legend_size") else 2
ran_ok = True
for weight in weights:
if args.weights and weight not in args.weights:
Expand Down Expand Up @@ -132,29 +202,39 @@ def process_one_file(infile, args):
df_filtered = df_filtered.groupby(level=df.index.names).sum()
plots, ok = plot_all(df_filtered, **vars(args))
ran_ok &= ok
dress_main_plots(plots, **vars(args))
args.limits = autoscale_values(args, df_filtered, weight, legend_size=legend_size)
dress_main_plots(plots, **vars(args), df=df_filtered)
save_plots(infile, weight, plots, args.outdir, args.extension)
return ran_ok


def dress_main_plots(plots, annotations=[], yscale=None, ylabel=None, legend={},
limits={}, xtickrotation=None, **kwargs):
limits={}, xtickrotation=None, df=None, annotate_xlabel=False, grid='both', **kwargs):
for main_ax, summary_ax in plots.values():
add_annotations(annotations, main_ax)
add_annotations(annotations, main_ax, summary_ax)
if annotate_xlabel:
met_cats=annotate_xlabel_vals(df, main_ax)
if yscale:
main_ax.set_yscale(yscale)
if ylabel:
main_ax.set_ylabel(ylabel)
main_ax.legend(**legend)
main_ax.grid(True)
legend['ncol'] = int(legend['ncol'])
main_ax.legend(**legend).set_zorder(20)
main_ax.grid(axis=grid)
main_ax.set_axisbelow(True)
for axis, lims in limits.items():
if isinstance(lims, (tuple, list)):
lims = map(float, lims)
if axis.lower() in "xy":
getattr(main_ax, "set_%slim" % axis)(*lims)
elif lims is None:
continue
elif lims.endswith("%"):
main_ax.margins(**{axis: float(lims[:-1])})
if annotate_xlabel:
x_ticks = [i for i in range(len(met_cats))]
main_ax.set_xticks(x_ticks)
main_ax.set_xticklabels(met_cats)
if xtickrotation:
matplotlib.pyplot.xticks(rotation=xtickrotation)

Expand Down
Loading