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