Back to home page

Project CMSSW displayed by LXR

 
 

    


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 ######## Import python libraries #############################################
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 ######## Import ROOT and rootplot libraries ##################################
0056 
0057 from .utilities import RootFile, Hist, Hist2D, HistStack
0058 from .utilities import find_num_processors, loadROOT
0059 
0060 argstring = ' '.join(sys.argv)
0061 ## Use ROOT's batch mode, unless outputting to C macros, since there is
0062 ## a bug in pyROOT that fails to export colors in batch mode
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 ######## Define globals ######################################################
0070 
0071 from .version import __version__          # version number
0072 prog = os.path.basename(sys.argv[0])     # rootplot or rootplotmpl
0073 use_mpl = False                          # set in plotmpl or rootplotmpl
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 ######## Classes #############################################################
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         #### Load variables from configs; scope is 'global' or 'plot'
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 ######## Templates ###########################################################
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 ## remove leading blank lines from the templates
0521 for key, value in globals().items():
0522     if 'template' in key:
0523         globals()[key] = value[1:]
0524 
0525 ##############################################################################
0526 ######## The Command-Line Interface ##########################################
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 ######## The Application Programming Interface ###############################
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     #### Create the output directory structure
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     #### Loop over plots to make, building the necessary calls
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     #### Create scripts for that make the API calls
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     #### Execute the calls
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     ## clean out empty directories
0755     for root, dirs, files in os.walk(options.output):
0756         if not os.listdir(root):
0757             os.rmdir(root)
0758     ## add index.html files to all directories
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 ######## Implementation ######################################################
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     #### Return a dict with the values from modified not present in default.
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     #### Yield (path, folders, objects) for each directory under path.
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     #### Return a list of argument sets to be sent to plot().
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                 #### targets are folders
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     #### Create a plot.
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     #### Create a plot.
1025     fig = plt.figure(1, figsize=options.size)
1026     fig.clf()     # clear figure
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             # Avoid errors due to zero bins with log y axis
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                         # Logy would fail if hist all zeroes
1071                         if not np.nonzero(hist.y)[0].tolist():
1072                             continue
1073                         # Errorbars get messed up when they extend down to zero
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     #### Return a width and height parsed from size_option.
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     #### Return an rgb tuple or a ROOT TColor from a ROOT color index or
1307     #### an rgb(a) tuple.
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     #### Return the appropriate histogram and axis titles for hist.
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     #### Print the current number of finished plots.
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     #### Merge together all the produced plots into one pdf file.
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     #### Add a page number to the top corner of the canvas.
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     #### Add the overflow to the last bin and print 'Overflow' on the bin.
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     #### Add the underflow to the first bin and print 'Underflow' on the bin.
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     #### Overlay TEXT on this bin.
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     #### Set all the pad attributes that depend on the first object drawn.
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, '') # Don't show tick labels
1427             if ';' in hist.GetTitle():
1428                 # dealing with bug in THStack title handling
1429                 titles = hist.GetTitle().split(';')
1430                 if len(titles) > 1: titles[1] = ''
1431                 hist.SetTitle(';'.join(titles))
1432             else:
1433                 hist.GetXaxis().SetTitle('')
1434             ## Avoid overlap of y-axis numbers by supressing zero
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     #### Divide the canvas into two pads; the bottom is ratio_fraction tall.
1449     ## Both pads are set to the full canvas size to maintain font sizes
1450     ## Fill style 4000 used to ensure pad transparency because of this
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     #### Create two subaxes, the lower one taking up ratio_fraction of total.
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     ## Make original axes and the upper ticklabels invisible
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     #### Plot the ratio of each hist in hists to the ratio_indexth hist.
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) # Avoids crowded labels
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     #### Plot the ratio of each hist in hists to the ratio_indexth hist.
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] ## Avoid gaps
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     #### Returns the indices of the low and high bins indicated in expression.
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     #### Return the corners to use for the legend based on options.
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     #### Refine options for this specific plot, based on plotname
1688     def comma_separator(obj, objtype, nhists):
1689         #### Split a comma-separated string into a list.
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         #### Apply extra options based on hist name
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         #### Final setup
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     # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
1806     # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
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     #### Process control options
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     #### Output options
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     #### Histogram manipulation options
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     #### Style options
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     #### Do immediate processing of arguments
2004     options, arguments = parser.parse_args(list(argv))
2005     options = Options(options, arguments, scope=scope)
2006     options.replace = [] # must have default in case not using mpl
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