2 An API and a CLI for quickly building complex figures. 4 from __future__
import absolute_import
5 from __future__
import print_function
7 from builtins
import range
9 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu> 11 Permission is hereby granted, free of charge, to any person obtaining a copy 12 of this software and associated documentation files (the "Software"), to deal 13 in the Software without restriction, including without limitation the rights 14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 copies of the Software, and to permit persons to whom the Software is 16 furnished to do so, subject to the following conditions: 18 The above copyright notice and this permission notice shall be included in 19 all copies or substantial portions of the Software. 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 Usage: %prog [config.py] targets [options] 33 Targets may be either multiple root files to compare or a single root file 34 followed by multiple histograms or folders to compare. For example: 35 %prog fileA.root fileB.root fileC.root 36 %prog file.root dirA dirB dirC 37 %prog file.root dirA/hist1 dirA/hist2 39 Full documentation is available at: 40 http://packages.python.org/rootplot/""" 53 from os.path
import join
as joined
59 from .utilities
import RootFile, Hist, Hist2D, HistStack
60 from .utilities
import find_num_processors, loadROOT
62 argstring =
' '.
join(sys.argv)
65 batch = (
not re.search(
'--ext[ =]*C', argstring)
and 66 not re.search(
'-e[ ]*C', argstring))
73 from .version
import __version__
74 prog = os.path.basename(sys.argv[0])
76 global_opts = [
'filenames',
'targets',
'debug',
'path',
'processors',
77 'merge',
'noclean',
'output',
'numbering',
'html_template',
80 import multiprocessing
81 use_multiprocessing =
True 83 use_multiprocessing =
False 90 def __init__(self, options, arguments, scope='global'):
91 for opt
in dir(options):
92 value = getattr(options, opt)
93 if (
not opt.startswith(
'__')
and 94 type(value)
in [int, float, str, bool, type(
None)]):
96 self.
filenames = [x
for x
in arguments
if x.endswith(
'.root')]
97 self.
configs = [x
for x
in arguments
if x.endswith(
'.py')]
98 self.
targets = [x
for x
in arguments
if not (x.endswith(
'.py')
or 106 for i
in range(len(self.
targets)):
114 for key, value
in self.items():
115 if (key
not in [
'filenames',
'targets',
'configs']
and 116 defaults[key] != value):
120 for attribute
in dir(package):
121 if '__' not in attribute:
122 if ((scope ==
'global' and attribute
in global_opts)
or 123 (scope ==
'plot' and attribute
not in global_opts)):
124 value = getattr(package, attribute)
125 self[attribute] = value
128 configdir = tempfile.mkdtemp()
129 sys.path.insert(0,
'')
130 sys.path.insert(0, configdir)
132 joined(configdir,
'default_config.py'))
133 configs = [
'default_config.py']
134 for i, c
in enumerate(self.
configs):
135 shutil.copy(c, joined(configdir,
'rpconfig%i.py' % i))
136 configs.append(
'rpconfig%i.py' % i)
137 rc_name = use_mpl
and 'rootplotmplrc' or 'rootplotrc' 138 rc_path = os.path.expanduser(
'~/.%s' % rc_name)
139 if os.path.exists(rc_path):
140 print(
"Using styles and options from ~/.%s" % rc_name)
141 shutil.copy(rc_path, joined(configdir,
'%s.py' % rc_name))
142 configs.insert(1,
'%s.py' % rc_name)
144 myconfig = __import__(f[:-3])
147 shutil.rmtree(configdir)
154 import ROOT # allows access to ROOT colors (e.g. ROOT.kRed) 156 ############################################################################## 157 ######## About Config Files ################################################## 159 ## This file can be generated by running '%prog --config' 161 ## Options are loaded in the following order: 162 ## 1. from the command line 163 ## 2. from the default configuration file 164 ## 3. from ~/.%progrc 165 ## 4. from configuration files specified on the command-line 166 ## This leads to two major points worth understanding: 167 ## 1. you may delete any lines you like in this file and they will still 168 ## be loaded correctly from the default 169 ## 2. values specified here will superceed the same options from the 171 ## Therefore, you could set, for example, 'xerr = True' in this file, 172 ## and x-errorbars will always be drawn, regardless of whether '--xerr' is 173 ## given on the command-line or not. You can do this with any of the command- 174 ## line options, but note that dashes are translated to underscores, so 175 ## '--ratio-split=1' becomes 'ratio_split = 1'. 177 ## Most global style options like default line widths can be set through 178 root::## a rootlogon.C, as described at: 179 root::## http://root.cern.ch/drupal/content/how-create-or-modify-style 180 mpl:::## a matplotlibrc, as described at: 181 mpl:::## http://matplotlib.sourceforge.net/users/customizing.html 183 ############################################################################## 184 ######## Specifying Files and Targets ######################################## 186 ## You can specify the files to run on through the 'filenames' variable rather 187 ## than entering them at the command-line, for example: 188 ## filenames = ['histTTbar.root', 'histZmumu.root'] 190 ## Likewise, you can specify target histograms or directories here rather than 191 ## on the command-line, for example: 192 ## targets = ['barrel/15to20', 'barrel/20to30'] 194 ## You might also want to specify fancy labels for the legend here rather 195 ## than on the command-line: 196 root::## legend_entries = [r'#bar{t}t', r'Z#rightarrow#mu#mu'] 197 mpl:::## legend_entries = [r'$\bar{t}t$', r'$Z\rightarrow\mu\mu$'] 199 ############################################################################## 200 ######## Different Options for Different Targets ############################# 202 ## Leave these lists empty to have them automatically filled according to the 203 ## command-line options. Any list that is filled must be at least as long 204 ## as the number of targets or it will throw an error. 206 line_colors = [] # normally filled by options.colors 207 fill_colors = [] # normally filled by options.colors 208 marker_colors = [] # normally filled by options.colors 209 mpl:::errorbar_colors = [] # color for bars around the central value 210 root::marker_sizes = [] # in pixels 211 mpl:::marker_sizes = [] # in points 212 root::line_styles = [] # 1 (solid), 2 (dashed), 4 (dashdot), 3 (dotted), ... 213 mpl:::line_styles = [] # 'solid', 'dashed', 'dashdot', 'dotted' 214 root::fill_styles = [] # 0 (hollow), 1001 (solid), 2001 (hatched), ... 215 mpl:::fill_styles = [] # None, '/', '\', '|', '-', '+', 'x', 'o', 'O', ... 216 root::draw_commands = [] # a TH1::Draw option, include 'stack' to make stacked 217 mpl:::plot_styles = [] # 'bar', 'hist', 'errorbar', 'stack' 218 mpl:::alphas = [] # transparencies for fills (value from 0 to 1) 220 ############################################################################## 221 ######## Global Style Options ################################################ 223 ## Colors can be specified as (r, g, b) tuples (with range 0. to 1. or range 224 root::## 0 to 255), or ROOT color constants (ROOT.kBlue or 600) 225 mpl:::## 0 to 255), ROOT color constants (ROOT.kBlue or 600), or any matplotlib 226 mpl:::## color specification (names like 'blue' or 'b') 229 ## a default set of contrasting colors the author happens to like 230 ( 82, 124, 219), # blue 231 (212, 58, 143), # red 232 (231, 139, 77), # orange 233 (145, 83, 207), # purple 234 (114, 173, 117), # green 235 ( 67, 77, 83), # dark grey 238 ## Used when --marker_styles is specified; more info available at: 239 root::## http://root.cern.ch/root/html/TAttMarker.html 240 mpl:::## http://matplotlib.sourceforge.net/api/ 241 mpl:::## artist_api.html#matplotlib.lines.Line2D.set_marker 243 mpl::: 'o', 's', '^', 'x', '*', 'D', 'h', '1' 246 root:: 26, # triangle 248 root:: 30, # five-pointed star 254 #### Styles for --data 255 root::data_linestyle = 1 256 mpl:::data_linestyle = 'solid' 257 data_color = (0,0,0) # black 258 mc_color = (50, 150, 150) # used when there are exactly 2 targets; set to 259 # None to pick up the normal color 260 root::data_marker = 4 # marker style (circle) 261 mpl:::data_marker = 'o' # marker style 263 #### Settings for --ratio-split or --efficiency-split 267 ratio_fraction = 0.3 # Fraction of the canvas that bottom plot occupies 268 ratio_label = 'Ratio to %(ratio_file)s' # Label for the bottom plot 269 efficiency_label = 'Efficiency vs. %(ratio_file)s' 271 #### Titles produced by --area-normalize and --normalize 272 area_normalized_title = 'Fraction of Events in Bin' 273 target_normalized_title = 'Events Normalized to %(norm_file)s' 275 #### Overflow and underflow text labels 276 overflow_text = ' Overflow' 277 underflow_text = ' Underflow' 278 mpl:::overflow_size = 'small' 279 mpl:::overflow_alpha = 0.5 281 #### Define how much headroom to add to the plot 282 top_padding_factor = 1.2 283 top_padding_factor_log = 5. # used when --logy is set 285 #### Plotting options based on histogram names 286 ## Apply options to histograms whose names match regular expressions 287 ## The tuples are of the form (option_name, value_to_apply, list_of_regexs) 288 ## ex: to rebin by 4 all histograms containing 'pt' or starting with 'eta': 289 ## ('rebin', 4, ['.*pt.*', 'eta.*']) 290 options_by_histname = [ 291 ('area_normalize', True, []), 295 root::legend_width = 0.38 # Fraction of canvas width 296 root::legend_entry_height = 0.05 # Fraction of canvas height 297 root::max_legend_height = 0.4 # Fraction of canvas height 298 root::legend_left_bound = 0.20 # For left justification 299 root::legend_right_bound = 0.95 # For right justification 300 root::legend_upper_bound = 0.91 # For top justification 301 root::legend_lower_bound = 0.15 # For bottom justification 302 root::legend_codes = { 1 : 'upper right', 303 root:: 2 : 'upper left', 304 root:: 3 : 'lower left', 305 root:: 4 : 'lower right', 307 root:: 6 : 'center left', 308 root:: 7 : 'center right', 309 root:: 8 : 'lower center', 310 root:: 9 : 'upper center', 311 root:: 10 : 'center', 314 root::#### Page numbers 315 root::numbering_size_root = 0.03 # Fraction of canvas width 316 root::numbering_align_root = 33 # Right-top adjusted 317 root::numbering_x_root = 0.97 # Fraction of canvas width 318 root::numbering_y_root = 0.985 # Fraction of canvas height 320 root::#### Draw style for TGraph 321 root::draw_graph = 'ap' 323 root::#### This code snippet will be executed after the histograms have all 324 root::#### been drawn, allowing you to add decorations to the canvas 325 root::decoration_root = ''' 326 root::## Draw a line to indicate a cut 327 root::#line = ROOT.TLine(5.,0.,5.,9.e9) 329 root::## Add a caption 330 root::#tt = ROOT.TText() 331 root::#tt.DrawTextNDC(0.6, 0.15, "CMS Preliminary") 334 mpl:::#### These options will override legend_location, allowing more precise control 335 mpl:::## Upper right corner of legend in figure coordinates 336 mpl:::legend_figure_bbox = None # [1.0, 1.0] for legend outside the axes 337 mpl:::## Upper right corner of legend in axes coordinates 338 mpl:::legend_axes_bbox = None 340 mpl:::#### Page numbers 341 mpl:::numbering_size_mpl = 'small' 342 mpl:::numbering_ha_mpl = 'right' 343 mpl:::numbering_va_mpl = 'top' 344 mpl:::numbering_x_mpl = 0.98 # Fraction of canvas width 345 mpl:::numbering_y_mpl = 0.98 # Fraction of canvas height 347 mpl:::#### Rotation for text x-axis labels 348 mpl:::xlabel_rotation = -15 349 mpl:::xlabel_alignment = 'left' 350 mpl:::xlabel_alignmenth = 'bottom' # For barh 352 mpl:::#### Convert ROOT symbols to proper LaTeX, for matplotlib plotting 353 mpl:::## By default, matplotlib renders only symbols between $'s as TeX, but if 354 mpl:::## you enable the 'text.usetex' matplotlibrc setting, then everything is handled 355 mpl:::## by the LaTeX engine on your system, in which case you can go wild with TeX. 357 mpl:::## ROOT-type strings on left get replaced with LaTeX strings on the right 359 mpl::: # some defaults that should work for most cases 360 mpl::: (' pt ' , r' $p_\mathrm{T}$ '), 361 mpl::: ('pT ' , r'$p_\mathrm{T}$ '), 362 mpl::: (' pT' , r' $p_\mathrm{T}$'), 363 mpl::: ('p_{T}' , r'$p_\mathrm{T}$'), 364 mpl::: ('E_{T}' , r'$E_\mathrm{T}$'), 365 mpl::: ('#eta' , r'$\eta$'), 366 mpl::: ('#phi' , r'$\phi$'), 367 mpl::: ('fb^{-1}' , r'$\mathrm{fb}^{-1}$'), 368 mpl::: ('pb^{-1}' , r'$\mathrm{pb}^{-1}$'), 369 mpl::: ('<' , r'$<$'), 370 mpl::: ('>' , r'$>$'), 374 mpl:::## If you include 'use_regexp' as the first item, the patterns to be replaced 375 mpl:::## will function as regular expressions using python's re module rather than 376 mpl:::## as simple text. The example below turn's ROOT's superscript and subscript 377 mpl:::## syntax into LaTeX: 380 mpl:::## ('use_regexp', True), 381 mpl:::## (r'\^\{(.*)\}', r'$^{\1}$'), 382 mpl:::## (r'\_\{(.*)\}', r'$_{\1}$'), 385 mpl:::#### A function that will be executed after all histograms have been drawn. 386 mpl:::#### It can be used to add extra decorations to your figure. 387 mpl:::def decoration_mpl(figure, axeses, path, options, hists): 388 mpl::: #### Draw a line to indicate a cut 389 mpl::: ## axeses[0].axvline(5., color='red', linestyle='--') 390 mpl::: #### Add a caption 391 mpl::: ## figure.text(0.6, 0.15, "CMS Preliminary") 394 ############################################################################## 395 ######## HTML Output ######################################################### 397 #### Number of columns for images in HTML output 400 #### Provide a template for the html index files 404 <link rel='shortcut icon' href='http://packages.python.org/rootplot/_static/rootplot.ico'> 405 <link href='http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:bold' rel='stylesheet' type='text/css'> 406 <style type='text/css'> 407 body { padding: 10px; font-family:Arial, Helvetica, sans-serif; 408 font-size:15px; color:#FFF; font-size: large; 409 background-image: url( 410 'http://packages.python.org/rootplot/_static/tile.jpg');} 411 img { border: solid black 1px; margin:10px; } 412 object { border: solid black 1px; margin:10px; } 413 h1 { text-shadow: 2px 2px 2px #000; 414 font-size:105px; color:#fff; border-bottom: solid black 1px; 415 font-size: 300%%; font-family: 'Yanone Kaffeesatz'} 416 a, a:active, a:visited { 417 color:#FADA00; text-decoration:none; } 418 a:hover{ color:#FFFF00; text-decoration:none; 419 text-shadow: 0px 0px 5px #fff; } 421 <title>%(path)s</title> 424 <a style="" href="http://packages.python.org/rootplot/"><img style="position: absolute; top:10 px; right: 10px; border: 0px" src="http://packages.python.org/rootplot/_static/rootplot-logo.png"></a> 432 <p style='font-size: x-small; text-align: center;'> 433 <a href='http://www.greepit.com/resume-template/resume.htm'> 434 Based on a template by Sarfraz Shoukat</a></p> 440 multi_call_template =
''' 447 allplots_template =
''' 448 ## This file contains all the necessary calls to the rootplot API to produce 449 ## the same set of plots that were created from the command-line. 451 ## You can use this file to intercept the objects and manipulate them before 452 ## the figure is saved, making any custom changes that are not possible from 455 ## 'objects' is a python dictionary containing all the elements used in the 456 ## plot, including 'hists', 'legend', etc. 457 ## ex: objects['hists'] returns a list of histograms 460 ## the normal way to import rootplot 461 from rootplot import plot, plotmpl 463 ## special import for CMSSW installations of rootplot 464 from PhysicsTools.PythonAnalysis.rootplot import plot, plotmpl 467 os.chdir('..') # return to the directory with the ROOT files 472 allplots_multi_template =
''' 473 ## This file is the same as allplots.py, except that it uses multiprocessing 474 ## to make better use of machines with multiple cores 477 ## the normal way to import rootplot 478 from rootplot import plot, plotmpl 479 from rootplot.core import report_progress 481 ## special import for CMSSW installations of rootplot 482 from PhysicsTools.PythonAnalysis.rootplot import plot, plotmpl 483 from PhysicsTools.PythonAnalysis.rootplot.core import report_progress 485 import multiprocessing as multi 488 os.chdir('..') # return to the directory with the ROOT files 494 queue = multi.JoinableQueue() 495 qglobals = multi.Manager().Namespace() 496 qglobals.nfinished = 0 497 qglobals.ntotal = len(calls) 501 def qfunc(queue, qglobals): 502 from Queue import Empty 504 try: mycall = queue.get(timeout=5) 505 except (Empty, IOError): break 507 ROOT.gROOT.GetListOfCanvases().Clear() 508 qglobals.nfinished += 1 509 report_progress(qglobals.nfinished, qglobals.ntotal, 510 '%(output)s', '%(ext)s') 513 for i in range(%(processors)i): 514 p = multi.Process(target=qfunc, args=(queue, qglobals)) 518 report_progress(len(calls), len(calls), '%(output)s', '%(ext)s') 523 for key, value
in globals().
items():
524 if 'template' in key:
525 globals()[key] = value[1:]
532 An application for plotting histograms from a ROOT file with |matplotlib|. 534 It is invoked from the command-line as ``rootplotmpl``. 542 An application for plotting histograms from a ROOT file. 544 It is invoked from the command-line as ``rootplot``. 547 optdiff = options.kwarg_list()
549 rootplot(*options.arguments(), **optdiff)
552 rootplot(*options.arguments(), **optdiff)
553 except Exception
as e:
555 print(
"For usage details, call '%s --help'" % prog)
565 plotmpl(file1, file2, file3, ..., target, **kwargs): 567 build a matplotlib figure, pulling the *target* histogram from each of the 572 plotmpl(file, target1, target2, target3, ..., **kwargs): 574 build a matplotlib figure, pulling all *target* histograms from *file*. 576 With either of these signatures, the plot style is specified through 577 *kwargs*, which can accept any of the options available to 578 :mod:`rootplotmpl` at the command-line. 580 Returns the tuple (*figure*, *axeses*, *stack*, *hists*, *plotpath*). 584 return plot(*args, **kwargs)
590 plot(file1, file2, file3, ..., target, **kwargs): 592 build a ROOT canvas, pulling the *target* histogram from each of the 597 plotmpl(file, target1, target2, target3, ..., **kwargs): 599 build a ROOT canvas, pulling all *target* histograms from *file*. 601 With either of these signatures, the plot style is specified through 602 *kwargs*, which can accept any of the options available to 603 :mod:`rootplot` at the command-line. 605 Returns the tuple (*canvas*, *pads*, *stack*, *hists*, *plotpath*). 617 rootplotmpl(file1, file2, file3, ..., **kwargs): 619 build ROOT canvases from corresponding histograms in each of the *files*. 623 rootplotmpl(file, folder1, folder2, folder3, ..., **kwargs): 625 build ROOT canvases from corresponding histograms in each of the *folders* 630 rootplotmpl(file, target1, target2, target3, ..., **kwargs): 632 build a ROOT canvas from each of the *targets* in *file*. 634 With any of these call signatures, images are generated in an output 635 directory along with a script with the necessary calls to :func:`plotmpl` 636 to reproduce each of the canvases. The plot style is specified through 637 *kwargs*, which can accept any of the options available to 638 :mod:`rootplotmpl` at the command-line. 648 rootplot(file1, file2, file3, ..., **kwargs): 650 build ROOT canvases from corresponding histograms in each of the *files*. 654 rootplot(file, folder1, folder2, folder3, ..., **kwargs): 656 build ROOT canvases from corresponding histograms in each of the *folders* 661 rootplot(file, target1, target2, target3, ..., **kwargs): 663 build a ROOT canvas from each of the *targets* in *file*. 665 With any of these call signatures, images are generated in an output 666 directory along with a script with the necessary calls to :func:`plot` 667 to reproduce each of the canvases. The plot style is specified through 668 *kwargs*, which can accept any of the options available to 669 :mod:`rootplot` at the command-line. 671 if 'config' in kwargs:
674 nfiles = len(options.filenames)
675 ntargets = len(options.targets)
677 raise TypeError(
"%s takes at least 1 filename argument (0 given)" %
679 elif ntargets > 0
and nfiles > 1:
680 raise TypeError(
"%s cannot accept targets (%i given) when " 681 "multiple files are specified (%i given)" %
682 (prog, ntargets, nfiles))
683 rootfiles = [RootFile(filename)
for filename
in options.filenames]
685 if not options.noclean
and os.path.exists(options.output):
686 shutil.rmtree(options.output)
687 for path, folders, objects
in walk_rootfile(
'', rootfiles[0], options):
688 if not os.path.exists(joined(options.output, path)):
689 os.makedirs(joined(options.output, path))
693 ndigits =
int(math.log10(len(plotargs))) + 1
694 for i, (filenames, targets)
in enumerate(plotargs):
695 argstring =
', '.
join([
"'%s'" % x
for x
in (filenames + targets +
697 reduced_kwargs =
dict(kwargs)
698 for key, value
in reduced_kwargs.items():
699 if key
in global_opts:
700 del reduced_kwargs[key]
701 elif isinstance(value, str):
702 reduced_kwargs[key] =
"'%s'" % value
703 if 'numbering' in kwargs:
704 reduced_kwargs[
'numbering'] = i + 1
705 optstring =
', '.
join([
'%s=%s' % (key, value)
706 for key, value
in reduced_kwargs.items()])
708 argstring =
"%s, %s" % (argstring, optstring)
709 plotpath, title, legentries =
get_plotpath(filenames, targets)
710 savepath = joined(options.output, plotpath)
711 if 'numbering' in reduced_kwargs:
712 dirs = savepath.split(
'/')
713 dirs[-1] =
str(i + 1).zfill(ndigits) + dirs[-1]
714 savepath =
'/'.
join(dirs)
715 call_vars = {
'argstring' : argstring,
'ext' : options.ext,
716 'savepath' : savepath}
718 call_vars[
'trans'] = options.transparent
719 call_vars[
'dpi'] = options.dpi
720 api_call = (
"figure, objects = " 721 "plotmpl(%(argstring)s)" % call_vars)
722 save_call = (
"figure.savefig('%(savepath)s', " 723 "transparent=%(trans)s, " 724 "dpi=%(dpi)s)" % call_vars)
726 api_call = (
"canvas, objects = " 727 "plot(%(argstring)s)" % call_vars)
728 save_call =
"canvas.SaveAs('%(savepath)s.%(ext)s')" % call_vars
729 call_lists.append([api_call, save_call])
732 output = options.output
733 processors = options.processors
734 call_statements =
'\n\n'.
join([plotcall +
'\n' + savecall
735 for plotcall, savecall
in call_lists])
736 allplots_script = allplots_template % locals()
737 call_statements =
"".
join([multi_call_template % (plotcall, savecall)
738 for plotcall, savecall
in call_lists])
739 allplots_multi_script = allplots_multi_template % locals()
740 write_to_file(allplots_script, joined(options.output,
'allplots.py'))
742 joined(options.output,
'allplots_multi.py'))
744 if use_multiprocessing:
745 original_dir = os.getcwd()
746 os.chdir(options.output)
747 exec(allplots_multi_script)
748 os.chdir(original_dir)
750 for i, call_list
in enumerate(call_lists):
754 options.output, options.ext)
757 for root, dirs, files
in os.walk(options.output):
758 if not os.listdir(root):
761 if options.ext
in [
'png',
'gif',
'svg']:
762 print(
"Writing html index files...")
763 width, height = options.size
765 width, height = [x * options.dpi
for x
in options.size]
766 for path, dirs, files
in os.walk(options.output):
767 dirs, files = sorted(dirs), sorted(files)
769 options.html_template, options.ncolumns_html,
779 f = open(destination,
'w')
790 for key
in dir(default):
791 default_val = getattr(default, key)
792 modified_val = getattr(modified, key)
793 if (type(default_val)
in [int, float, str, bool, type(
None)]
and 794 key
in dir(modified)
and default_val != modified_val):
795 diff[key] = modified_val
801 s = re.sub(
'root::.*\n',
'', s)
802 s = s.replace(
'mpl:::',
'')
803 s = s.replace(
'%prog',
'rootplotmpl')
805 s = re.sub(
'mpl:::.*\n',
'', s)
806 s = s.replace(
'root::',
'')
807 s = s.replace(
'%prog',
'rootplot')
812 filename =
'rootplotmpl_config.py' 814 filename =
'rootplot_config.py' 815 f = open(filename,
'w')
818 print(
"Wrote %s to the current directory" % filename)
822 def append_to_options(config, options):
823 for attribute
in dir(config):
824 if '__' not in attribute:
825 attr = getattr(config, attribute)
826 setattr(options, attribute, attr)
827 configdir = tempfile.mkdtemp()
828 sys.path.insert(0,
'')
829 sys.path.insert(0, configdir)
830 f = open(joined(configdir,
'default_config.py'),
'w')
833 import default_config
834 append_to_options(default_config, options)
836 rc_name =
'rootplotmplrc' 838 rc_name =
'rootplotrc' 839 rc_path = os.path.expanduser(
'~/.%s' % rc_name)
840 if os.path.exists(rc_path):
841 print(
"Using styles and options from ~/.%s" % rc_name)
842 shutil.copy(rc_path, joined(configdir,
'%s.py' % rc_name))
843 configs.insert(0,
'%s.py' % rc_name)
845 user_config = __import__(f[:-3])
846 append_to_options(user_config, options)
847 shutil.rmtree(configdir)
851 keys = rootfile.file.GetDirectory(path).GetListOfKeys()
852 folders, objects = [], []
855 classname = key.GetClassName()
856 newpath = joined(path, name)
858 matches_path = re.match(options.path, newpath)
859 if 'TDirectory' in classname:
861 elif (
'TH1' in classname
or 'TGraph' in classname
or 862 classname ==
'TProfile'):
864 elif options.draw2D
and (
'TH2' in classname
or 865 classname ==
'TProfile2D'):
867 if (matches_path
and dimension):
869 yield path, folders, objects
870 for folder
in folders:
871 for x
in walk_rootfile(joined(path, folder), rootfile, options):
878 for path, folders, objects
in walk_rootfile(
'', files[0], options):
879 if path == options.targets[0]:
882 target_lists.append([joined(t, obj)
883 for t
in options.targets])
885 target_sets = [set()
for f
in files]
886 for i, f
in enumerate(files):
889 target_sets[i].
add(joined(path, obj))
890 target_set = target_sets[0]
891 for s
in target_sets:
893 target_lists = [[t]
for t
in target_set]
895 return [(options.filenames, options.targets)]
897 return [(options.filenames,
list(t))
for t
in target_lists]
901 for key, value
in kwargs.items():
903 options.process_configs(scope=scope)
905 options.split = options.ratio_split
or options.efficiency_split
906 options.ratio = (options.ratio
or options.efficiency
or 907 options.ratio_split
or options.efficiency_split)
908 options.efficiency = options.efficiency
or options.efficiency_split
909 if len(options.filenames) > 1
or len(options.targets) > 1:
910 options.draw2D =
None 915 canvas = ROOT.TCanvas(
"canvas",
"",
916 int(options.size[0]),
int(options.size[1]))
917 isTGraph =
'TGraph' in hists[0].rootclass
918 objects = {
'pads': [canvas]}
921 objects[
'pads'] =
divide_canvas(canvas, options.ratio_fraction)
922 objects[
'pads'][0].
cd()
927 ROOT.gStyle.SetErrorX()
928 histmax, first_draw, roothists =
None,
True, []
930 objects[
'multigraph'] = ROOT.TMultiGraph()
932 objects[
'stack'] = ROOT.THStack(
933 'st%s' % os.path.basename(options.plotpath),
934 '%s;%s;%s' % (hists[0].title,
935 hists[0].xlabel, hists[0].ylabel))
936 for i, hist
in enumerate(hists):
937 if not hist:
continue 938 name =
"%s_%i" % (options.plotpath, i)
940 roothist = hist.TGraph(name=name)
941 elif isinstance(hist, Hist):
942 roothist = hist.TH1F(name=name.replace(
'/',
'__'))
944 roothist = hist.TH2F(name=name)
945 roothist.SetLineStyle(options.line_styles[i])
946 roothist.SetLineColor(options.line_colors[i])
947 roothist.SetFillColor(options.fill_colors[i])
948 roothist.SetMarkerColor(options.marker_colors[i])
949 roothist.SetFillStyle(options.fill_styles[i])
950 roothist.SetMarkerStyle(options.marker_styles[i])
951 roothist.SetMarkerSize(options.marker_sizes[i])
952 roothists.append(roothist)
953 if (isinstance(hist, Hist)
and not isTGraph
and 954 'stack' in options.draw_commands[i]):
955 objects[
'stack'].Add(roothist)
956 if 'stack' in objects
and objects[
'stack'].GetHists():
957 histmax = objects[
'stack'].GetMaximum()
958 for roothist
in roothists:
959 histmax =
max(histmax, roothist.GetMaximum())
961 if isinstance(hist, Hist2D):
963 if options.gridx
or options.grid:
964 for pad
in objects[
'pads']:
965 pad.SetGridx(
not pad.GetGridx())
966 if options.gridy
or options.grid:
967 objects[
'pads'][0].SetGridy(
not objects[
'pads'][0].GetGridy())
969 for com
in options.draw_commands:
972 com = com.replace(
'stack',
'')
973 objects[
'stack'].Draw(com)
975 for i, roothist
in enumerate(roothists):
977 objects[
'multigraph'].Add(roothist)
979 if 'stack' not in options.draw_commands[i]:
982 roothist.Draw(options.draw_commands[i])
984 roothist.Draw(
"same " + options.draw_commands[i])
986 roothist.Draw(options.draw2D)
988 if options.fill_styles[i]: legendopt +=
'f' 989 if 'e' in options.draw_commands[i]: legendopt +=
'e' 990 objects[
'legend'].AddEntry(roothist, options.legend_entries[i],
993 objects[
'multigraph'].Draw(options.draw_graph)
995 objects[
'multigraph'].Draw(options.draw_graph)
996 if options.split
and dimension == 1:
997 objects[
'pads'][1].
cd()
999 hists, roothist.GetXaxis().GetTitle(), options)
1000 xmin = hists[0].xedges[0]
1001 xmax = hists[0].xedges[-1]
1002 objects[
'ratio_multigraph'].GetXaxis().SetRangeUser(xmin, xmax)
1003 objects[
'pads'][0].
cd()
1005 for pad
in objects[
'pads']:
1008 objects[
'pads'][0].SetLogy(
True)
1009 if options.ratio_logy:
1010 if len(objects[
'pads']) > 1:
1011 objects[
'pads'][1].SetLogy(
True)
1012 if options.numbering:
1014 if roothist.InheritsFrom(
'TH1'):
1015 if options.overflow:
1017 if options.underflow:
1019 if options.legend_location
and dimension == 1:
1020 objects[
'legend'].Draw()
1021 exec(options.decoration_root)
1022 objects[
'hists'] = roothists
1023 return canvas, objects
1027 fig = plt.figure(1, figsize=options.size)
1030 objects = {
'axes' : [axes, axes]}
1033 objects[
'axes'] =
divide_axes(fig, axes, options.ratio_fraction)
1034 axes = objects[
'axes'][0]
1041 fullstack, objects[
'stack'] = HistStack(), HistStack()
1042 histmax, allempty =
None,
True 1043 for i, hist
in enumerate(hists):
1044 if hist
and hist.entries:
1046 if isinstance(hist, Hist):
1048 if options.logy
and options.plot_styles[i] !=
'errorbar':
1049 for j
in range(hist.nbins):
1050 hist.y[j] =
max(hist.y[j], 1e-10)
1051 if options.plot_styles[i]
in [
'barh',
'barcluster',
'stack']:
1052 objects[
'stack'].
add(hist, log=options.logy,
1053 hatch=options.fill_styles[i],
1054 linestyle=options.line_styles[i],
1055 edgecolor=options.line_colors[i],
1056 facecolor=options.fill_colors[i])
1058 if 'stack' in options.plot_styles:
1059 histmax =
max(histmax, objects[
'stack'].stackmax())
1060 elif 'barh' in options.plot_styles
or 'barcluster' in options.plot_styles:
1061 histmax =
max(histmax, objects[
'stack'].
max())
1062 for hist
in fullstack:
1063 histmax =
max(histmax,
max(hist))
1065 fig.text(0.5, 0.5,
"No Entries", ha=
'center', va=
'center')
1066 elif isinstance(refhist, Hist):
1067 for i, hist
in enumerate(hists):
1069 if options.plot_styles[i] ==
"errorbar":
1071 axes.set_yscale(
'log')
1073 if not np.nonzero(hist.y)[0].tolist():
1076 for j
in range(hist.nbins):
1077 yerr = hist.yerr[0][j]
1078 if (hist[j] - yerr) < (0.01 * yerr):
1079 hist.yerr[0][j] *= 0.99
1080 hist.errorbar(fmt=options.marker_styles[i],
1083 markersize=options.marker_sizes[i],
1084 color=options.fill_colors[i],
1085 ecolor=options.errorbar_colors[i],
1086 label_rotation=options.xlabel_rotation,
1087 label_alignment=options.xlabel_alignment)
1088 elif options.plot_styles[i] ==
"bar":
1089 hist.bar(alpha=options.alphas[i],
1091 width=options.barwidth,
1092 hatch=options.fill_styles[i],
1093 edgecolor=options.line_colors[i],
1094 facecolor=options.fill_colors[i],
1095 label_rotation=options.xlabel_rotation,
1096 label_alignment=options.xlabel_alignment)
1097 elif 'hist' in options.plot_styles[i]:
1099 if 'fill' in options.plot_styles[i]:
1100 histtype =
'stepfilled' 1101 hist.hist(alpha=options.alphas[i],
1104 hatch=options.fill_styles[i],
1105 edgecolor=options.line_colors[i],
1106 facecolor=options.fill_colors[i],
1107 label_rotation=options.xlabel_rotation,
1108 label_alignment=options.xlabel_alignment)
1110 for ax
in objects[
'axes']:
1111 ax.set_xscale(
'log')
1112 if objects[
'stack'].hists:
1113 if 'stack' in options.plot_styles:
1114 objects[
'stack'].histstack(
1115 histtype=
'stepfilled',
1116 label_rotation=options.xlabel_rotation,
1117 label_alignment=options.xlabel_alignment)
1118 elif 'barh' in options.plot_styles:
1119 objects[
'stack'].barh(
1120 width=options.barwidth,
1121 label_rotation=options.xlabel_rotation,
1122 label_alignment=options.xlabel_alignmenth)
1123 elif 'barcluster' in options.plot_styles:
1124 objects[
'stack'].barcluster(
1125 width=options.barwidth,
1126 label_rotation=options.xlabel_rotation,
1127 label_alignment=options.xlabel_alignment)
1128 if 'barh' not in options.plot_styles:
1129 axes.set_xlim(refhist.xedges[0], refhist.xedges[-1])
1131 my_min = fullstack.min(threshold=1.1e-10)
1133 while (rounded_min > my_min):
1135 axes.set_ylim(ymin=rounded_min)
1136 if options.xmin
is not None:
1137 axes.set_xlim(xmin=options.xmin)
1138 if options.xmax
is not None:
1139 axes.set_xlim(xmax=options.xmax)
1140 if options.ymin
is not None:
1141 axes.set_ylim(ymin=options.ymin)
1142 if options.ymax
is not None:
1143 axes.set_ylim(ymax=options.ymax)
1144 elif (
'barh' not in options.plot_styles
and 1145 histmax != 0
and not options.ymax):
1146 axes.set_ylim(ymax=histmax * options.top_padding_factor)
1147 if options.overflow:
1148 axes.text(hist.x[-1], axes.set_ylim()[0], options.overflow_text,
1149 rotation=
'vertical', ha=
'center',
1150 alpha=options.overflow_alpha, size=options.overflow_size)
1151 if options.underflow:
1152 axes.text(hist.x[0], axes.set_ylim()[0], options.underflow_text,
1153 rotation=
'vertical', ha=
'center',
1154 alpha=options.overflow_alpha, size=options.overflow_size)
1155 if options.gridx
or options.grid:
1157 if options.gridy
or options.grid:
1159 if (options.legend_location !=
'None' or options.legend_axes_bbox
or 1160 options.legend_figure_bbox):
1162 options.legend_location =
int(options.legend_location)
1165 if options.legend_axes_bbox:
1166 kwargs = {
'bbox_to_anchor' : options.legend_axes_bbox}
1167 elif options.legend_figure_bbox:
1168 kwargs = {
'bbox_to_anchor' : options.legend_figure_bbox,
1169 'bbox_transform' : fig.transFigure}
1171 kwargs = {
'loc' : options.legend_location}
1172 if options.legend_ncols:
1173 kwargs[
'ncol'] =
int(options.legend_ncols)
1174 objects[
'legend'] = axes.legend(numpoints=1, **kwargs)
1175 elif isinstance(refhist, Hist2D):
1176 drawfunc = getattr(hist, options.draw2D)
1177 if 'col' in options.draw2D:
1179 drawfunc(cmap=options.cmap)
1183 drawfunc(color=options.fill_colors[0])
1184 axes.set_title(r2m.replace(refhist.title, options.replace))
1185 if 'barh' in options.plot_styles:
1186 set_ylabel = objects[
'axes'][1].set_xlabel
1187 set_xlabel = axes.set_ylabel
1189 set_ylabel = axes.set_ylabel
1190 set_xlabel = objects[
'axes'][1].set_xlabel
1191 if options.ratio
and not options.split
and not options.ylabel:
1192 ratio_file = options.legend_entries[options.ratio - 1]
1193 if options.efficiency:
1194 set_ylabel(options.efficiency_label % locals())
1196 set_ylabel(options.ratio_label % locals())
1198 set_ylabel(r2m.replace(refhist.ylabel, options.replace))
1199 set_xlabel(r2m.replace(refhist.xlabel, options.replace))
1201 fig.sca(objects[
'axes'][1])
1203 fig.sca(objects[
'axes'][0])
1204 if options.numbering:
1205 fig.text(options.numbering_x_mpl, options.numbering_y_mpl,
1206 options.numbering, size=options.numbering_size_mpl,
1207 ha=options.numbering_ha_mpl, va=options.numbering_va_mpl)
1208 options.decoration_mpl(fig, objects[
'axes'], options.plotpath,
1210 objects[
'hists'] = hists
1217 nfiles = len(options.filenames)
1218 ntargets = len(options.targets)
1220 raise TypeError(
"plot() takes at least 1 filename argument (0 given)")
1221 elif ntargets > 1
and nfiles > 1:
1222 raise TypeError(
"plot() takes exactly 1 target (%i given) when " 1223 "multiple files are specified (%i given)" %
1225 options.nhists =
max(nfiles, ntargets)
1227 files = [RootFile(filename)
for filename
in options.filenames]
1232 roothist = f.file.Get(target)
1233 except ReferenceError:
1237 isTGraph =
not roothist.InheritsFrom(
'TH1')
1239 raise TypeError(
"'%s' does not appear to be a valid target" %
1243 stacked =
'stack' in options.plot_styles[i]
1245 stacked =
'stack' in options.draw_commands[i]
1247 dimension = roothist.GetDimension()
1248 roothist.Scale(options.scale[i])
1250 roothist.Rebin(options.rebin)
1251 title, xlabel, ylabel =
get_labels(roothist, options)
1252 if options.normalize:
1253 norm_file = options.legend_entries[options.normalize - 1]
1254 ylabel = options.target_normalized_title % locals()
1255 if options.area_normalize:
1256 ylabel = options.area_normalized_title
1258 hist = Hist(roothist, label=options.legend_entries[i],
1259 title=title, xlabel=xlabel, ylabel=ylabel)
1261 hist = Hist2D(roothist, label=options.legend_entries[i],
1262 title=title, xlabel=xlabel, ylabel=ylabel)
1264 stack_integral += roothist.Integral()
1267 for i, hist
in enumerate(hists):
1269 stacked =
'stack' in options.plot_styles[i]
1271 stacked =
'stack' in options.draw_commands[i]
1273 if options.overflow:
1274 hist.y[-1] += hist.overflow
1275 if options.underflow:
1276 hist.y[0] += hist.underflow
1277 if options.area_normalize:
1280 hist.scale(1./stack_integral)
1282 hist.scale(1./sum(hist.y))
1283 if options.normalize:
1284 numerhist = hists[options.normalize - 1]
1285 if options.norm_range:
1288 numer = numerhist.TH1F().Integral(lowbin, highbin)
1289 denom = hist.TH1F().Integral(lowbin, highbin)
1291 numer = sum(numerhist.y)
1294 denom = stack_integral
1296 hist.scale(numer / denom)
1297 return hists, options
1302 xpos = size_option.find(
'x')
1303 return float(size_option[:xpos]),
float(size_option[xpos+1:])
1312 elif color ==
'none' or color ==
'None':
1316 color = ROOT.gROOT.GetColor(color)
1317 r, g, b = color.GetRed(), color.GetGreen(), color.GetBlue()
1321 color = [x/256.
for x
in color][0:3]
1325 color = mpal.colors.colorConverter.to_rgb(color)
1328 r, g, b = color[0:3]
1330 return ROOT.TColor.GetColor(r, g, b)
1335 title = hist.GetTitle().
split(
';')[0]
1336 xlabel = hist.GetXaxis().GetTitle()
1337 ylabel = hist.GetYaxis().GetTitle()
1339 if options.title.startswith(
'+'):
1340 title += options.title[1:]
1342 title = options.title
1344 if options.xlabel.startswith(
'+'):
1345 xlabel += options.xlabel[1:]
1347 xlabel = options.xlabel
1349 if options.ylabel.startswith(
'+'):
1350 ylabel += options.ylabel[1:]
1352 ylabel = options.ylabel
1353 return title, xlabel, ylabel
1357 if counter % divisor == 0:
1358 print((
"\r%i plots of %i written to %s/ in %s format" %
1359 (counter, nplots, output, ext)), end=
' ')
1364 destination = joined(options.output,
'allplots.pdf')
1366 for path, dirs, files
in os.walk(options.output):
1367 paths += [joined(path, x)
for x
in files
if x.endswith(
'.pdf')]
1369 print(
"No output files, so no merged pdf was made")
1371 print(
"Writing %s..." % destination)
1372 os.system(
'gs -q -dBATCH -dNOPAUSE -sDEVICE=pdfwrite ' 1373 '-dAutoRotatePages=/All ' 1374 '-sOutputFile=%s %s' % (destination,
' '.
join(paths)))
1378 page_text = ROOT.TText()
1379 page_text.SetTextSize(options.numbering_size_root)
1380 page_text.SetTextAlign(options.numbering_align_root)
1381 page_text.DrawTextNDC(options.numbering_x_root, options.numbering_y_root,
1382 '%i' % options.numbering)
1386 nbins = hist.GetNbinsX()
1387 x = 0.5 * (hist.GetBinLowEdge(nbins) +
1388 hist.GetBinLowEdge(nbins + 1))
1389 y = stack.GetMinimum(
'nostack')
1394 nbins = hist.GetNbinsX()
1395 x = 0.5 * (hist.GetBinLowEdge(1) +
1396 hist.GetBinLowEdge(2))
1397 y = stack.GetMinimum(
'nostack')
1402 bin_text = ROOT.TText()
1403 bin_text.SetTextSize(
min(1. / nbins, 0.04))
1404 bin_text.SetTextAlign(12)
1405 bin_text.SetTextAngle(90)
1406 bin_text.SetTextColor(13)
1407 bin_text.SetTextFont(42)
1408 bin_text.DrawText(x, y, text)
1412 hist.SetMaximum(histmax * options.top_padding_factor)
1413 if options.xmin
is not None and options.xmax
is not None:
1414 hist.GetXaxis().SetRangeUser(options.xmin, options.xmax)
1415 elif options.xmin
is not None:
1416 original_max = hist.GetBinLowEdge(hist.GetNbinsX() + 1)
1417 hist.GetXaxis().SetRangeUser(options.xmin, original_max)
1418 elif options.xmax
is not None:
1419 original_min = hist.GetBinLowEdge(1)
1420 hist.GetXaxis().SetRangeUser(original_min, options.xmax)
1421 if options.ymin
is not None:
1422 hist.SetMinimum(options.ymin)
1423 if options.ymax
is not None:
1424 hist.SetMaximum(options.ymax)
1428 hist.GetXaxis().SetBinLabel(1,
'')
1429 if ';' in hist.GetTitle():
1431 titles = hist.GetTitle().
split(
';')
1432 if len(titles) > 1: titles[1] =
'' 1433 hist.SetTitle(
';'.
join(titles))
1435 hist.GetXaxis().SetTitle(
'')
1437 if (
not options.logy
and 1438 hist.GetMaximum() > 0
and 1439 hist.GetMinimum() / hist.GetMaximum() < 0.25):
1440 hist.SetMinimum(hist.GetMaximum() / 10000)
1442 ratio_file = options.legend_entries[options.ratio - 1]
1443 if options.efficiency:
1444 hist.GetYaxis().SetTitle(options.efficiency_label % locals())
1446 hist.GetYaxis().SetTitle(options.ratio_label % locals())
1453 margins = [ROOT.gStyle.GetPadTopMargin(), ROOT.gStyle.GetPadBottomMargin()]
1454 useable_height = 1 - (margins[0] + margins[1])
1456 pad = ROOT.TPad(
'mainPad',
'mainPad', 0., 0., 1., 1.)
1457 pad.SetFillStyle(4000)
1459 pad.SetBottomMargin(margins[1] + ratio_fraction * useable_height)
1460 pad_ratio = ROOT.TPad(
'ratioPad',
'ratioPad', 0., 0., 1., 1.);
1461 pad_ratio.SetFillStyle(4000)
1463 pad_ratio.SetTopMargin(margins[0] + (1 - ratio_fraction) * useable_height)
1464 return pad, pad_ratio
1468 x1, y1, x2, y2 = axes.get_position().get_points().
flatten().tolist()
1471 lower_height = height * ratio_fraction
1472 upper_height = height - lower_height
1473 lower_axes = fig.add_axes([x1, y1, width, lower_height], axisbg=
'None')
1474 upper_axes = fig.add_axes([x1, y1 + lower_height, width, upper_height],
1475 axisbg=
'None', sharex=lower_axes)
1479 plt.setp(upper_axes.get_xticklabels(), visible=
False)
1480 return upper_axes, lower_axes
1483 denom = hists[ratio_index]
1484 if options.efficiency:
1485 ratios = [hist.divide_wilson(denom)
for hist
in hists]
1487 ratios = [hist.divide(denom)
for hist
in hists]
1488 ratios[ratio_index] =
None 1493 ratio_index = options.ratio - 1
1494 ratio_file = options.legend_entries[ratio_index]
1495 if options.efficiency:
1496 ylabel = options.efficiency_label % locals()
1498 ylabel = options.ratio_label % locals()
1499 multigraph = ROOT.TMultiGraph(
"ratio_multi",
1500 ";%s;%s" % (xlabel, ylabel))
1501 if options.stack
and options.data:
1502 numerator = hists[ratio_index]
1504 hists.pop(ratio_index)
1505 denominator = hists[0]
1506 for hist
in hists[1:]:
1508 hists = [numerator, denominator]
1512 if i == ratio_index:
1514 graph = ratio_hist.TGraph()
1515 graph.SetLineColor(options.line_colors[i])
1516 graph.SetMarkerColor(options.marker_colors[i])
1517 graph.SetMarkerStyle(options.marker_styles[i])
1518 graph.SetMarkerSize(options.marker_sizes[i])
1519 multigraph.Add(graph)
1520 multigraph.Draw(options.draw_graph)
1521 multigraph.GetYaxis().SetNdivisions(507)
1522 if options.ratio_max
is not None: multigraph.SetMaximum(options.ratio_max)
1523 if options.ratio_min
is not None: multigraph.SetMinimum(options.ratio_min)
1524 multigraph.Draw(options.draw_graph)
1529 ratio_index = options.ratio - 1
1531 if options.stack
and options.data:
1532 numerator = hists[ratio_index]
1534 hists.pop(ratio_index)
1535 denominator = hists[0]
1536 for hist
in hists[1:]:
1538 hists = [numerator, denominator]
1542 if i == ratio_index:
1544 ratio_hist.y = [item
or 0
for item
in ratio_hist.y]
1545 stack.add(ratio_hist, fmt=options.marker_styles[i],
1546 color=options.fill_colors[i],
1547 ecolor=options.errorbar_colors[i])
1548 if options.ratio_logy:
1549 axes.set_yscale(
'log')
1550 stack.errorbar(yerr=
True)
1551 axes.yaxis.set_major_locator(
1552 mpl.ticker.MaxNLocator(nbins=5, steps=[1, 2, 5, 10]))
1553 if options.ratio_max
is not None: axes.set_ylim(ymax=options.ratio_max)
1554 if options.ratio_min
is not None: axes.set_ylim(ymin=options.ratio_min)
1555 ratio_file = options.legend_entries[ratio_index]
1556 if options.efficiency:
1557 axes.set_ylabel(options.efficiency_label % locals())
1559 axes.set_ylabel(options.ratio_label % locals())
1560 axes.yaxis.tick_right()
1561 axes.yaxis.set_label_position(
'right')
1562 axes.yaxis.label.set_rotation(-90)
1566 files = [x
for x
in files
if x.endswith(filetype)]
1567 output_file = open(joined(path,
'index.html'),
'w')
1568 previous_dirs = [x
for x
in path.split(
'/')
if x]
1569 ndirs = len(previous_dirs)
1570 back_nav = [
'<a href="%s">%s</a>' %
1571 (
'../' * (ndirs - i - 1) +
'index.html', previous_dirs[i])
1572 for i
in range(ndirs)]
1573 back_nav =
'/'.
join(back_nav) +
'/' 1574 forward_nav = [
'<li><a href="%s/index.html">%s</a>/</li>' % (x, x)
1576 forward_nav =
'\n '.
join(forward_nav)
1577 imgtemplate =
'<a name="%(plot)s"><a href="index.html#%(plot)s">' 1578 if filetype.lower() ==
'svg':
1579 imgtemplate += (
'<object type="image/svg+xml" data="%(plot)s" ' 1580 'width=%(width)i height=%(height)i></object>')
1582 imgtemplate +=
'<img src="%(plot)s" height=%(height)i width=%(width)i>' 1583 imgtemplate +=
'</a></a>' 1586 plots += imgtemplate % locals() +
'\n' 1587 plots = re.sub(
'((\\n<a.*){%i})' % ncolumns,
r'\1<br>', plots)
1588 output_file.write(template % locals())
1593 closest =
lambda l,y: l.index(
min(l, key=
lambda x:
abs(x-y)))
1594 match = re.match(
r'([^x]*)x([^x]*)', expression)
1595 lower, upper =
float(match.group(1)),
float(match.group(2))
1596 lowbin = closest(xedges, lower) + 1
1597 highbin = closest(xedges, upper)
1598 return lowbin, highbin
1602 legend_height =
min(options.legend_entry_height * options.nhists + 0.02,
1603 options.max_legend_height)
1604 if isinstance(options.legend_location, int):
1605 options.legend_location = options.legend_codes[options.legend_location]
1606 elif options.legend_location.lower() ==
'none':
1607 options.legend_location =
None 1608 if options.legend_location:
1609 if 'upper' in options.legend_location:
1610 top = options.legend_upper_bound
1611 bottom = options.legend_upper_bound - legend_height
1612 elif 'lower' in options.legend_location:
1613 bottom = options.legend_lower_bound
1614 top = options.legend_lower_bound + legend_height
1616 top = 0.5 + legend_height / 2
1617 bottom = 0.5 - legend_height / 2
1618 if 'left' in options.legend_location:
1619 left = options.legend_left_bound
1620 right = options.legend_left_bound + options.legend_width
1621 elif 'right' in options.legend_location:
1622 right = options.legend_right_bound
1623 left = options.legend_right_bound - options.legend_width
1625 right = 0.5 + options.legend_width / 2
1626 left = 0.5 - options.legend_width / 2
1627 return [left, bottom, right, top]
1631 if 'mpl' not in globals().
keys():
1632 global r2m, mpl, np, plt
1634 import matplotlib
as mpl
1636 print(
"Unable to access matplotlib")
1639 mpldict = {
'png' :
'AGG',
1643 if ext
not in mpldict:
1644 raise ValueError(
"%s is not an output type recognized by " 1646 mpl.use(mpldict[ext])
1647 global Hist, Hist2D, HistStack
1648 import rootplot.root2matplotlib
as r2m
1649 from rootplot.root2matplotlib
import Hist, Hist2D, HistStack
1650 import matplotlib.pyplot
as plt
1653 for target
in targets:
1654 if os.path.basename(target) != os.path.basename(targets[0]):
1659 for target
in targets:
1660 if target != targets[0]:
1665 targets = [target.split(
'/')
for target
in targets]
1666 for i
in range(len(targets[0])):
1667 for target
in targets:
1668 if target[i] != targets[0][i]:
1669 return [
'/'.
join(target[i:])
for target
in targets]
1672 if len(targets) >= 2:
1674 if not allsame([d.split(
'/')[-1]
for d
in diffs]):
1679 plotpath =
'/'.
join(diffs[0].
split(
'/')[1:])
1680 title = diffs[0].
split(
'/')[-1]
1681 legentries = [d.split(
'/')[0]
for d
in diffs]
1683 plotpath = targets[0]
1685 legentries = [f[:-5]
for f
in filenames]
1686 return plotpath, title, legentries
1690 def comma_separator(obj, objtype, nhists):
1692 if isinstance(obj, list):
1694 if isinstance(obj, str)
and ',' in obj:
1696 return [objtype(x)
for x
in obj.split(
',')]
1698 return [eval(objtype)(x)
for x
in obj.split(
',')]
1700 return [objtype(obj)
for i
in range(nhists)]
1702 return [eval(objtype)(obj)
for i
in range(nhists)]
1703 nhists = options.nhists
1705 plotpath, title, legentries =
get_plotpath(options.filenames,
1707 options.plotpath = plotpath
1708 if not options.title:
1709 options.title = title
1710 if not options.legend_entries:
1711 options.legend_entries = legentries
1712 options.legend_entries = comma_separator(options.legend_entries,
1714 options.scale = comma_separator(options.scale, float, nhists)
1715 if options.efficiency_split: options.ratio_max = 1.
1716 if nhists > 1: options.draw2D =
None 1718 plot_style =
'histfill' 1719 if options.bar: plot_style =
'bar' 1720 elif options.barh: plot_style =
'barh' 1721 elif options.barcluster: plot_style =
'barcluster' 1722 elif options.errorbar: plot_style =
'errorbar' 1723 elif options.hist: plot_style =
'hist' 1724 elif options.histfill: plot_style =
'histfill' 1725 if options.stack: plot_style =
'stack' 1726 if not options.markers
and use_mpl:
1727 options.marker_styles = [
'o' for i
in range(nhists)]
1728 if not options.line_colors:
1729 options.line_colors = options.colors
1730 if not options.fill_colors:
1731 options.fill_colors = options.colors
1732 if not options.marker_colors:
1733 options.marker_colors = options.colors
1735 if not options.line_styles:
1736 options.line_styles = [
'solid' for i
in range(nhists)]
1737 if not options.plot_styles:
1738 options.plot_styles = [plot_style
for i
in range(nhists)]
1739 if not options.errorbar_colors:
1740 options.errorbar_colors = [
None for i
in range(nhists)]
1741 if not options.alphas:
1742 options.alphas = [options.alpha
for i
in range(nhists)]
1744 if not options.line_styles:
1745 options.line_styles = [1
for i
in range(nhists)]
1746 if not options.draw_commands:
1748 options.draw_commands = [
'stack ' + options.draw
1749 for i
in range(nhists)]
1751 options.draw_commands = [options.draw
1752 for i
in range(nhists)]
1753 if not options.fill_styles:
1755 options.fill_styles = [
None for i
in range(nhists)]
1758 options.fill_styles = [1001
for i
in range(nhists)]
1760 options.fill_styles = [0
for i
in range(nhists)]
1761 if not options.marker_sizes:
1763 if use_mpl: size = mpl.rcParams[
'lines.markersize']
1764 else: size = ROOT.gStyle.GetMarkerSize()
1767 options.marker_sizes = [size
for i
in range(nhists)]
1769 i = options.data - 1
1770 options.line_styles[i] = options.data_linestyle
1771 options.line_colors[i] = options.data_color
1772 options.fill_colors[i] = options.data_color
1773 options.marker_colors[i] = options.data_color
1775 options.plot_styles[i] =
'errorbar' 1777 options.fill_styles[i] = 0
1778 options.draw_commands[i] =
'e' 1779 options.marker_styles[i] = options.data_marker
1780 if not options.marker_sizes[i]:
1782 options.marker_sizes[i] = mpl.rcParams[
'lines.markersize']
1784 options.marker_sizes[i] = ROOT.gStyle.GetMarkerSize()
1785 if nhists == 2
and options.mc_color:
1786 options.fill_colors[(i+1)%2] = options.mc_color
1787 for opt
in [x
for x
in options.keys()
if 'colors' in x]:
1789 colors = options[opt]
1790 options[opt] = [
parse_color(x,
not use_mpl)
for x
in colors]
1791 except AttributeError:
1795 plotname = os.path.basename(options.plotpath)
1796 for option, value, regexes
in options.options_by_histname:
1797 for regex
in regexes:
1798 if re.match(regex, plotname):
1799 setattr(options, option, value)
1802 if options.ymin <= 0:
1804 options.top_padding_factor = options.top_padding_factor_log
1809 pools =
map(tuple, args) * kwds.get(
'repeat', 1)
1812 result = [x+[y]
for x
in result
for y
in pool]
1818 import matplotlib
as mpl
1819 figsize =
'x'.
join([
str(x)
for x
in mpl.rcParams[
'figure.figsize']])
1821 figsize =
'x'.
join([
str(ROOT.gStyle.GetCanvasDefW()),
1822 str(ROOT.gStyle.GetCanvasDefH())])
1825 def addopt(group, *args, **kwargs):
1826 if use_mpl
and 'mpl' in kwargs:
1827 opts = kwargs[
'mpl']
1828 kwargs =
dict(kwargs, **opts)
1829 if not use_mpl
and 'root' in kwargs:
1830 opts = kwargs[
'root']
1831 kwargs =
dict(kwargs, **opts)
1832 if 'opts' in locals():
1835 if 'metadefault' in kwargs:
1836 val = kwargs.pop(
'metadefault')
1837 kwargs[
'default'] = val
1838 kwargs[
'metavar'] = val
1839 if 'metavar' in kwargs
and ' ' in str(kwargs[
'metavar']):
1840 kwargs[
'metavar']=
"'%s'" % kwargs[
'metavar']
1841 group.add_option(*args, **kwargs)
1843 help_formatter = optparse.IndentedHelpFormatter()
1844 parser = optparse.OptionParser(usage=usage, formatter=help_formatter,
1845 version=
'%s %s' % (
'%prog', __version__))
1846 Group = optparse.OptionGroup
1847 g_control =
Group(parser,
"Control the overall behavior of rootplot")
1848 g_output =
Group(parser,
"Control the output format")
1849 g_hist =
Group(parser,
"Manipulate your histograms")
1850 g_style =
Group(parser,
"Fine-tune your style")
1851 parser.add_option_group(g_control)
1852 parser.add_option_group(g_output)
1853 parser.add_option_group(g_hist)
1854 parser.add_option_group(g_style)
1856 addopt(g_control,
'--config', action=
'store_true',
1857 help=
"do nothing but write a template configuration file " 1858 "called rootplot_config.py")
1859 addopt(g_control,
'--debug', action=
'store_true',
1860 help=
"turn off error-catching to more easily identify errors")
1861 addopt(g_control,
'--path', metavar=
"'.*'", default=
'.*',
1862 help=
"only process plot(s) matching this regular expression")
1863 addopt(g_control,
'--processors', type=
'int',
1865 help=
"the number of parallel plotting processes to create")
1867 addopt(g_output,
'-e',
'--ext', metadefault=
'png',
1868 help=
"choose an output extension")
1869 addopt(g_output,
'--merge', action=
'store_true', default=
False,
1870 help=
"sets --ext=pdf and creates a merged file " 1871 "containing all plots")
1872 addopt(g_output,
'--noclean', action=
'store_true', default=
False,
1873 help=
"skips destroying the output directory before drawing")
1874 addopt(g_output,
'--output', metadefault=
'plots',
1875 help=
"name of output directory")
1876 addopt(g_output,
'--numbering', action=
'store_true', default=
False,
1877 help=
"print page numbers on images and prepend them to file names; " 1878 "numbering will respect the order of objects in the ROOT file")
1879 addopt(g_output,
'--size', metadefault=figsize,
1880 root=opt(help=
"set the canvas size to 'width x height' in pixels"),
1881 mpl=opt(help=
"set the canvas size to 'width x height' in inches"))
1883 addopt(g_output,
'--dpi', type=float,
1884 metadefault=mpl.rcParams[
'figure.dpi'],
1885 help=
"set the resolution of the output files")
1886 addopt(g_output,
'--transparent', action=
"store_true", default=
False,
1887 help=
"use a transparent background")
1889 addopt(g_hist,
'-n',
'--area-normalize', action=
'store_true',
1890 default=
False, help=
"area-normalize the histograms")
1891 addopt(g_hist,
'--scale', metavar=
'VAL', default=1.,
1892 help=
"normalize all histograms by VAL, or by individual values " 1893 "if VAL is a comma-separated list")
1894 addopt(g_hist,
'--normalize', metavar=
'NUM', type=
'int', default=0,
1895 help=
"normalize to the NUMth target (starting with 1)")
1896 addopt(g_hist,
'--norm-range', metavar=
'LOWxHIGH',
1897 help=
"only use the specified data range in determining " 1898 "the normalization")
1899 addopt(g_hist,
'--rebin', metavar=
"N", type=int,
1900 help=
"group together bins in sets of N")
1901 addopt(g_hist,
'--ratio', type=
'int', default=0, metavar=
'NUM',
1902 help=
"divide histograms by the NUMth target (starting from 1)")
1903 addopt(g_hist,
'--ratio-split', type=
'int', default=0, metavar=
'NUM',
1904 help=
"same as --ratio, but split the figure in two, displaying " 1905 "the normal figure on top and the ratio on the bottom")
1906 addopt(g_hist,
'--efficiency', type=
'int', default=0, metavar=
'NUM',
1907 help=
"same as --ratio, but with errors computed by the Wilson " 1909 addopt(g_hist,
'--efficiency-split', type=
'int', default=0, metavar=
'NUM',
1910 help=
"same as --ratio-split, but with errors computed by the Wilson " 1914 addopt(g_style,
'--draw', metadefault=
'p H',
1915 help=
"argument to pass to ROOT's Draw command; try 'e' for " 1916 "errorbars, or 'hist' to make sure no errorbars appear")
1917 addopt(g_style,
'--draw2D',
1918 root=opt(metadefault=
'box',
1919 help=
"argument to pass to TH2::Draw (ignored if multiple " 1920 "targets specified); set " 1921 'to "" to turn off 2D drawing'),
1922 mpl=opt(metadefault=
'box',
1923 help=
"command to use for plotting 2D hists; (ignored if " 1924 "multiple targets specified) " 1925 "choose from 'contour', 'col', 'colz', and 'box'")
1928 addopt(g_style,
'-f',
'--fill', action=
'store_true', default=
False,
1929 help=
"Histograms will have a color fill")
1931 addopt(g_style,
'--errorbar', action=
"store_true", default=
False,
1932 help=
"output a matplotlib errorbar graph")
1933 addopt(g_style,
'--barcluster', action=
"store_true", default=
False,
1934 help=
"output a clustered bar graph")
1935 addopt(g_style,
'--barh', action=
"store_true", default=
False,
1936 help=
"output a horizontal clustered bar graph")
1937 addopt(g_style,
'--bar', action=
"store_true", default=
False,
1938 help=
"output a bar graph with all histograms overlaid")
1939 addopt(g_style,
'--hist', action=
"store_true", default=
False,
1940 help=
"output a matplotlib hist graph (no fill)")
1941 addopt(g_style,
'--histfill', action=
"store_true", default=
False,
1942 help=
"output a matplotlib hist graph with solid fill")
1943 addopt(g_style,
'--stack', action=
"store_true", default=
False,
1944 help=
"stack histograms")
1945 addopt(g_style,
'-m',
'--markers', action=
'store_true', default=
False,
1946 help=
"add markers to histograms")
1947 addopt(g_style,
'--xerr', action=
"store_true", default=
False,
1948 help=
"show width of bins")
1949 addopt(g_style,
'--data', type=
'int', default=0, metavar=
'NUM',
1950 root=opt(help=
"treat the NUMth target (starting from 1) " 1951 "specially, drawing it as black datapoints; to achieve " 1952 "a classic data vs. MC plot, try this along with " 1953 "--stack and --fill"),
1954 mpl=opt(help=
"treat the NUMth target (starting from 1) " 1955 "specially, drawing it as black datapoints; to achieve " 1956 "a classic data vs. MC plot, try this along with --stack"))
1957 addopt(g_style,
'--xmax', type=
'float', default=
None,
1958 help=
"set the maximum value of the x-axis")
1959 addopt(g_style,
'--xmin', type=
'float', default=
None,
1960 help=
"set the minimum value of the x-axis")
1961 addopt(g_style,
'--ymax', type=
'float', default=
None,
1962 help=
"set the maximum value of the y-axis")
1963 addopt(g_style,
'--ymin', type=
'float', default=
None,
1964 help=
"set the minimum value of the y-axis")
1965 addopt(g_style,
'--legend-location', metavar=
'upper right', default=1,
1966 help=
"Place legend in LOC, according to matplotlib " 1967 "location codes; try 'lower left' or 'center'; " 1968 "to turn off, set to 'None'")
1969 addopt(g_style,
'--legend-entries', default=
None, metavar=
"''",
1970 help=
"A comma-separated string giving the labels to " 1971 "appear in the legend")
1973 addopt(g_style,
'--legend-ncols', default=
None, metavar=1,
1974 help=
"Number of columns to use in the legend")
1975 addopt(g_style,
'--title', default=
None,
1976 help=
"replace the plot titles, or append to them by " 1977 "preceeding with a '+'")
1978 addopt(g_style,
'--xlabel', default=
None,
1979 help=
"replace the x-axis labels, or append to them by " 1980 "preceeding with a '+'")
1981 addopt(g_style,
'--ylabel', default=
None,
1982 help=
"replace the y-axis labels, or append to them by " 1983 "preceeding with a '+'")
1984 addopt(g_style,
'--grid', action=
'store_true', default=
False,
1985 help=
"toggle the grid on or off for both axes")
1986 addopt(g_style,
'--gridx', action=
'store_true', default=
False,
1987 help=
"toggle the grid on or off for the x axis")
1988 addopt(g_style,
'--gridy', action=
'store_true', default=
False,
1989 help=
"toggle the grid on or off for the y axis")
1991 addopt(g_style,
'--cmap',
1992 help=
"matplotlib colormap to use for 2D plots")
1993 addopt(g_style,
'--barwidth', metadefault=1.0, type=float,
1994 help=
"fraction of the bin width for bars to fill")
1995 addopt(g_style,
'--alpha', type=
'float', metadefault=0.5,
1996 help=
"set the opacity of fills")
1997 addopt(g_style,
'--logx', action=
'store_true', default=
False,
1998 help=
"force log scale for x axis")
1999 addopt(g_style,
'--logy', action=
'store_true', default=
False,
2000 help=
"force log scale for y axis")
2001 addopt(g_style,
'--overflow', action=
'store_true', default=
False,
2002 help=
"display overflow content in the highest bin")
2003 addopt(g_style,
'--underflow', action=
'store_true', default=
False,
2004 help=
"display underflow content in the lowest bin")
2006 options, arguments = parser.parse_args(
list(argv))
2007 options =
Options(options, arguments, scope=scope)
2008 options.replace = []
2009 if options.processors == 1
or options.ext ==
'C':
2010 global use_multiprocessing
2011 use_multiprocessing =
False 2012 if options.merge: options.ext =
'pdf' def add_from_config_files(options, configs)
def __getattr__(self, key)
def loadROOT(batch=True)
Define additional helping functions.
def parse_color(color, tcolor=False)
def divide_canvas(canvas, ratio_fraction)
def display_underflow(stack, hist)
def walk_rootfile(path, rootfile, options)
def process_options(options)
def __init__(self, options, arguments, scope='global')
def display_overflow(stack, hist)
def process_configs(self, scope)
def make_ratio_hists(hists, options, ratio_index)
def rootplot(args, kwargs)
def display_bin_text(x, y, nbins, text)
def get_plotpath(filenames, targets)
def parse_legend_root(options)
def plotmpl(args, kwargs)
The Application Programming Interface ###############################.
S & print(S &os, JobReport::InputFile const &f)
def parse_size(size_option)
def make_html_index(path, dirs, files, filetype, template, ncolumns, width, height)
def __setattr__(self, key, value)
def display_page_number(options)
def cartesian_product(args, kwds)
def plot_ratio_mpl(axes, hists, options)
def write_to_file(script, destination)
Implementation ######################################################.
def initialize_hists(args, kwargs)
Abs< T >::type abs(const T &t)
def plot_hists_root(hists, options)
def plot_hists_mpl(hists, options)
def parse_range(xedges, expression)
def find_num_processors()
def get_plot_inputs(files, options)
void add(std::map< std::string, TH1 * > &h, TH1 *hist)
static std::string join(char **cmd)
def prep_first_draw(hist, histmax, options)
def rootplotmpl(args, kwargs)
def report_progress(counter, nplots, output, ext, divisor=1)
Classes #############################################################.
def plot_ratio_root(hists, xlabel, options)
def option_diff(default, modified)
def make_calls(api_call, save_call)
def get_labels(hist, options)
def divide_axes(fig, axes, ratio_fraction)
def cli_rootplotmpl()
remove leading blank lines from the templates
def fill_options(args, kwargs, scope)
def append_from_package(self, package, scope)
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run
def parse_arguments(argv, scope='global')