File indexing completed on 2023-03-17 11:16:37
0001 """
0002 An API and a CLI for quickly building complex figures.
0003 """
0004 from __future__ import absolute_import
0005 from __future__ import print_function
0006
0007 from builtins import range
0008 __license__ = '''\
0009 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
0010
0011 Permission is hereby granted, free of charge, to any person obtaining a copy
0012 of this software and associated documentation files (the "Software"), to deal
0013 in the Software without restriction, including without limitation the rights
0014 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0015 copies of the Software, and to permit persons to whom the Software is
0016 furnished to do so, subject to the following conditions:
0017
0018 The above copyright notice and this permission notice shall be included in
0019 all copies or substantial portions of the Software.
0020
0021 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0022 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0023 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0024 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0025 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0026 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0027 THE SOFTWARE.
0028 '''
0029
0030 usage="""\
0031 Usage: %prog [config.py] targets [options]
0032
0033 Targets may be either multiple root files to compare or a single root file
0034 followed by multiple histograms or folders to compare. For example:
0035 %prog fileA.root fileB.root fileC.root
0036 %prog file.root dirA dirB dirC
0037 %prog file.root dirA/hist1 dirA/hist2
0038
0039 Full documentation is available at:
0040 http://packages.python.org/rootplot/"""
0041
0042
0043
0044
0045 import sys
0046 import optparse
0047 import shutil
0048 import math
0049 import os
0050 import re
0051 import tempfile
0052 import copy
0053 from os.path import join as joined
0054
0055
0056
0057
0058
0059 from .utilities import RootFile, Hist, Hist2D, HistStack
0060 from .utilities import find_num_processors, loadROOT
0061
0062 argstring = ' '.join(sys.argv)
0063
0064
0065 batch = (not re.search('--ext[ =]*C', argstring) and
0066 not re.search('-e[ ]*C', argstring))
0067 ROOT = loadROOT(batch=batch)
0068
0069
0070
0071
0072
0073 from .version import __version__
0074 prog = os.path.basename(sys.argv[0])
0075 use_mpl = False
0076 global_opts = ['filenames', 'targets', 'debug', 'path', 'processors',
0077 'merge', 'noclean', 'output', 'numbering', 'html_template',
0078 'ncolumns_html']
0079 try:
0080 import multiprocessing
0081 use_multiprocessing = True
0082 except ImportError:
0083 use_multiprocessing = False
0084
0085
0086
0087
0088
0089 class Options(dict):
0090 def __init__(self, options, arguments, scope='global'):
0091 for opt in dir(options):
0092 value = getattr(options, opt)
0093 if (not opt.startswith('__') and
0094 type(value) in [int, float, str, bool, type(None)]):
0095 self[opt] = value
0096 self.filenames = [x for x in arguments if x.endswith('.root')]
0097 self.configs = [x for x in arguments if x.endswith('.py')]
0098 self.targets = [x for x in arguments if not (x.endswith('.py') or
0099 x.endswith('.root'))]
0100 self.process_configs(scope)
0101 def __setattr__(self, key, value):
0102 self[key] = value
0103 def __getattr__(self, key):
0104 return self[key]
0105 def clean_targets(self):
0106 for i in range(len(self.targets)):
0107 if self.targets[i][-1] == '/':
0108 self.targets[i] = self.targets[i][:-1]
0109 def arguments(self):
0110 return self.filenames + self.targets + self.configs
0111 def kwarg_list(self):
0112 diffs = {}
0113 defaults = parse_arguments([])
0114 for key, value in self.items():
0115 if (key not in ['filenames', 'targets', 'configs'] and
0116 defaults[key] != value):
0117 diffs[key] = value
0118 return diffs
0119 def append_from_package(self, package, scope):
0120 for attribute in dir(package):
0121 if '__' not in attribute:
0122 if ((scope == 'global' and attribute in global_opts) or
0123 (scope == 'plot' and attribute not in global_opts)):
0124 value = getattr(package, attribute)
0125 self[attribute] = value
0126 def process_configs(self, scope):
0127
0128 configdir = tempfile.mkdtemp()
0129 sys.path.insert(0, '')
0130 sys.path.insert(0, configdir)
0131 write_to_file(config_string(),
0132 joined(configdir, 'default_config.py'))
0133 configs = ['default_config.py']
0134 for i, c in enumerate(self.configs):
0135 shutil.copy(c, joined(configdir, 'rpconfig%i.py' % i))
0136 configs.append('rpconfig%i.py' % i)
0137 rc_name = use_mpl and 'rootplotmplrc' or 'rootplotrc'
0138 rc_path = os.path.expanduser('~/.%s' % rc_name)
0139 if os.path.exists(rc_path):
0140 print("Using styles and options from ~/.%s" % rc_name)
0141 shutil.copy(rc_path, joined(configdir, '%s.py' % rc_name))
0142 configs.insert(1, '%s.py' % rc_name)
0143 for f in configs:
0144 myconfig = __import__(f[:-3])
0145 self.append_from_package(myconfig, scope)
0146 self.clean_targets()
0147 shutil.rmtree(configdir)
0148
0149
0150
0151
0152
0153 config_template=r"""
0154 import ROOT # allows access to ROOT colors (e.g. ROOT.kRed)
0155
0156 ##############################################################################
0157 ######## About Config Files ##################################################
0158
0159 ## This file can be generated by running '%prog --config'
0160
0161 ## Options are loaded in the following order:
0162 ## 1. from the command line
0163 ## 2. from the default configuration file
0164 ## 3. from ~/.%progrc
0165 ## 4. from configuration files specified on the command-line
0166 ## This leads to two major points worth understanding:
0167 ## 1. you may delete any lines you like in this file and they will still
0168 ## be loaded correctly from the default
0169 ## 2. values specified here will superceed the same options from the
0170 ## command-line
0171 ## Therefore, you could set, for example, 'xerr = True' in this file,
0172 ## and x-errorbars will always be drawn, regardless of whether '--xerr' is
0173 ## given on the command-line or not. You can do this with any of the command-
0174 ## line options, but note that dashes are translated to underscores, so
0175 ## '--ratio-split=1' becomes 'ratio_split = 1'.
0176
0177 ## Most global style options like default line widths can be set through
0178 root::## a rootlogon.C, as described at:
0179 root::## http://root.cern.ch/drupal/content/how-create-or-modify-style
0180 mpl:::## a matplotlibrc, as described at:
0181 mpl:::## http://matplotlib.sourceforge.net/users/customizing.html
0182
0183 ##############################################################################
0184 ######## Specifying Files and Targets ########################################
0185
0186 ## You can specify the files to run on through the 'filenames' variable rather
0187 ## than entering them at the command-line, for example:
0188 ## filenames = ['histTTbar.root', 'histZmumu.root']
0189
0190 ## Likewise, you can specify target histograms or directories here rather than
0191 ## on the command-line, for example:
0192 ## targets = ['barrel/15to20', 'barrel/20to30']
0193
0194 ## You might also want to specify fancy labels for the legend here rather
0195 ## than on the command-line:
0196 root::## legend_entries = [r'#bar{t}t', r'Z#rightarrow#mu#mu']
0197 mpl:::## legend_entries = [r'$\bar{t}t$', r'$Z\rightarrow\mu\mu$']
0198
0199 ##############################################################################
0200 ######## Different Options for Different Targets #############################
0201
0202 ## Leave these lists empty to have them automatically filled according to the
0203 ## command-line options. Any list that is filled must be at least as long
0204 ## as the number of targets or it will throw an error.
0205
0206 line_colors = [] # normally filled by options.colors
0207 fill_colors = [] # normally filled by options.colors
0208 marker_colors = [] # normally filled by options.colors
0209 mpl:::errorbar_colors = [] # color for bars around the central value
0210 root::marker_sizes = [] # in pixels
0211 mpl:::marker_sizes = [] # in points
0212 root::line_styles = [] # 1 (solid), 2 (dashed), 4 (dashdot), 3 (dotted), ...
0213 mpl:::line_styles = [] # 'solid', 'dashed', 'dashdot', 'dotted'
0214 root::fill_styles = [] # 0 (hollow), 1001 (solid), 2001 (hatched), ...
0215 mpl:::fill_styles = [] # None, '/', '\', '|', '-', '+', 'x', 'o', 'O', ...
0216 root::draw_commands = [] # a TH1::Draw option, include 'stack' to make stacked
0217 mpl:::plot_styles = [] # 'bar', 'hist', 'errorbar', 'stack'
0218 mpl:::alphas = [] # transparencies for fills (value from 0 to 1)
0219
0220 ##############################################################################
0221 ######## Global Style Options ################################################
0222
0223 ## Colors can be specified as (r, g, b) tuples (with range 0. to 1. or range
0224 root::## 0 to 255), or ROOT color constants (ROOT.kBlue or 600)
0225 mpl:::## 0 to 255), ROOT color constants (ROOT.kBlue or 600), or any matplotlib
0226 mpl:::## color specification (names like 'blue' or 'b')
0227
0228 colors = [
0229 ## a default set of contrasting colors the author happens to like
0230 ( 82, 124, 219), # blue
0231 (212, 58, 143), # red
0232 (231, 139, 77), # orange
0233 (145, 83, 207), # purple
0234 (114, 173, 117), # green
0235 ( 67, 77, 83), # dark grey
0236 ]
0237
0238 ## Used when --marker_styles is specified; more info available at:
0239 root::## http://root.cern.ch/root/html/TAttMarker.html
0240 mpl:::## http://matplotlib.sourceforge.net/api/
0241 mpl:::## artist_api.html#matplotlib.lines.Line2D.set_marker
0242 marker_styles = [
0243 mpl::: 'o', 's', '^', 'x', '*', 'D', 'h', '1'
0244 root:: 4, # circle
0245 root:: 25, # square
0246 root:: 26, # triangle
0247 root:: 5, # x
0248 root:: 30, # five-pointed star
0249 root:: 27, # diamond
0250 root:: 28, # cross
0251 root:: 3, # asterisk
0252 ]
0253
0254 #### Styles for --data
0255 root::data_linestyle = 1
0256 mpl:::data_linestyle = 'solid'
0257 data_color = (0,0,0) # black
0258 mc_color = (50, 150, 150) # used when there are exactly 2 targets; set to
0259 # None to pick up the normal color
0260 root::data_marker = 4 # marker style (circle)
0261 mpl:::data_marker = 'o' # marker style
0262
0263 #### Settings for --ratio-split or --efficiency-split
0264 ratio_max = None
0265 ratio_min = None
0266 ratio_logy = False
0267 ratio_fraction = 0.3 # Fraction of the canvas that bottom plot occupies
0268 ratio_label = 'Ratio to %(ratio_file)s' # Label for the bottom plot
0269 efficiency_label = 'Efficiency vs. %(ratio_file)s'
0270
0271 #### Titles produced by --area-normalize and --normalize
0272 area_normalized_title = 'Fraction of Events in Bin'
0273 target_normalized_title = 'Events Normalized to %(norm_file)s'
0274
0275 #### Overflow and underflow text labels
0276 overflow_text = ' Overflow'
0277 underflow_text = ' Underflow'
0278 mpl:::overflow_size = 'small'
0279 mpl:::overflow_alpha = 0.5
0280
0281 #### Define how much headroom to add to the plot
0282 top_padding_factor = 1.2
0283 top_padding_factor_log = 5. # used when --logy is set
0284
0285 #### Plotting options based on histogram names
0286 ## Apply options to histograms whose names match regular expressions
0287 ## The tuples are of the form (option_name, value_to_apply, list_of_regexs)
0288 ## ex: to rebin by 4 all histograms containing 'pt' or starting with 'eta':
0289 ## ('rebin', 4, ['.*pt.*', 'eta.*'])
0290 options_by_histname = [
0291 ('area_normalize', True, []),
0292 ]
0293
0294 root::#### Legend
0295 root::legend_width = 0.38 # Fraction of canvas width
0296 root::legend_entry_height = 0.05 # Fraction of canvas height
0297 root::max_legend_height = 0.4 # Fraction of canvas height
0298 root::legend_left_bound = 0.20 # For left justification
0299 root::legend_right_bound = 0.95 # For right justification
0300 root::legend_upper_bound = 0.91 # For top justification
0301 root::legend_lower_bound = 0.15 # For bottom justification
0302 root::legend_codes = { 1 : 'upper right',
0303 root:: 2 : 'upper left',
0304 root:: 3 : 'lower left',
0305 root:: 4 : 'lower right',
0306 root:: 5 : 'right',
0307 root:: 6 : 'center left',
0308 root:: 7 : 'center right',
0309 root:: 8 : 'lower center',
0310 root:: 9 : 'upper center',
0311 root:: 10 : 'center',
0312 root:: }
0313 root::
0314 root::#### Page numbers
0315 root::numbering_size_root = 0.03 # Fraction of canvas width
0316 root::numbering_align_root = 33 # Right-top adjusted
0317 root::numbering_x_root = 0.97 # Fraction of canvas width
0318 root::numbering_y_root = 0.985 # Fraction of canvas height
0319 root::
0320 root::#### Draw style for TGraph
0321 root::draw_graph = 'ap'
0322 root::
0323 root::#### This code snippet will be executed after the histograms have all
0324 root::#### been drawn, allowing you to add decorations to the canvas
0325 root::decoration_root = '''
0326 root::## Draw a line to indicate a cut
0327 root::#line = ROOT.TLine(5.,0.,5.,9.e9)
0328 root::#line.Draw()
0329 root::## Add a caption
0330 root::#tt = ROOT.TText()
0331 root::#tt.DrawTextNDC(0.6, 0.15, "CMS Preliminary")
0332 root::'''
0333 mpl:::#### Legend
0334 mpl:::#### These options will override legend_location, allowing more precise control
0335 mpl:::## Upper right corner of legend in figure coordinates
0336 mpl:::legend_figure_bbox = None # [1.0, 1.0] for legend outside the axes
0337 mpl:::## Upper right corner of legend in axes coordinates
0338 mpl:::legend_axes_bbox = None
0339 mpl:::
0340 mpl:::#### Page numbers
0341 mpl:::numbering_size_mpl = 'small'
0342 mpl:::numbering_ha_mpl = 'right'
0343 mpl:::numbering_va_mpl = 'top'
0344 mpl:::numbering_x_mpl = 0.98 # Fraction of canvas width
0345 mpl:::numbering_y_mpl = 0.98 # Fraction of canvas height
0346 mpl:::
0347 mpl:::#### Rotation for text x-axis labels
0348 mpl:::xlabel_rotation = -15
0349 mpl:::xlabel_alignment = 'left'
0350 mpl:::xlabel_alignmenth = 'bottom' # For barh
0351 mpl:::
0352 mpl:::#### Convert ROOT symbols to proper LaTeX, for matplotlib plotting
0353 mpl:::## By default, matplotlib renders only symbols between $'s as TeX, but if
0354 mpl:::## you enable the 'text.usetex' matplotlibrc setting, then everything is handled
0355 mpl:::## by the LaTeX engine on your system, in which case you can go wild with TeX.
0356 mpl:::
0357 mpl:::## ROOT-type strings on left get replaced with LaTeX strings on the right
0358 mpl:::replace = [
0359 mpl::: # some defaults that should work for most cases
0360 mpl::: (' pt ' , r' $p_\mathrm{T}$ '),
0361 mpl::: ('pT ' , r'$p_\mathrm{T}$ '),
0362 mpl::: (' pT' , r' $p_\mathrm{T}$'),
0363 mpl::: ('p_{T}' , r'$p_\mathrm{T}$'),
0364 mpl::: ('E_{T}' , r'$E_\mathrm{T}$'),
0365 mpl::: ('#eta' , r'$\eta$'),
0366 mpl::: ('#phi' , r'$\phi$'),
0367 mpl::: ('fb^{-1}' , r'$\mathrm{fb}^{-1}$'),
0368 mpl::: ('pb^{-1}' , r'$\mathrm{pb}^{-1}$'),
0369 mpl::: ('<' , r'$<$'),
0370 mpl::: ('>' , r'$>$'),
0371 mpl::: ('#' , r''),
0372 mpl::: ]
0373 mpl:::
0374 mpl:::## If you include 'use_regexp' as the first item, the patterns to be replaced
0375 mpl:::## will function as regular expressions using python's re module rather than
0376 mpl:::## as simple text. The example below turn's ROOT's superscript and subscript
0377 mpl:::## syntax into LaTeX:
0378 mpl:::
0379 mpl:::## replace = [
0380 mpl:::## ('use_regexp', True),
0381 mpl:::## (r'\^\{(.*)\}', r'$^{\1}$'),
0382 mpl:::## (r'\_\{(.*)\}', r'$_{\1}$'),
0383 mpl:::## ]
0384 mpl:::
0385 mpl:::#### A function that will be executed after all histograms have been drawn.
0386 mpl:::#### It can be used to add extra decorations to your figure.
0387 mpl:::def decoration_mpl(figure, axeses, path, options, hists):
0388 mpl::: #### Draw a line to indicate a cut
0389 mpl::: ## axeses[0].axvline(5., color='red', linestyle='--')
0390 mpl::: #### Add a caption
0391 mpl::: ## figure.text(0.6, 0.15, "CMS Preliminary")
0392 mpl::: return
0393
0394 ##############################################################################
0395 ######## HTML Output #########################################################
0396
0397 #### Number of columns for images in HTML output
0398 ncolumns_html = 2
0399
0400 #### Provide a template for the html index files
0401 html_template=r'''
0402 <html>
0403 <head>
0404 <link rel='shortcut icon' href='http://packages.python.org/rootplot/_static/rootplot.ico'>
0405 <link href='http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:bold' rel='stylesheet' type='text/css'>
0406 <style type='text/css'>
0407 body { padding: 10px; font-family:Arial, Helvetica, sans-serif;
0408 font-size:15px; color:#FFF; font-size: large;
0409 background-image: url(
0410 'http://packages.python.org/rootplot/_static/tile.jpg');}
0411 img { border: solid black 1px; margin:10px; }
0412 object { border: solid black 1px; margin:10px; }
0413 h1 { text-shadow: 2px 2px 2px #000;
0414 font-size:105px; color:#fff; border-bottom: solid black 1px;
0415 font-size: 300%%; font-family: 'Yanone Kaffeesatz'}
0416 a, a:active, a:visited {
0417 color:#FADA00; text-decoration:none; }
0418 a:hover{ color:#FFFF00; text-decoration:none;
0419 text-shadow: 0px 0px 5px #fff; }
0420 </style>
0421 <title>%(path)s</title>
0422 </head>
0423 <body>
0424 <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>
0425 <h1>Navigation</h1>
0426 %(back_nav)s
0427 <ul>
0428 %(forward_nav)s
0429 </ul>
0430 <h1>Images</h1>
0431 %(plots)s
0432 <p style='font-size: x-small; text-align: center;'>
0433 <a href='http://www.greepit.com/resume-template/resume.htm'>
0434 Based on a template by Sarfraz Shoukat</a></p>
0435 </body>
0436 </html>
0437 '''
0438 """
0439
0440 multi_call_template = '''
0441 calls.append("""
0442 %s
0443 %s
0444 """)
0445 '''
0446
0447 allplots_template = '''
0448 ## This file contains all the necessary calls to the rootplot API to produce
0449 ## the same set of plots that were created from the command-line.
0450
0451 ## You can use this file to intercept the objects and manipulate them before
0452 ## the figure is saved, making any custom changes that are not possible from
0453 ## the command-line.
0454
0455 ## 'objects' is a python dictionary containing all the elements used in the
0456 ## plot, including 'hists', 'legend', etc.
0457 ## ex: objects['hists'] returns a list of histograms
0458
0459 try:
0460 ## the normal way to import rootplot
0461 from rootplot import plot, plotmpl
0462 except ImportError:
0463 ## special import for CMSSW installations of rootplot
0464 from PhysicsTools.PythonAnalysis.rootplot import plot, plotmpl
0465
0466 import os
0467 os.chdir('..') # return to the directory with the ROOT files
0468
0469 %(call_statements)s
0470 '''
0471
0472 allplots_multi_template = '''
0473 ## This file is the same as allplots.py, except that it uses multiprocessing
0474 ## to make better use of machines with multiple cores
0475
0476 try:
0477 ## the normal way to import rootplot
0478 from rootplot import plot, plotmpl
0479 from rootplot.core import report_progress
0480 except ImportError:
0481 ## special import for CMSSW installations of rootplot
0482 from PhysicsTools.PythonAnalysis.rootplot import plot, plotmpl
0483 from PhysicsTools.PythonAnalysis.rootplot.core import report_progress
0484 import ROOT
0485 import multiprocessing as multi
0486
0487 import os
0488 os.chdir('..') # return to the directory with the ROOT files
0489
0490 calls = []
0491
0492 %(call_statements)s
0493
0494 queue = multi.JoinableQueue()
0495 qglobals = multi.Manager().Namespace()
0496 qglobals.nfinished = 0
0497 qglobals.ntotal = len(calls)
0498 for call in calls:
0499 queue.put(call)
0500
0501 def qfunc(queue, qglobals):
0502 from Queue import Empty
0503 while True:
0504 try: mycall = queue.get(timeout=5)
0505 except (Empty, IOError): break
0506 exec(mycall)
0507 ROOT.gROOT.GetListOfCanvases().Clear()
0508 qglobals.nfinished += 1
0509 report_progress(qglobals.nfinished, qglobals.ntotal,
0510 '%(output)s', '%(ext)s')
0511 queue.task_done()
0512
0513 for i in range(%(processors)i):
0514 p = multi.Process(target=qfunc, args=(queue, qglobals))
0515 p.daemon = True
0516 p.start()
0517 queue.join()
0518 report_progress(len(calls), len(calls), '%(output)s', '%(ext)s')
0519 print ''
0520 '''
0521
0522
0523 for key, value in globals().items():
0524 if 'template' in key:
0525 globals()[key] = value[1:]
0526
0527
0528
0529
0530 def cli_rootplotmpl():
0531 """
0532 An application for plotting histograms from a ROOT file with |matplotlib|.
0533
0534 It is invoked from the command-line as ``rootplotmpl``.
0535 """
0536 global use_mpl
0537 use_mpl = True
0538 cli_rootplot()
0539
0540 def cli_rootplot():
0541 """
0542 An application for plotting histograms from a ROOT file.
0543
0544 It is invoked from the command-line as ``rootplot``.
0545 """
0546 options = parse_arguments(sys.argv[1:])
0547 optdiff = options.kwarg_list()
0548 if options.debug:
0549 rootplot(*options.arguments(), **optdiff)
0550 else:
0551 try:
0552 rootplot(*options.arguments(), **optdiff)
0553 except Exception as e:
0554 print("Error:", e)
0555 print("For usage details, call '%s --help'" % prog)
0556 sys.exit(1)
0557
0558
0559
0560
0561 def plotmpl(*args, **kwargs):
0562 """
0563 call signature::
0564
0565 plotmpl(file1, file2, file3, ..., target, **kwargs):
0566
0567 build a matplotlib figure, pulling the *target* histogram from each of the
0568 *files*.
0569
0570 call signature::
0571
0572 plotmpl(file, target1, target2, target3, ..., **kwargs):
0573
0574 build a matplotlib figure, pulling all *target* histograms from *file*.
0575
0576 With either of these signatures, the plot style is specified through
0577 *kwargs*, which can accept any of the options available to
0578 :mod:`rootplotmpl` at the command-line.
0579
0580 Returns the tuple (*figure*, *axeses*, *stack*, *hists*, *plotpath*).
0581 """
0582 global use_mpl
0583 use_mpl = True
0584 return plot(*args, **kwargs)
0585
0586 def plot(*args, **kwargs):
0587 """
0588 call signature::
0589
0590 plot(file1, file2, file3, ..., target, **kwargs):
0591
0592 build a ROOT canvas, pulling the *target* histogram from each of the
0593 *files*.
0594
0595 call signature::
0596
0597 plotmpl(file, target1, target2, target3, ..., **kwargs):
0598
0599 build a ROOT canvas, pulling all *target* histograms from *file*.
0600
0601 With either of these signatures, the plot style is specified through
0602 *kwargs*, which can accept any of the options available to
0603 :mod:`rootplot` at the command-line.
0604
0605 Returns the tuple (*canvas*, *pads*, *stack*, *hists*, *plotpath*).
0606 """
0607 hists, options = initialize_hists(args, kwargs)
0608 if use_mpl:
0609 return plot_hists_mpl(hists, options)
0610 else:
0611 return plot_hists_root(hists, options)
0612
0613 def rootplotmpl(*args, **kwargs):
0614 """
0615 call signature::
0616
0617 rootplotmpl(file1, file2, file3, ..., **kwargs):
0618
0619 build ROOT canvases from corresponding histograms in each of the *files*.
0620
0621 call signature::
0622
0623 rootplotmpl(file, folder1, folder2, folder3, ..., **kwargs):
0624
0625 build ROOT canvases from corresponding histograms in each of the *folders*
0626 in *file*.
0627
0628 call signature::
0629
0630 rootplotmpl(file, target1, target2, target3, ..., **kwargs):
0631
0632 build a ROOT canvas from each of the *targets* in *file*.
0633
0634 With any of these call signatures, images are generated in an output
0635 directory along with a script with the necessary calls to :func:`plotmpl`
0636 to reproduce each of the canvases. The plot style is specified through
0637 *kwargs*, which can accept any of the options available to
0638 :mod:`rootplotmpl` at the command-line.
0639 """
0640 global use_mpl
0641 use_mpl = True
0642 rootplot(args, kwargs)
0643
0644 def rootplot(*args, **kwargs):
0645 """
0646 call signature::
0647
0648 rootplot(file1, file2, file3, ..., **kwargs):
0649
0650 build ROOT canvases from corresponding histograms in each of the *files*.
0651
0652 call signature::
0653
0654 rootplot(file, folder1, folder2, folder3, ..., **kwargs):
0655
0656 build ROOT canvases from corresponding histograms in each of the *folders*
0657 in *file*.
0658
0659 call signature::
0660
0661 rootplot(file, target1, target2, target3, ..., **kwargs):
0662
0663 build a ROOT canvas from each of the *targets* in *file*.
0664
0665 With any of these call signatures, images are generated in an output
0666 directory along with a script with the necessary calls to :func:`plot`
0667 to reproduce each of the canvases. The plot style is specified through
0668 *kwargs*, which can accept any of the options available to
0669 :mod:`rootplot` at the command-line.
0670 """
0671 if 'config' in kwargs:
0672 write_config()
0673 options = fill_options(args, kwargs, scope='global')
0674 nfiles = len(options.filenames)
0675 ntargets = len(options.targets)
0676 if nfiles < 1:
0677 raise TypeError("%s takes at least 1 filename argument (0 given)" %
0678 prog)
0679 elif ntargets > 0 and nfiles > 1:
0680 raise TypeError("%s cannot accept targets (%i given) when "
0681 "multiple files are specified (%i given)" %
0682 (prog, ntargets, nfiles))
0683 rootfiles = [RootFile(filename) for filename in options.filenames]
0684
0685 if not options.noclean and os.path.exists(options.output):
0686 shutil.rmtree(options.output)
0687 for path, folders, objects in walk_rootfile('', rootfiles[0], options):
0688 if not os.path.exists(joined(options.output, path)):
0689 os.makedirs(joined(options.output, path))
0690
0691 plotargs = get_plot_inputs(rootfiles, options)
0692 call_lists = []
0693 ndigits = int(math.log10(len(plotargs))) + 1
0694 for i, (filenames, targets) in enumerate(plotargs):
0695 argstring = ', '.join(["'%s'" % x for x in (filenames + targets +
0696 options.configs)])
0697 reduced_kwargs = dict(kwargs)
0698 for key, value in reduced_kwargs.items():
0699 if key in global_opts:
0700 del reduced_kwargs[key]
0701 elif isinstance(value, str):
0702 reduced_kwargs[key] = "'%s'" % value
0703 if 'numbering' in kwargs:
0704 reduced_kwargs['numbering'] = i + 1
0705 optstring = ', '.join(['%s=%s' % (key, value)
0706 for key, value in reduced_kwargs.items()])
0707 if optstring:
0708 argstring = "%s, %s" % (argstring, optstring)
0709 plotpath, title, legentries = get_plotpath(filenames, targets)
0710 savepath = joined(options.output, plotpath)
0711 if 'numbering' in reduced_kwargs:
0712 dirs = savepath.split('/')
0713 dirs[-1] = str(i + 1).zfill(ndigits) + dirs[-1]
0714 savepath = '/'.join(dirs)
0715 call_vars = {'argstring' : argstring, 'ext' : options.ext,
0716 'savepath' : savepath}
0717 if use_mpl:
0718 call_vars['trans'] = options.transparent
0719 call_vars['dpi'] = options.dpi
0720 api_call = ("figure, objects = "
0721 "plotmpl(%(argstring)s)" % call_vars)
0722 save_call = ("figure.savefig('%(savepath)s', "
0723 "transparent=%(trans)s, "
0724 "dpi=%(dpi)s)" % call_vars)
0725 else:
0726 api_call = ("canvas, objects = "
0727 "plot(%(argstring)s)" % call_vars)
0728 save_call = "canvas.SaveAs('%(savepath)s.%(ext)s')" % call_vars
0729 call_lists.append([api_call, save_call])
0730
0731 ext = options.ext
0732 output = options.output
0733 processors = options.processors
0734 call_statements = '\n\n'.join([plotcall + '\n' + savecall
0735 for plotcall, savecall in call_lists])
0736 allplots_script = allplots_template % locals()
0737 call_statements = "".join([multi_call_template % (plotcall, savecall)
0738 for plotcall, savecall in call_lists])
0739 allplots_multi_script = allplots_multi_template % locals()
0740 write_to_file(allplots_script, joined(options.output, 'allplots.py'))
0741 write_to_file(allplots_multi_script,
0742 joined(options.output, 'allplots_multi.py'))
0743
0744 if use_multiprocessing:
0745 original_dir = os.getcwd()
0746 os.chdir(options.output)
0747 exec(allplots_multi_script)
0748 os.chdir(original_dir)
0749 else:
0750 for i, call_list in enumerate(call_lists):
0751 make_calls(*call_list)
0752 report_progress(i + 1, len(plotargs), options.output, options.ext)
0753 report_progress(len(plotargs), len(plotargs),
0754 options.output, options.ext)
0755 print('')
0756
0757 for root, dirs, files in os.walk(options.output):
0758 if not os.listdir(root):
0759 os.rmdir(root)
0760
0761 if options.ext in ['png', 'gif', 'svg']:
0762 print("Writing html index files...")
0763 width, height = options.size
0764 if use_mpl:
0765 width, height = [x * options.dpi for x in options.size]
0766 for path, dirs, files in os.walk(options.output):
0767 dirs, files = sorted(dirs), sorted(files)
0768 make_html_index(path, dirs, files, options.ext,
0769 options.html_template, options.ncolumns_html,
0770 width, height)
0771 if options.merge:
0772 merge_pdf(options)
0773
0774
0775
0776
0777
0778 def write_to_file(script, destination):
0779 f = open(destination, 'w')
0780 f.write(script)
0781 f.close()
0782
0783 def make_calls(api_call, save_call):
0784 exec(api_call)
0785 exec(save_call)
0786
0787 def option_diff(default, modified):
0788
0789 diff = {}
0790 for key in dir(default):
0791 default_val = getattr(default, key)
0792 modified_val = getattr(modified, key)
0793 if (type(default_val) in [int, float, str, bool, type(None)] and
0794 key in dir(modified) and default_val != modified_val):
0795 diff[key] = modified_val
0796 return diff
0797
0798 def config_string():
0799 s = config_template
0800 if use_mpl:
0801 s = re.sub('root::.*\n', '', s)
0802 s = s.replace('mpl:::', '')
0803 s = s.replace('%prog', 'rootplotmpl')
0804 else:
0805 s = re.sub('mpl:::.*\n', '', s)
0806 s = s.replace('root::', '')
0807 s = s.replace('%prog', 'rootplot')
0808 return s
0809
0810 def write_config():
0811 if use_mpl:
0812 filename = 'rootplotmpl_config.py'
0813 else:
0814 filename = 'rootplot_config.py'
0815 f = open(filename, 'w')
0816 f.write(config_string())
0817 f.close()
0818 print("Wrote %s to the current directory" % filename)
0819 sys.exit(0)
0820
0821 def add_from_config_files(options, configs):
0822 def append_to_options(config, options):
0823 for attribute in dir(config):
0824 if '__' not in attribute:
0825 attr = getattr(config, attribute)
0826 setattr(options, attribute, attr)
0827 configdir = tempfile.mkdtemp()
0828 sys.path.insert(0, '')
0829 sys.path.insert(0, configdir)
0830 f = open(joined(configdir, 'default_config.py'), 'w')
0831 f.write(config_string())
0832 f.close()
0833 import default_config
0834 append_to_options(default_config, options)
0835 if use_mpl:
0836 rc_name = 'rootplotmplrc'
0837 else:
0838 rc_name = 'rootplotrc'
0839 rc_path = os.path.expanduser('~/.%s' % rc_name)
0840 if os.path.exists(rc_path):
0841 print("Using styles and options from ~/.%s" % rc_name)
0842 shutil.copy(rc_path, joined(configdir, '%s.py' % rc_name))
0843 configs.insert(0, '%s.py' % rc_name)
0844 for f in configs:
0845 user_config = __import__(f[:-3])
0846 append_to_options(user_config, options)
0847 shutil.rmtree(configdir)
0848
0849 def walk_rootfile(path, rootfile, options):
0850
0851 keys = rootfile.file.GetDirectory(path).GetListOfKeys()
0852 folders, objects = [], []
0853 for key in keys:
0854 name = key.GetName()
0855 classname = key.GetClassName()
0856 newpath = joined(path, name)
0857 dimension = 0
0858 matches_path = re.match(options.path, newpath)
0859 if 'TDirectory' in classname:
0860 folders.append(name)
0861 elif ('TH1' in classname or 'TGraph' in classname or
0862 classname == 'TProfile'):
0863 dimension = 1
0864 elif options.draw2D and ('TH2' in classname or
0865 classname == 'TProfile2D'):
0866 dimension = 2
0867 if (matches_path and dimension):
0868 objects.append(name)
0869 yield path, folders, objects
0870 for folder in folders:
0871 for x in walk_rootfile(joined(path, folder), rootfile, options):
0872 yield x
0873
0874 def get_plot_inputs(files, options):
0875
0876 target_lists = []
0877 if options.targets:
0878 for path, folders, objects in walk_rootfile('', files[0], options):
0879 if path == options.targets[0]:
0880
0881 for obj in objects:
0882 target_lists.append([joined(t, obj)
0883 for t in options.targets])
0884 else:
0885 target_sets = [set() for f in files]
0886 for i, f in enumerate(files):
0887 for path, folders, objects in walk_rootfile('', f, options):
0888 for obj in objects:
0889 target_sets[i].add(joined(path, obj))
0890 target_set = target_sets[0]
0891 for s in target_sets:
0892 target_set &= s
0893 target_lists = [[t] for t in target_set]
0894 if not target_lists:
0895 return [(options.filenames, options.targets)]
0896 else:
0897 return [(options.filenames, list(t)) for t in target_lists]
0898
0899 def fill_options(args, kwargs, scope):
0900 options = parse_arguments(args, scope=scope)
0901 for key, value in kwargs.items():
0902 options[key] = value
0903 options.process_configs(scope=scope)
0904 options.size = parse_size(options.size)
0905 options.split = options.ratio_split or options.efficiency_split
0906 options.ratio = (options.ratio or options.efficiency or
0907 options.ratio_split or options.efficiency_split)
0908 options.efficiency = options.efficiency or options.efficiency_split
0909 if len(options.filenames) > 1 or len(options.targets) > 1:
0910 options.draw2D = None
0911 return options
0912
0913 def plot_hists_root(hists, options):
0914
0915 canvas = ROOT.TCanvas("canvas", "",
0916 int(options.size[0]), int(options.size[1]))
0917 isTGraph = 'TGraph' in hists[0].rootclass
0918 objects = {'pads': [canvas]}
0919 if options.ratio:
0920 if options.split:
0921 objects['pads'] = divide_canvas(canvas, options.ratio_fraction)
0922 objects['pads'][0].cd()
0923 else:
0924 hists = make_ratio_hists(hists, options, options.ratio - 1)
0925 isTGraph = True
0926 if options.xerr:
0927 ROOT.gStyle.SetErrorX()
0928 histmax, first_draw, roothists = None, True, []
0929 if isTGraph:
0930 objects['multigraph'] = ROOT.TMultiGraph()
0931 else:
0932 objects['stack'] = ROOT.THStack(
0933 'st%s' % os.path.basename(options.plotpath),
0934 '%s;%s;%s' % (hists[0].title,
0935 hists[0].xlabel, hists[0].ylabel))
0936 for i, hist in enumerate(hists):
0937 if not hist: continue
0938 name = "%s_%i" % (options.plotpath, i)
0939 if isTGraph:
0940 roothist = hist.TGraph(name=name)
0941 elif isinstance(hist, Hist):
0942 roothist = hist.TH1F(name=name.replace('/', '__'))
0943 else:
0944 roothist = hist.TH2F(name=name)
0945 roothist.SetLineStyle(options.line_styles[i])
0946 roothist.SetLineColor(options.line_colors[i])
0947 roothist.SetFillColor(options.fill_colors[i])
0948 roothist.SetMarkerColor(options.marker_colors[i])
0949 roothist.SetFillStyle(options.fill_styles[i])
0950 roothist.SetMarkerStyle(options.marker_styles[i])
0951 roothist.SetMarkerSize(options.marker_sizes[i])
0952 roothists.append(roothist)
0953 if (isinstance(hist, Hist) and not isTGraph and
0954 'stack' in options.draw_commands[i]):
0955 objects['stack'].Add(roothist)
0956 if 'stack' in objects and objects['stack'].GetHists():
0957 histmax = objects['stack'].GetMaximum()
0958 for roothist in roothists:
0959 histmax = max(histmax, roothist.GetMaximum())
0960 dimension = 1
0961 if isinstance(hist, Hist2D):
0962 dimension = 2
0963 if options.gridx or options.grid:
0964 for pad in objects['pads']:
0965 pad.SetGridx(not pad.GetGridx())
0966 if options.gridy or options.grid:
0967 objects['pads'][0].SetGridy(not objects['pads'][0].GetGridy())
0968 objects['legend'] = ROOT.TLegend(*parse_legend_root(options))
0969 for com in options.draw_commands:
0970 if 'stack' in com:
0971 first_draw = prep_first_draw(objects['stack'], histmax, options)
0972 com = com.replace('stack', '')
0973 objects['stack'].Draw(com)
0974 break
0975 for i, roothist in enumerate(roothists):
0976 if isTGraph:
0977 objects['multigraph'].Add(roothist)
0978 elif dimension == 1:
0979 if 'stack' not in options.draw_commands[i]:
0980 if first_draw:
0981 first_draw = prep_first_draw(roothist, histmax, options)
0982 roothist.Draw(options.draw_commands[i])
0983 else:
0984 roothist.Draw("same " + options.draw_commands[i])
0985 else:
0986 roothist.Draw(options.draw2D)
0987 legendopt = 'lp'
0988 if options.fill_styles[i]: legendopt += 'f'
0989 if 'e' in options.draw_commands[i]: legendopt += 'e'
0990 objects['legend'].AddEntry(roothist, options.legend_entries[i],
0991 legendopt)
0992 if isTGraph:
0993 objects['multigraph'].Draw(options.draw_graph)
0994 prep_first_draw(objects['multigraph'], histmax, options)
0995 objects['multigraph'].Draw(options.draw_graph)
0996 if options.split and dimension == 1:
0997 objects['pads'][1].cd()
0998 objects['ratio_multigraph'] = plot_ratio_root(
0999 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()
1004 if options.logx:
1005 for pad in objects['pads']:
1006 pad.SetLogx(True)
1007 if options.logy:
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:
1013 display_page_number(options)
1014 if roothist.InheritsFrom('TH1'):
1015 if options.overflow:
1016 display_overflow(objects['stack'], roothist)
1017 if options.underflow:
1018 display_underflow(objects['stack'], roothist)
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
1024
1025 def plot_hists_mpl(hists, options):
1026
1027 fig = plt.figure(1, figsize=options.size)
1028 fig.clf()
1029 axes = plt.axes()
1030 objects = {'axes' : [axes, axes]}
1031 if options.ratio:
1032 if options.split:
1033 objects['axes'] = divide_axes(fig, axes, options.ratio_fraction)
1034 axes = objects['axes'][0]
1035 fig.sca(axes)
1036 else:
1037 hists = make_ratio_hists(hists, options, options.ratio - 1)
1038 refhist = hists[0]
1039 if refhist is None:
1040 refhist = hists[1]
1041 fullstack, objects['stack'] = HistStack(), HistStack()
1042 histmax, allempty = None, True
1043 for i, hist in enumerate(hists):
1044 if hist and hist.entries:
1045 allempty = False
1046 if isinstance(hist, Hist):
1047
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])
1057 fullstack.add(hist)
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))
1064 if allempty:
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):
1068 if hist:
1069 if options.plot_styles[i] == "errorbar":
1070 if options.logy:
1071 axes.set_yscale('log')
1072
1073 if not np.nonzero(hist.y)[0].tolist():
1074 continue
1075
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],
1081 yerr=True,
1082 xerr=options.xerr,
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],
1090 log=options.logy,
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]:
1098 histtype = 'step'
1099 if 'fill' in options.plot_styles[i]:
1100 histtype = 'stepfilled'
1101 hist.hist(alpha=options.alphas[i],
1102 histtype=histtype,
1103 log=options.logy,
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)
1109 if options.logx:
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])
1130 if options.logy:
1131 my_min = fullstack.min(threshold=1.1e-10)
1132 rounded_min = 1e100
1133 while (rounded_min > my_min):
1134 rounded_min /= 10
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:
1156 axes.xaxis.grid()
1157 if options.gridy or options.grid:
1158 axes.yaxis.grid()
1159 if (options.legend_location != 'None' or options.legend_axes_bbox or
1160 options.legend_figure_bbox):
1161 try:
1162 options.legend_location = int(options.legend_location)
1163 except ValueError:
1164 pass
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}
1170 else:
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:
1178 if options.cmap:
1179 drawfunc(cmap=options.cmap)
1180 else:
1181 drawfunc()
1182 else:
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
1188 else:
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())
1195 else:
1196 set_ylabel(options.ratio_label % locals())
1197 else:
1198 set_ylabel(r2m.replace(refhist.ylabel, options.replace))
1199 set_xlabel(r2m.replace(refhist.xlabel, options.replace))
1200 if options.split:
1201 fig.sca(objects['axes'][1])
1202 plot_ratio_mpl(objects['axes'][1], hists, options)
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,
1209 options, hists)
1210 objects['hists'] = hists
1211 return fig, objects
1212
1213 def initialize_hists(args, kwargs):
1214 options = fill_options(args, kwargs, scope='plot')
1215 if use_mpl:
1216 load_matplotlib(options.ext)
1217 nfiles = len(options.filenames)
1218 ntargets = len(options.targets)
1219 if nfiles < 1:
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)" %
1224 (nfiles, ntargets))
1225 options.nhists = max(nfiles, ntargets)
1226 process_options(options)
1227 files = [RootFile(filename) for filename in options.filenames]
1228 hists = []
1229 stack_integral = 0.
1230 for i, (f, target) in enumerate(cartesian_product(files, options.targets)):
1231 try:
1232 roothist = f.file.Get(target)
1233 except ReferenceError:
1234 hists.append(None)
1235 continue
1236 try:
1237 isTGraph = not roothist.InheritsFrom('TH1')
1238 except TypeError:
1239 raise TypeError("'%s' does not appear to be a valid target" %
1240 target)
1241 dimension = 1
1242 if use_mpl:
1243 stacked = 'stack' in options.plot_styles[i]
1244 else:
1245 stacked = 'stack' in options.draw_commands[i]
1246 if not isTGraph:
1247 dimension = roothist.GetDimension()
1248 roothist.Scale(options.scale[i])
1249 if options.rebin:
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
1257 if dimension == 1:
1258 hist = Hist(roothist, label=options.legend_entries[i],
1259 title=title, xlabel=xlabel, ylabel=ylabel)
1260 else:
1261 hist = Hist2D(roothist, label=options.legend_entries[i],
1262 title=title, xlabel=xlabel, ylabel=ylabel)
1263 if stacked:
1264 stack_integral += roothist.Integral()
1265 roothist.Delete()
1266 hists.append(hist)
1267 for i, hist in enumerate(hists):
1268 if use_mpl:
1269 stacked = 'stack' in options.plot_styles[i]
1270 else:
1271 stacked = 'stack' in options.draw_commands[i]
1272 if dimension == 1:
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:
1278 if sum(hist.y):
1279 if stacked:
1280 hist.scale(1./stack_integral)
1281 else:
1282 hist.scale(1./sum(hist.y))
1283 if options.normalize:
1284 numerhist = hists[options.normalize - 1]
1285 if options.norm_range:
1286 lowbin, highbin = parse_range(hist.xedges,
1287 options.norm_range)
1288 numer = numerhist.TH1F().Integral(lowbin, highbin)
1289 denom = hist.TH1F().Integral(lowbin, highbin)
1290 else:
1291 numer = sum(numerhist.y)
1292 denom = sum(hist.y)
1293 if stacked:
1294 denom = stack_integral
1295 if denom:
1296 hist.scale(numer / denom)
1297 return hists, options
1298
1299 def parse_size(size_option):
1300
1301 try:
1302 xpos = size_option.find('x')
1303 return float(size_option[:xpos]), float(size_option[xpos+1:])
1304 except TypeError:
1305 return size_option
1306
1307 def parse_color(color, tcolor=False):
1308
1309
1310 if color is None:
1311 return None
1312 elif color == 'none' or color == 'None':
1313 return 'none'
1314 r, g, b = 0, 0, 0
1315 try:
1316 color = ROOT.gROOT.GetColor(color)
1317 r, g, b = color.GetRed(), color.GetGreen(), color.GetBlue()
1318 except TypeError:
1319 try:
1320 if max(color) > 1.:
1321 color = [x/256. for x in color][0:3]
1322 except TypeError:
1323 pass
1324 try:
1325 color = mpal.colors.colorConverter.to_rgb(color)
1326 except NameError:
1327 pass
1328 r, g, b = color[0:3]
1329 if tcolor:
1330 return ROOT.TColor.GetColor(r, g, b)
1331 return (r, g, b)
1332
1333 def get_labels(hist, options):
1334
1335 title = hist.GetTitle().split(';')[0]
1336 xlabel = hist.GetXaxis().GetTitle()
1337 ylabel = hist.GetYaxis().GetTitle()
1338 if options.title:
1339 if options.title.startswith('+'):
1340 title += options.title[1:]
1341 else:
1342 title = options.title
1343 if options.xlabel:
1344 if options.xlabel.startswith('+'):
1345 xlabel += options.xlabel[1:]
1346 else:
1347 xlabel = options.xlabel
1348 if options.ylabel:
1349 if options.ylabel.startswith('+'):
1350 ylabel += options.ylabel[1:]
1351 else:
1352 ylabel = options.ylabel
1353 return title, xlabel, ylabel
1354
1355 def report_progress(counter, nplots, output, ext, divisor=1):
1356
1357 if counter % divisor == 0:
1358 print(("\r%i plots of %i written to %s/ in %s format" %
1359 (counter, nplots, output, ext)), end=' ')
1360 sys.stdout.flush()
1361
1362 def merge_pdf(options):
1363
1364 destination = joined(options.output, 'allplots.pdf')
1365 paths = []
1366 for path, dirs, files in os.walk(options.output):
1367 paths += [joined(path, x) for x in files if x.endswith('.pdf')]
1368 if not paths:
1369 print("No output files, so no merged pdf was made")
1370 return
1371 print("Writing %s..." % destination)
1372 os.system('gs -q -dBATCH -dNOPAUSE -sDEVICE=pdfwrite '
1373 '-dAutoRotatePages=/All '
1374 '-sOutputFile=%s %s' % (destination, ' '.join(paths)))
1375
1376 def display_page_number(options):
1377
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)
1383
1384 def display_overflow(stack, hist):
1385
1386 nbins = hist.GetNbinsX()
1387 x = 0.5 * (hist.GetBinLowEdge(nbins) +
1388 hist.GetBinLowEdge(nbins + 1))
1389 y = stack.GetMinimum('nostack')
1390 display_bin_text(x, y, nbins, 'Overflow')
1391
1392 def display_underflow(stack, hist):
1393
1394 nbins = hist.GetNbinsX()
1395 x = 0.5 * (hist.GetBinLowEdge(1) +
1396 hist.GetBinLowEdge(2))
1397 y = stack.GetMinimum('nostack')
1398 display_bin_text(x, y, nbins, 'Underflow')
1399
1400 def display_bin_text(x, y, nbins, text):
1401
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)
1409
1410 def prep_first_draw(hist, histmax, options):
1411
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)
1425 if options.ratio:
1426 if options.split:
1427 hist.Draw()
1428 hist.GetXaxis().SetBinLabel(1, '')
1429 if ';' in hist.GetTitle():
1430
1431 titles = hist.GetTitle().split(';')
1432 if len(titles) > 1: titles[1] = ''
1433 hist.SetTitle(';'.join(titles))
1434 else:
1435 hist.GetXaxis().SetTitle('')
1436
1437 if (not options.logy and
1438 hist.GetMaximum() > 0 and
1439 hist.GetMinimum() / hist.GetMaximum() < 0.25):
1440 hist.SetMinimum(hist.GetMaximum() / 10000)
1441 else:
1442 ratio_file = options.legend_entries[options.ratio - 1]
1443 if options.efficiency:
1444 hist.GetYaxis().SetTitle(options.efficiency_label % locals())
1445 else:
1446 hist.GetYaxis().SetTitle(options.ratio_label % locals())
1447 return False
1448
1449 def divide_canvas(canvas, ratio_fraction):
1450
1451
1452
1453 margins = [ROOT.gStyle.GetPadTopMargin(), ROOT.gStyle.GetPadBottomMargin()]
1454 useable_height = 1 - (margins[0] + margins[1])
1455 canvas.Clear()
1456 pad = ROOT.TPad('mainPad', 'mainPad', 0., 0., 1., 1.)
1457 pad.SetFillStyle(4000)
1458 pad.Draw()
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)
1462 pad_ratio.Draw()
1463 pad_ratio.SetTopMargin(margins[0] + (1 - ratio_fraction) * useable_height)
1464 return pad, pad_ratio
1465
1466 def divide_axes(fig, axes, ratio_fraction):
1467
1468 x1, y1, x2, y2 = axes.get_position().get_points().flatten().tolist()
1469 width = x2 - x1
1470 height = y2 - y1
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)
1476
1477 axes.set_xticks([])
1478 axes.set_yticks([])
1479 plt.setp(upper_axes.get_xticklabels(), visible=False)
1480 return upper_axes, lower_axes
1481
1482 def make_ratio_hists(hists, options, ratio_index):
1483 denom = hists[ratio_index]
1484 if options.efficiency:
1485 ratios = [hist.divide_wilson(denom) for hist in hists]
1486 else:
1487 ratios = [hist.divide(denom) for hist in hists]
1488 ratios[ratio_index] = None
1489 return ratios
1490
1491 def plot_ratio_root(hists, xlabel, options):
1492
1493 ratio_index = options.ratio - 1
1494 ratio_file = options.legend_entries[ratio_index]
1495 if options.efficiency:
1496 ylabel = options.efficiency_label % locals()
1497 else:
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]
1503 hists = hists[:]
1504 hists.pop(ratio_index)
1505 denominator = hists[0]
1506 for hist in hists[1:]:
1507 denominator += hist
1508 hists = [numerator, denominator]
1509 ratio_index = 1
1510 for i, ratio_hist in enumerate(make_ratio_hists(hists, options,
1511 ratio_index)):
1512 if i == ratio_index:
1513 continue
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)
1525 return multigraph
1526
1527 def plot_ratio_mpl(axes, hists, options):
1528
1529 ratio_index = options.ratio - 1
1530 stack = HistStack()
1531 if options.stack and options.data:
1532 numerator = hists[ratio_index]
1533 hists = hists[:]
1534 hists.pop(ratio_index)
1535 denominator = hists[0]
1536 for hist in hists[1:]:
1537 denominator += hist
1538 hists = [numerator, denominator]
1539 ratio_index = 1
1540 for i, ratio_hist in enumerate(make_ratio_hists(hists, options,
1541 ratio_index)):
1542 if i == ratio_index:
1543 continue
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())
1558 else:
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)
1563
1564 def make_html_index(path, dirs, files, filetype, template, ncolumns,
1565 width, height):
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)
1575 for x in dirs]
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>')
1581 else:
1582 imgtemplate += '<img src="%(plot)s" height=%(height)i width=%(width)i>'
1583 imgtemplate += '</a></a>'
1584 plots = '\n'
1585 for plot in files:
1586 plots += imgtemplate % locals() + '\n'
1587 plots = re.sub('((\\n<a.*){%i})' % ncolumns, r'\1<br>', plots)
1588 output_file.write(template % locals())
1589 output_file.close()
1590
1591 def parse_range(xedges, expression):
1592
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
1599
1600 def parse_legend_root(options):
1601
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
1615 else:
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
1624 else:
1625 right = 0.5 + options.legend_width / 2
1626 left = 0.5 - options.legend_width / 2
1627 return [left, bottom, right, top]
1628 return [0, 0, 0, 0]
1629
1630 def load_matplotlib(ext):
1631 if 'mpl' not in globals().keys():
1632 global r2m, mpl, np, plt
1633 try:
1634 import matplotlib as mpl
1635 except ImportError:
1636 print("Unable to access matplotlib")
1637 sys.exit(1)
1638 import numpy as np
1639 mpldict = {'png' : 'AGG',
1640 'pdf' : 'PDF',
1641 'ps' : 'PS',
1642 'svg' : 'SVG'}
1643 if ext not in mpldict:
1644 raise ValueError("%s is not an output type recognized by "
1645 "matplotlib" % ext)
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
1651
1652 def samebase(targets):
1653 for target in targets:
1654 if os.path.basename(target) != os.path.basename(targets[0]):
1655 return False
1656 return True
1657
1658 def allsame(targets):
1659 for target in targets:
1660 if target != targets[0]:
1661 return False
1662 return True
1663
1664 def diffpart(targets):
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]
1670
1671 def get_plotpath(filenames, targets):
1672 if len(targets) >= 2:
1673 diffs = diffpart(targets)
1674 if not allsame([d.split('/')[-1] for d in diffs]):
1675 plotpath = 'plot'
1676 title = 'plot'
1677 legentries = diffs
1678 else:
1679 plotpath = '/'.join(diffs[0].split('/')[1:])
1680 title = diffs[0].split('/')[-1]
1681 legentries = [d.split('/')[0] for d in diffs]
1682 else:
1683 plotpath = targets[0]
1684 title = ''
1685 legentries = [f[:-5] for f in filenames]
1686 return plotpath, title, legentries
1687
1688 def process_options(options):
1689
1690 def comma_separator(obj, objtype, nhists):
1691
1692 if isinstance(obj, list):
1693 return obj
1694 if isinstance(obj, str) and ',' in obj:
1695 try:
1696 return [objtype(x) for x in obj.split(',')]
1697 except TypeError:
1698 return [eval(objtype)(x) for x in obj.split(',')]
1699 try:
1700 return [objtype(obj) for i in range(nhists)]
1701 except TypeError:
1702 return [eval(objtype)(obj) for i in range(nhists)]
1703 nhists = options.nhists
1704 if options.targets:
1705 plotpath, title, legentries = get_plotpath(options.filenames,
1706 options.targets)
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,
1713 str, nhists)
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
1717 if use_mpl:
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
1734 if use_mpl:
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)]
1743 else:
1744 if not options.line_styles:
1745 options.line_styles = [1 for i in range(nhists)]
1746 if not options.draw_commands:
1747 if options.stack:
1748 options.draw_commands = ['stack ' + options.draw
1749 for i in range(nhists)]
1750 else:
1751 options.draw_commands = [options.draw
1752 for i in range(nhists)]
1753 if not options.fill_styles:
1754 if use_mpl:
1755 options.fill_styles = [None for i in range(nhists)]
1756 else:
1757 if options.fill:
1758 options.fill_styles = [1001 for i in range(nhists)]
1759 else:
1760 options.fill_styles = [0 for i in range(nhists)]
1761 if not options.marker_sizes:
1762 if options.markers:
1763 if use_mpl: size = mpl.rcParams['lines.markersize']
1764 else: size = ROOT.gStyle.GetMarkerSize()
1765 else:
1766 size = 0
1767 options.marker_sizes = [size for i in range(nhists)]
1768 if options.data:
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
1774 if use_mpl:
1775 options.plot_styles[i] = 'errorbar'
1776 else:
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]:
1781 if use_mpl:
1782 options.marker_sizes[i] = mpl.rcParams['lines.markersize']
1783 else:
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]:
1788 try:
1789 colors = options[opt]
1790 options[opt] = [parse_color(x, not use_mpl) for x in colors]
1791 except AttributeError:
1792 pass
1793 if options.targets:
1794
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)
1800
1801 if options.logy:
1802 if options.ymin <= 0:
1803 options.ymin = None
1804 options.top_padding_factor = options.top_padding_factor_log
1805
1806 def cartesian_product(*args, **kwds):
1807
1808
1809 pools = map(tuple, args) * kwds.get('repeat', 1)
1810 result = [[]]
1811 for pool in pools:
1812 result = [x+[y] for x in result for y in pool]
1813 for prod in result:
1814 yield tuple(prod)
1815
1816 def parse_arguments(argv, scope='global'):
1817 if use_mpl:
1818 import matplotlib as mpl
1819 figsize = 'x'.join([str(x) for x in mpl.rcParams['figure.figsize']])
1820 else:
1821 figsize = 'x'.join([str(ROOT.gStyle.GetCanvasDefW()),
1822 str(ROOT.gStyle.GetCanvasDefH())])
1823 def opt(**kwargs):
1824 return kwargs
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():
1833 del kwargs['mpl']
1834 del kwargs['root']
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)
1842
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)
1855
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',
1864 metadefault=find_num_processors(),
1865 help="the number of parallel plotting processes to create")
1866
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"))
1882 if use_mpl:
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")
1888
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 "
1908 "score interval")
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 "
1911 "score interval")
1912
1913 if not use_mpl:
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'")
1926 )
1927 if not use_mpl:
1928 addopt(g_style, '-f', '--fill', action='store_true', default=False,
1929 help="Histograms will have a color fill")
1930 if use_mpl:
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")
1972 if use_mpl:
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")
1990 if use_mpl:
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")
2005
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'
2013 return options