CMS 3D CMS Logo

core.py
Go to the documentation of this file.
1 """
2 An API and a CLI for quickly building complex figures.
3 """
4 from __future__ import absolute_import
5 from __future__ import print_function
6 
7 from builtins import range
8 __license__ = '''\
9 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
10 
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17 
18 The above copyright notice and this permission notice shall be included in
19 all copies or substantial portions of the Software.
20 
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 THE SOFTWARE.
28 '''
29 
30 usage="""\
31 Usage: %prog [config.py] targets [options]
32 
33 Targets may be either multiple root files to compare or a single root file
34 followed by multiple histograms or folders to compare. For example:
35  %prog fileA.root fileB.root fileC.root
36  %prog file.root dirA dirB dirC
37  %prog file.root dirA/hist1 dirA/hist2
38 
39 Full documentation is available at:
40  http://packages.python.org/rootplot/"""
41 
42 ##############################################################################
43 ######## Import python libraries #############################################
44 
45 import sys
46 import optparse
47 import shutil
48 import math
49 import os
50 import re
51 import tempfile
52 import copy
53 from os.path import join as joined
54 
55 
56 ##############################################################################
57 ######## Import ROOT and rootplot libraries ##################################
58 
59 from .utilities import RootFile, Hist, Hist2D, HistStack
60 from .utilities import find_num_processors, loadROOT
61 
62 argstring = ' '.join(sys.argv)
63 ## Use ROOT's batch mode, unless outputting to C macros, since there is
64 ## a bug in pyROOT that fails to export colors in batch mode
65 batch = (not re.search('--ext[ =]*C', argstring) and
66  not re.search('-e[ ]*C', argstring))
67 ROOT = loadROOT(batch=batch)
68 
69 
70 ##############################################################################
71 ######## Define globals ######################################################
72 
73 from .version import __version__ # version number
74 prog = os.path.basename(sys.argv[0]) # rootplot or rootplotmpl
75 use_mpl = False # set in plotmpl or rootplotmpl
76 global_opts = ['filenames', 'targets', 'debug', 'path', 'processors',
77  'merge', 'noclean', 'output', 'numbering', 'html_template',
78  'ncolumns_html']
79 try:
80  import multiprocessing
81  use_multiprocessing = True
82 except ImportError:
83  use_multiprocessing = False
84 
85 
86 ##############################################################################
87 ######## Classes #############################################################
88 
89 class Options(dict):
90  def __init__(self, options, arguments, scope='global'):
91  for opt in dir(options):
92  value = getattr(options, opt)
93  if (not opt.startswith('__') and
94  type(value) in [int, float, str, bool, type(None)]):
95  self[opt] = value
96  self.filenames = [x for x in arguments if x.endswith('.root')]
97  self.configs = [x for x in arguments if x.endswith('.py')]
98  self.targets = [x for x in arguments if not (x.endswith('.py') or
99  x.endswith('.root'))]
100  self.process_configs(scope)
101  def __setattr__(self, key, value):
102  self[key] = value
103  def __getattr__(self, key):
104  return self[key]
105  def clean_targets(self):
106  for i in range(len(self.targets)):
107  if self.targets[i][-1] == '/':
108  self.targets[i] = self.targets[i][:-1]
109  def arguments(self):
110  return self.filenames + self.targets + self.configs
111  def kwarg_list(self):
112  diffs = {}
113  defaults = parse_arguments([])
114  for key, value in self.items():
115  if (key not in ['filenames', 'targets', 'configs'] and
116  defaults[key] != value):
117  diffs[key] = value
118  return diffs
119  def append_from_package(self, package, scope):
120  for attribute in dir(package):
121  if '__' not in attribute:
122  if ((scope == 'global' and attribute in global_opts) or
123  (scope == 'plot' and attribute not in global_opts)):
124  value = getattr(package, attribute)
125  self[attribute] = value
126  def process_configs(self, scope):
127  #### Load variables from configs; scope is 'global' or 'plot'
128  configdir = tempfile.mkdtemp()
129  sys.path.insert(0, '')
130  sys.path.insert(0, configdir)
132  joined(configdir, 'default_config.py'))
133  configs = ['default_config.py']
134  for i, c in enumerate(self.configs):
135  shutil.copy(c, joined(configdir, 'rpconfig%i.py' % i))
136  configs.append('rpconfig%i.py' % i)
137  rc_name = use_mpl and 'rootplotmplrc' or 'rootplotrc'
138  rc_path = os.path.expanduser('~/.%s' % rc_name)
139  if os.path.exists(rc_path):
140  print("Using styles and options from ~/.%s" % rc_name)
141  shutil.copy(rc_path, joined(configdir, '%s.py' % rc_name))
142  configs.insert(1, '%s.py' % rc_name)
143  for f in configs:
144  myconfig = __import__(f[:-3])
145  self.append_from_package(myconfig, scope)
146  self.clean_targets()
147  shutil.rmtree(configdir)
148 
149 
150 ##############################################################################
151 ######## Templates ###########################################################
152 
153 config_template=r"""
154 import ROOT # allows access to ROOT colors (e.g. ROOT.kRed)
155 
156 ##############################################################################
157 ######## About Config Files ##################################################
158 
159 ## This file can be generated by running '%prog --config'
160 
161 ## Options are loaded in the following order:
162 ## 1. from the command line
163 ## 2. from the default configuration file
164 ## 3. from ~/.%progrc
165 ## 4. from configuration files specified on the command-line
166 ## This leads to two major points worth understanding:
167 ## 1. you may delete any lines you like in this file and they will still
168 ## be loaded correctly from the default
169 ## 2. values specified here will superceed the same options from the
170 ## command-line
171 ## Therefore, you could set, for example, 'xerr = True' in this file,
172 ## and x-errorbars will always be drawn, regardless of whether '--xerr' is
173 ## given on the command-line or not. You can do this with any of the command-
174 ## line options, but note that dashes are translated to underscores, so
175 ## '--ratio-split=1' becomes 'ratio_split = 1'.
176 
177 ## Most global style options like default line widths can be set through
178 root::## a rootlogon.C, as described at:
179 root::## http://root.cern.ch/drupal/content/how-create-or-modify-style
180 mpl:::## a matplotlibrc, as described at:
181 mpl:::## http://matplotlib.sourceforge.net/users/customizing.html
182 
183 ##############################################################################
184 ######## Specifying Files and Targets ########################################
185 
186 ## You can specify the files to run on through the 'filenames' variable rather
187 ## than entering them at the command-line, for example:
188 ## filenames = ['histTTbar.root', 'histZmumu.root']
189 
190 ## Likewise, you can specify target histograms or directories here rather than
191 ## on the command-line, for example:
192 ## targets = ['barrel/15to20', 'barrel/20to30']
193 
194 ## You might also want to specify fancy labels for the legend here rather
195 ## than on the command-line:
196 root::## legend_entries = [r'#bar{t}t', r'Z#rightarrow#mu#mu']
197 mpl:::## legend_entries = [r'$\bar{t}t$', r'$Z\rightarrow\mu\mu$']
198 
199 ##############################################################################
200 ######## Different Options for Different Targets #############################
201 
202 ## Leave these lists empty to have them automatically filled according to the
203 ## command-line options. Any list that is filled must be at least as long
204 ## as the number of targets or it will throw an error.
205 
206 line_colors = [] # normally filled by options.colors
207 fill_colors = [] # normally filled by options.colors
208 marker_colors = [] # normally filled by options.colors
209 mpl:::errorbar_colors = [] # color for bars around the central value
210 root::marker_sizes = [] # in pixels
211 mpl:::marker_sizes = [] # in points
212 root::line_styles = [] # 1 (solid), 2 (dashed), 4 (dashdot), 3 (dotted), ...
213 mpl:::line_styles = [] # 'solid', 'dashed', 'dashdot', 'dotted'
214 root::fill_styles = [] # 0 (hollow), 1001 (solid), 2001 (hatched), ...
215 mpl:::fill_styles = [] # None, '/', '\', '|', '-', '+', 'x', 'o', 'O', ...
216 root::draw_commands = [] # a TH1::Draw option, include 'stack' to make stacked
217 mpl:::plot_styles = [] # 'bar', 'hist', 'errorbar', 'stack'
218 mpl:::alphas = [] # transparencies for fills (value from 0 to 1)
219 
220 ##############################################################################
221 ######## Global Style Options ################################################
222 
223 ## Colors can be specified as (r, g, b) tuples (with range 0. to 1. or range
224 root::## 0 to 255), or ROOT color constants (ROOT.kBlue or 600)
225 mpl:::## 0 to 255), ROOT color constants (ROOT.kBlue or 600), or any matplotlib
226 mpl:::## color specification (names like 'blue' or 'b')
227 
228 colors = [
229  ## a default set of contrasting colors the author happens to like
230  ( 82, 124, 219), # blue
231  (212, 58, 143), # red
232  (231, 139, 77), # orange
233  (145, 83, 207), # purple
234  (114, 173, 117), # green
235  ( 67, 77, 83), # dark grey
236  ]
237 
238 ## Used when --marker_styles is specified; more info available at:
239 root::## http://root.cern.ch/root/html/TAttMarker.html
240 mpl:::## http://matplotlib.sourceforge.net/api/
241 mpl:::## artist_api.html#matplotlib.lines.Line2D.set_marker
242 marker_styles = [
243 mpl::: 'o', 's', '^', 'x', '*', 'D', 'h', '1'
244 root:: 4, # circle
245 root:: 25, # square
246 root:: 26, # triangle
247 root:: 5, # x
248 root:: 30, # five-pointed star
249 root:: 27, # diamond
250 root:: 28, # cross
251 root:: 3, # asterisk
252  ]
253 
254 #### Styles for --data
255 root::data_linestyle = 1
256 mpl:::data_linestyle = 'solid'
257 data_color = (0,0,0) # black
258 mc_color = (50, 150, 150) # used when there are exactly 2 targets; set to
259  # None to pick up the normal color
260 root::data_marker = 4 # marker style (circle)
261 mpl:::data_marker = 'o' # marker style
262 
263 #### Settings for --ratio-split or --efficiency-split
264 ratio_max = None
265 ratio_min = None
266 ratio_logy = False
267 ratio_fraction = 0.3 # Fraction of the canvas that bottom plot occupies
268 ratio_label = 'Ratio to %(ratio_file)s' # Label for the bottom plot
269 efficiency_label = 'Efficiency vs. %(ratio_file)s'
270 
271 #### Titles produced by --area-normalize and --normalize
272 area_normalized_title = 'Fraction of Events in Bin'
273 target_normalized_title = 'Events Normalized to %(norm_file)s'
274 
275 #### Overflow and underflow text labels
276 overflow_text = ' Overflow'
277 underflow_text = ' Underflow'
278 mpl:::overflow_size = 'small'
279 mpl:::overflow_alpha = 0.5
280 
281 #### Define how much headroom to add to the plot
282 top_padding_factor = 1.2
283 top_padding_factor_log = 5. # used when --logy is set
284 
285 #### Plotting options based on histogram names
286 ## Apply options to histograms whose names match regular expressions
287 ## The tuples are of the form (option_name, value_to_apply, list_of_regexs)
288 ## ex: to rebin by 4 all histograms containing 'pt' or starting with 'eta':
289 ## ('rebin', 4, ['.*pt.*', 'eta.*'])
290 options_by_histname = [
291  ('area_normalize', True, []),
292  ]
293 
294 root::#### Legend
295 root::legend_width = 0.38 # Fraction of canvas width
296 root::legend_entry_height = 0.05 # Fraction of canvas height
297 root::max_legend_height = 0.4 # Fraction of canvas height
298 root::legend_left_bound = 0.20 # For left justification
299 root::legend_right_bound = 0.95 # For right justification
300 root::legend_upper_bound = 0.91 # For top justification
301 root::legend_lower_bound = 0.15 # For bottom justification
302 root::legend_codes = { 1 : 'upper right',
303 root:: 2 : 'upper left',
304 root:: 3 : 'lower left',
305 root:: 4 : 'lower right',
306 root:: 5 : 'right',
307 root:: 6 : 'center left',
308 root:: 7 : 'center right',
309 root:: 8 : 'lower center',
310 root:: 9 : 'upper center',
311 root:: 10 : 'center',
312 root:: }
313 root::
314 root::#### Page numbers
315 root::numbering_size_root = 0.03 # Fraction of canvas width
316 root::numbering_align_root = 33 # Right-top adjusted
317 root::numbering_x_root = 0.97 # Fraction of canvas width
318 root::numbering_y_root = 0.985 # Fraction of canvas height
319 root::
320 root::#### Draw style for TGraph
321 root::draw_graph = 'ap'
322 root::
323 root::#### This code snippet will be executed after the histograms have all
324 root::#### been drawn, allowing you to add decorations to the canvas
325 root::decoration_root = '''
326 root::## Draw a line to indicate a cut
327 root::#line = ROOT.TLine(5.,0.,5.,9.e9)
328 root::#line.Draw()
329 root::## Add a caption
330 root::#tt = ROOT.TText()
331 root::#tt.DrawTextNDC(0.6, 0.15, "CMS Preliminary")
332 root::'''
333 mpl:::#### Legend
334 mpl:::#### These options will override legend_location, allowing more precise control
335 mpl:::## Upper right corner of legend in figure coordinates
336 mpl:::legend_figure_bbox = None # [1.0, 1.0] for legend outside the axes
337 mpl:::## Upper right corner of legend in axes coordinates
338 mpl:::legend_axes_bbox = None
339 mpl:::
340 mpl:::#### Page numbers
341 mpl:::numbering_size_mpl = 'small'
342 mpl:::numbering_ha_mpl = 'right'
343 mpl:::numbering_va_mpl = 'top'
344 mpl:::numbering_x_mpl = 0.98 # Fraction of canvas width
345 mpl:::numbering_y_mpl = 0.98 # Fraction of canvas height
346 mpl:::
347 mpl:::#### Rotation for text x-axis labels
348 mpl:::xlabel_rotation = -15
349 mpl:::xlabel_alignment = 'left'
350 mpl:::xlabel_alignmenth = 'bottom' # For barh
351 mpl:::
352 mpl:::#### Convert ROOT symbols to proper LaTeX, for matplotlib plotting
353 mpl:::## By default, matplotlib renders only symbols between $'s as TeX, but if
354 mpl:::## you enable the 'text.usetex' matplotlibrc setting, then everything is handled
355 mpl:::## by the LaTeX engine on your system, in which case you can go wild with TeX.
356 mpl:::
357 mpl:::## ROOT-type strings on left get replaced with LaTeX strings on the right
358 mpl:::replace = [
359 mpl::: # some defaults that should work for most cases
360 mpl::: (' pt ' , r' $p_\mathrm{T}$ '),
361 mpl::: ('pT ' , r'$p_\mathrm{T}$ '),
362 mpl::: (' pT' , r' $p_\mathrm{T}$'),
363 mpl::: ('p_{T}' , r'$p_\mathrm{T}$'),
364 mpl::: ('E_{T}' , r'$E_\mathrm{T}$'),
365 mpl::: ('#eta' , r'$\eta$'),
366 mpl::: ('#phi' , r'$\phi$'),
367 mpl::: ('fb^{-1}' , r'$\mathrm{fb}^{-1}$'),
368 mpl::: ('pb^{-1}' , r'$\mathrm{pb}^{-1}$'),
369 mpl::: ('<' , r'$<$'),
370 mpl::: ('>' , r'$>$'),
371 mpl::: ('#' , r''),
372 mpl::: ]
373 mpl:::
374 mpl:::## If you include 'use_regexp' as the first item, the patterns to be replaced
375 mpl:::## will function as regular expressions using python's re module rather than
376 mpl:::## as simple text. The example below turn's ROOT's superscript and subscript
377 mpl:::## syntax into LaTeX:
378 mpl:::
379 mpl:::## replace = [
380 mpl:::## ('use_regexp', True),
381 mpl:::## (r'\^\{(.*)\}', r'$^{\1}$'),
382 mpl:::## (r'\_\{(.*)\}', r'$_{\1}$'),
383 mpl:::## ]
384 mpl:::
385 mpl:::#### A function that will be executed after all histograms have been drawn.
386 mpl:::#### It can be used to add extra decorations to your figure.
387 mpl:::def decoration_mpl(figure, axeses, path, options, hists):
388 mpl::: #### Draw a line to indicate a cut
389 mpl::: ## axeses[0].axvline(5., color='red', linestyle='--')
390 mpl::: #### Add a caption
391 mpl::: ## figure.text(0.6, 0.15, "CMS Preliminary")
392 mpl::: return
393 
394 ##############################################################################
395 ######## HTML Output #########################################################
396 
397 #### Number of columns for images in HTML output
398 ncolumns_html = 2
399 
400 #### Provide a template for the html index files
401 html_template=r'''
402  <html>
403  <head>
404  <link rel='shortcut icon' href='http://packages.python.org/rootplot/_static/rootplot.ico'>
405  <link href='http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:bold' rel='stylesheet' type='text/css'>
406  <style type='text/css'>
407  body { padding: 10px; font-family:Arial, Helvetica, sans-serif;
408  font-size:15px; color:#FFF; font-size: large;
409  background-image: url(
410  'http://packages.python.org/rootplot/_static/tile.jpg');}
411  img { border: solid black 1px; margin:10px; }
412  object { border: solid black 1px; margin:10px; }
413  h1 { text-shadow: 2px 2px 2px #000;
414  font-size:105px; color:#fff; border-bottom: solid black 1px;
415  font-size: 300%%; font-family: 'Yanone Kaffeesatz'}
416  a, a:active, a:visited {
417  color:#FADA00; text-decoration:none; }
418  a:hover{ color:#FFFF00; text-decoration:none;
419  text-shadow: 0px 0px 5px #fff; }
420  </style>
421  <title>%(path)s</title>
422  </head>
423  <body>
424  <a style="" href="http://packages.python.org/rootplot/"><img style="position: absolute; top:10 px; right: 10px; border: 0px" src="http://packages.python.org/rootplot/_static/rootplot-logo.png"></a>
425  <h1>Navigation</h1>
426  %(back_nav)s
427  <ul>
428  %(forward_nav)s
429  </ul>
430  <h1>Images</h1>
431  %(plots)s
432  <p style='font-size: x-small; text-align: center;'>
433  <a href='http://www.greepit.com/resume-template/resume.htm'>
434  Based on a template by Sarfraz Shoukat</a></p>
435  </body>
436  </html>
437 '''
438 """
439 
440 multi_call_template = '''
441 calls.append("""
442 %s
443 %s
444 """)
445 '''
446 
447 allplots_template = '''
448 ## This file contains all the necessary calls to the rootplot API to produce
449 ## the same set of plots that were created from the command-line.
450 
451 ## You can use this file to intercept the objects and manipulate them before
452 ## the figure is saved, making any custom changes that are not possible from
453 ## the command-line.
454 
455 ## 'objects' is a python dictionary containing all the elements used in the
456 ## plot, including 'hists', 'legend', etc.
457 ## ex: objects['hists'] returns a list of histograms
458 
459 try:
460  ## the normal way to import rootplot
461  from rootplot import plot, plotmpl
462 except ImportError:
463  ## special import for CMSSW installations of rootplot
464  from PhysicsTools.PythonAnalysis.rootplot import plot, plotmpl
465 
466 import os
467 os.chdir('..') # return to the directory with the ROOT files
468 
469 %(call_statements)s
470 '''
471 
472 allplots_multi_template = '''
473 ## This file is the same as allplots.py, except that it uses multiprocessing
474 ## to make better use of machines with multiple cores
475 
476 try:
477  ## the normal way to import rootplot
478  from rootplot import plot, plotmpl
479  from rootplot.core import report_progress
480 except ImportError:
481  ## special import for CMSSW installations of rootplot
482  from PhysicsTools.PythonAnalysis.rootplot import plot, plotmpl
483  from PhysicsTools.PythonAnalysis.rootplot.core import report_progress
484 import ROOT
485 import multiprocessing as multi
486 
487 import os
488 os.chdir('..') # return to the directory with the ROOT files
489 
490 calls = []
491 
492 %(call_statements)s
493 
494 queue = multi.JoinableQueue()
495 qglobals = multi.Manager().Namespace()
496 qglobals.nfinished = 0
497 qglobals.ntotal = len(calls)
498 for call in calls:
499  queue.put(call)
500 
501 def qfunc(queue, qglobals):
502  from Queue import Empty
503  while True:
504  try: mycall = queue.get(timeout=5)
505  except (Empty, IOError): break
506  exec(mycall)
507  ROOT.gROOT.GetListOfCanvases().Clear()
508  qglobals.nfinished += 1
509  report_progress(qglobals.nfinished, qglobals.ntotal,
510  '%(output)s', '%(ext)s')
511  queue.task_done()
512 
513 for i in range(%(processors)i):
514  p = multi.Process(target=qfunc, args=(queue, qglobals))
515  p.daemon = True
516  p.start()
517 queue.join()
518 report_progress(len(calls), len(calls), '%(output)s', '%(ext)s')
519 print ''
520 '''
521 
522 ## remove leading blank lines from the templates
523 for key, value in globals().items():
524  if 'template' in key:
525  globals()[key] = value[1:]
526 
527 ##############################################################################
528 ######## The Command-Line Interface ##########################################
529 
531  """
532  An application for plotting histograms from a ROOT file with |matplotlib|.
533 
534  It is invoked from the command-line as ``rootplotmpl``.
535  """
536  global use_mpl
537  use_mpl = True
538  cli_rootplot()
539 
541  """
542  An application for plotting histograms from a ROOT file.
543 
544  It is invoked from the command-line as ``rootplot``.
545  """
546  options = parse_arguments(sys.argv[1:])
547  optdiff = options.kwarg_list()
548  if options.debug:
549  rootplot(*options.arguments(), **optdiff)
550  else:
551  try:
552  rootplot(*options.arguments(), **optdiff)
553  except Exception as e:
554  print("Error:", e)
555  print("For usage details, call '%s --help'" % prog)
556  sys.exit(1)
557 
558 ##############################################################################
559 ######## The Application Programming Interface ###############################
560 
561 def plotmpl(*args, **kwargs):
562  """
563  call signature::
564 
565  plotmpl(file1, file2, file3, ..., target, **kwargs):
566 
567  build a matplotlib figure, pulling the *target* histogram from each of the
568  *files*.
569 
570  call signature::
571 
572  plotmpl(file, target1, target2, target3, ..., **kwargs):
573 
574  build a matplotlib figure, pulling all *target* histograms from *file*.
575 
576  With either of these signatures, the plot style is specified through
577  *kwargs*, which can accept any of the options available to
578  :mod:`rootplotmpl` at the command-line.
579 
580  Returns the tuple (*figure*, *axeses*, *stack*, *hists*, *plotpath*).
581  """
582  global use_mpl
583  use_mpl = True
584  return plot(*args, **kwargs)
585 
586 def plot(*args, **kwargs):
587  """
588  call signature::
589 
590  plot(file1, file2, file3, ..., target, **kwargs):
591 
592  build a ROOT canvas, pulling the *target* histogram from each of the
593  *files*.
594 
595  call signature::
596 
597  plotmpl(file, target1, target2, target3, ..., **kwargs):
598 
599  build a ROOT canvas, pulling all *target* histograms from *file*.
600 
601  With either of these signatures, the plot style is specified through
602  *kwargs*, which can accept any of the options available to
603  :mod:`rootplot` at the command-line.
604 
605  Returns the tuple (*canvas*, *pads*, *stack*, *hists*, *plotpath*).
606  """
607  hists, options = initialize_hists(args, kwargs)
608  if use_mpl:
609  return plot_hists_mpl(hists, options)
610  else:
611  return plot_hists_root(hists, options)
612 
613 def rootplotmpl(*args, **kwargs):
614  """
615  call signature::
616 
617  rootplotmpl(file1, file2, file3, ..., **kwargs):
618 
619  build ROOT canvases from corresponding histograms in each of the *files*.
620 
621  call signature::
622 
623  rootplotmpl(file, folder1, folder2, folder3, ..., **kwargs):
624 
625  build ROOT canvases from corresponding histograms in each of the *folders*
626  in *file*.
627 
628  call signature::
629 
630  rootplotmpl(file, target1, target2, target3, ..., **kwargs):
631 
632  build a ROOT canvas from each of the *targets* in *file*.
633 
634  With any of these call signatures, images are generated in an output
635  directory along with a script with the necessary calls to :func:`plotmpl`
636  to reproduce each of the canvases. The plot style is specified through
637  *kwargs*, which can accept any of the options available to
638  :mod:`rootplotmpl` at the command-line.
639  """
640  global use_mpl
641  use_mpl = True
642  rootplot(args, kwargs)
643 
644 def rootplot(*args, **kwargs):
645  """
646  call signature::
647 
648  rootplot(file1, file2, file3, ..., **kwargs):
649 
650  build ROOT canvases from corresponding histograms in each of the *files*.
651 
652  call signature::
653 
654  rootplot(file, folder1, folder2, folder3, ..., **kwargs):
655 
656  build ROOT canvases from corresponding histograms in each of the *folders*
657  in *file*.
658 
659  call signature::
660 
661  rootplot(file, target1, target2, target3, ..., **kwargs):
662 
663  build a ROOT canvas from each of the *targets* in *file*.
664 
665  With any of these call signatures, images are generated in an output
666  directory along with a script with the necessary calls to :func:`plot`
667  to reproduce each of the canvases. The plot style is specified through
668  *kwargs*, which can accept any of the options available to
669  :mod:`rootplot` at the command-line.
670  """
671  if 'config' in kwargs:
672  write_config()
673  options = fill_options(args, kwargs, scope='global')
674  nfiles = len(options.filenames)
675  ntargets = len(options.targets)
676  if nfiles < 1:
677  raise TypeError("%s takes at least 1 filename argument (0 given)" %
678  prog)
679  elif ntargets > 0 and nfiles > 1:
680  raise TypeError("%s cannot accept targets (%i given) when "
681  "multiple files are specified (%i given)" %
682  (prog, ntargets, nfiles))
683  rootfiles = [RootFile(filename) for filename in options.filenames]
684  #### Create the output directory structure
685  if not options.noclean and os.path.exists(options.output):
686  shutil.rmtree(options.output)
687  for path, folders, objects in walk_rootfile('', rootfiles[0], options):
688  if not os.path.exists(joined(options.output, path)):
689  os.makedirs(joined(options.output, path))
690  #### Loop over plots to make, building the necessary calls
691  plotargs = get_plot_inputs(rootfiles, options)
692  call_lists = []
693  ndigits = int(math.log10(len(plotargs))) + 1
694  for i, (filenames, targets) in enumerate(plotargs):
695  argstring = ', '.join(["'%s'" % x for x in (filenames + targets +
696  options.configs)])
697  reduced_kwargs = dict(kwargs)
698  for key, value in reduced_kwargs.items():
699  if key in global_opts:
700  del reduced_kwargs[key]
701  elif isinstance(value, str):
702  reduced_kwargs[key] = "'%s'" % value
703  if 'numbering' in kwargs:
704  reduced_kwargs['numbering'] = i + 1
705  optstring = ', '.join(['%s=%s' % (key, value)
706  for key, value in reduced_kwargs.items()])
707  if optstring:
708  argstring = "%s, %s" % (argstring, optstring)
709  plotpath, title, legentries = get_plotpath(filenames, targets)
710  savepath = joined(options.output, plotpath)
711  if 'numbering' in reduced_kwargs:
712  dirs = savepath.split('/')
713  dirs[-1] = str(i + 1).zfill(ndigits) + dirs[-1]
714  savepath = '/'.join(dirs)
715  call_vars = {'argstring' : argstring, 'ext' : options.ext,
716  'savepath' : savepath}
717  if use_mpl:
718  call_vars['trans'] = options.transparent
719  call_vars['dpi'] = options.dpi
720  api_call = ("figure, objects = "
721  "plotmpl(%(argstring)s)" % call_vars)
722  save_call = ("figure.savefig('%(savepath)s', "
723  "transparent=%(trans)s, "
724  "dpi=%(dpi)s)" % call_vars)
725  else:
726  api_call = ("canvas, objects = "
727  "plot(%(argstring)s)" % call_vars)
728  save_call = "canvas.SaveAs('%(savepath)s.%(ext)s')" % call_vars
729  call_lists.append([api_call, save_call])
730  #### Create scripts for that make the API calls
731  ext = options.ext
732  output = options.output
733  processors = options.processors
734  call_statements = '\n\n'.join([plotcall + '\n' + savecall
735  for plotcall, savecall in call_lists])
736  allplots_script = allplots_template % locals()
737  call_statements = "".join([multi_call_template % (plotcall, savecall)
738  for plotcall, savecall in call_lists])
739  allplots_multi_script = allplots_multi_template % locals()
740  write_to_file(allplots_script, joined(options.output, 'allplots.py'))
741  write_to_file(allplots_multi_script,
742  joined(options.output, 'allplots_multi.py'))
743  #### Execute the calls
744  if use_multiprocessing:
745  original_dir = os.getcwd()
746  os.chdir(options.output)
747  exec(allplots_multi_script)
748  os.chdir(original_dir)
749  else:
750  for i, call_list in enumerate(call_lists):
751  make_calls(*call_list)
752  report_progress(i + 1, len(plotargs), options.output, options.ext)
753  report_progress(len(plotargs), len(plotargs),
754  options.output, options.ext)
755  print('')
756  ## clean out empty directories
757  for root, dirs, files in os.walk(options.output):
758  if not os.listdir(root):
759  os.rmdir(root)
760  ## add index.html files to all directories
761  if options.ext in ['png', 'gif', 'svg']:
762  print("Writing html index files...")
763  width, height = options.size
764  if use_mpl:
765  width, height = [x * options.dpi for x in options.size]
766  for path, dirs, files in os.walk(options.output):
767  dirs, files = sorted(dirs), sorted(files)
768  make_html_index(path, dirs, files, options.ext,
769  options.html_template, options.ncolumns_html,
770  width, height)
771  if options.merge:
772  merge_pdf(options)
773 
774 
775 ##############################################################################
776 ######## Implementation ######################################################
777 
778 def write_to_file(script, destination):
779  f = open(destination, 'w')
780  f.write(script)
781  f.close()
782 
783 def make_calls(api_call, save_call):
784  exec(api_call)
785  exec(save_call)
786 
787 def option_diff(default, modified):
788  #### Return a dict with the values from modified not present in default.
789  diff = {}
790  for key in dir(default):
791  default_val = getattr(default, key)
792  modified_val = getattr(modified, key)
793  if (type(default_val) in [int, float, str, bool, type(None)] and
794  key in dir(modified) and default_val != modified_val):
795  diff[key] = modified_val
796  return diff
797 
799  s = config_template
800  if use_mpl:
801  s = re.sub('root::.*\n', '', s)
802  s = s.replace('mpl:::', '')
803  s = s.replace('%prog', 'rootplotmpl')
804  else:
805  s = re.sub('mpl:::.*\n', '', s)
806  s = s.replace('root::', '')
807  s = s.replace('%prog', 'rootplot')
808  return s
809 
811  if use_mpl:
812  filename = 'rootplotmpl_config.py'
813  else:
814  filename = 'rootplot_config.py'
815  f = open(filename, 'w')
816  f.write(config_string())
817  f.close()
818  print("Wrote %s to the current directory" % filename)
819  sys.exit(0)
820 
821 def add_from_config_files(options, configs):
822  def append_to_options(config, options):
823  for attribute in dir(config):
824  if '__' not in attribute:
825  attr = getattr(config, attribute)
826  setattr(options, attribute, attr)
827  configdir = tempfile.mkdtemp()
828  sys.path.insert(0, '')
829  sys.path.insert(0, configdir)
830  f = open(joined(configdir, 'default_config.py'), 'w')
831  f.write(config_string())
832  f.close()
833  import default_config
834  append_to_options(default_config, options)
835  if use_mpl:
836  rc_name = 'rootplotmplrc'
837  else:
838  rc_name = 'rootplotrc'
839  rc_path = os.path.expanduser('~/.%s' % rc_name)
840  if os.path.exists(rc_path):
841  print("Using styles and options from ~/.%s" % rc_name)
842  shutil.copy(rc_path, joined(configdir, '%s.py' % rc_name))
843  configs.insert(0, '%s.py' % rc_name)
844  for f in configs:
845  user_config = __import__(f[:-3])
846  append_to_options(user_config, options)
847  shutil.rmtree(configdir)
848 
849 def walk_rootfile(path, rootfile, options):
850  #### Yield (path, folders, objects) for each directory under path.
851  keys = rootfile.file.GetDirectory(path).GetListOfKeys()
852  folders, objects = [], []
853  for key in keys:
854  name = key.GetName()
855  classname = key.GetClassName()
856  newpath = joined(path, name)
857  dimension = 0
858  matches_path = re.match(options.path, newpath)
859  if 'TDirectory' in classname:
860  folders.append(name)
861  elif ('TH1' in classname or 'TGraph' in classname or
862  classname == 'TProfile'):
863  dimension = 1
864  elif options.draw2D and ('TH2' in classname or
865  classname == 'TProfile2D'):
866  dimension = 2
867  if (matches_path and dimension):
868  objects.append(name)
869  yield path, folders, objects
870  for folder in folders:
871  for x in walk_rootfile(joined(path, folder), rootfile, options):
872  yield x
873 
874 def get_plot_inputs(files, options):
875  #### Return a list of argument sets to be sent to plot().
876  target_lists = []
877  if options.targets:
878  for path, folders, objects in walk_rootfile('', files[0], options):
879  if path == options.targets[0]:
880  #### targets are folders
881  for obj in objects:
882  target_lists.append([joined(t, obj)
883  for t in options.targets])
884  else:
885  target_sets = [set() for f in files]
886  for i, f in enumerate(files):
887  for path, folders, objects in walk_rootfile('', f, options):
888  for obj in objects:
889  target_sets[i].add(joined(path, obj))
890  target_set = target_sets[0]
891  for s in target_sets:
892  target_set &= s
893  target_lists = [[t] for t in target_set]
894  if not target_lists:
895  return [(options.filenames, options.targets)]
896  else:
897  return [(options.filenames, list(t)) for t in target_lists]
898 
899 def fill_options(args, kwargs, scope):
900  options = parse_arguments(args, scope=scope)
901  for key, value in kwargs.items():
902  options[key] = value
903  options.process_configs(scope=scope)
904  options.size = parse_size(options.size)
905  options.split = options.ratio_split or options.efficiency_split
906  options.ratio = (options.ratio or options.efficiency or
907  options.ratio_split or options.efficiency_split)
908  options.efficiency = options.efficiency or options.efficiency_split
909  if len(options.filenames) > 1 or len(options.targets) > 1:
910  options.draw2D = None
911  return options
912 
913 def plot_hists_root(hists, options):
914  #### Create a plot.
915  canvas = ROOT.TCanvas("canvas", "",
916  int(options.size[0]), int(options.size[1]))
917  isTGraph = 'TGraph' in hists[0].rootclass
918  objects = {'pads': [canvas]}
919  if options.ratio:
920  if options.split:
921  objects['pads'] = divide_canvas(canvas, options.ratio_fraction)
922  objects['pads'][0].cd()
923  else:
924  hists = make_ratio_hists(hists, options, options.ratio - 1)
925  isTGraph = True
926  if options.xerr:
927  ROOT.gStyle.SetErrorX()
928  histmax, first_draw, roothists = None, True, []
929  if isTGraph:
930  objects['multigraph'] = ROOT.TMultiGraph()
931  else:
932  objects['stack'] = ROOT.THStack(
933  'st%s' % os.path.basename(options.plotpath),
934  '%s;%s;%s' % (hists[0].title,
935  hists[0].xlabel, hists[0].ylabel))
936  for i, hist in enumerate(hists):
937  if not hist: continue
938  name = "%s_%i" % (options.plotpath, i)
939  if isTGraph:
940  roothist = hist.TGraph(name=name)
941  elif isinstance(hist, Hist):
942  roothist = hist.TH1F(name=name.replace('/', '__'))
943  else:
944  roothist = hist.TH2F(name=name)
945  roothist.SetLineStyle(options.line_styles[i])
946  roothist.SetLineColor(options.line_colors[i])
947  roothist.SetFillColor(options.fill_colors[i])
948  roothist.SetMarkerColor(options.marker_colors[i])
949  roothist.SetFillStyle(options.fill_styles[i])
950  roothist.SetMarkerStyle(options.marker_styles[i])
951  roothist.SetMarkerSize(options.marker_sizes[i])
952  roothists.append(roothist)
953  if (isinstance(hist, Hist) and not isTGraph and
954  'stack' in options.draw_commands[i]):
955  objects['stack'].Add(roothist)
956  if 'stack' in objects and objects['stack'].GetHists():
957  histmax = objects['stack'].GetMaximum()
958  for roothist in roothists:
959  histmax = max(histmax, roothist.GetMaximum())
960  dimension = 1
961  if isinstance(hist, Hist2D):
962  dimension = 2
963  if options.gridx or options.grid:
964  for pad in objects['pads']:
965  pad.SetGridx(not pad.GetGridx())
966  if options.gridy or options.grid:
967  objects['pads'][0].SetGridy(not objects['pads'][0].GetGridy())
968  objects['legend'] = ROOT.TLegend(*parse_legend_root(options))
969  for com in options.draw_commands:
970  if 'stack' in com:
971  first_draw = prep_first_draw(objects['stack'], histmax, options)
972  com = com.replace('stack', '')
973  objects['stack'].Draw(com)
974  break
975  for i, roothist in enumerate(roothists):
976  if isTGraph:
977  objects['multigraph'].Add(roothist)
978  elif dimension == 1:
979  if 'stack' not in options.draw_commands[i]:
980  if first_draw:
981  first_draw = prep_first_draw(roothist, histmax, options)
982  roothist.Draw(options.draw_commands[i])
983  else:
984  roothist.Draw("same " + options.draw_commands[i])
985  else:
986  roothist.Draw(options.draw2D)
987  legendopt = 'lp'
988  if options.fill_styles[i]: legendopt += 'f'
989  if 'e' in options.draw_commands[i]: legendopt += 'e'
990  objects['legend'].AddEntry(roothist, options.legend_entries[i],
991  legendopt)
992  if isTGraph:
993  objects['multigraph'].Draw(options.draw_graph)
994  prep_first_draw(objects['multigraph'], histmax, options)
995  objects['multigraph'].Draw(options.draw_graph)
996  if options.split and dimension == 1:
997  objects['pads'][1].cd()
998  objects['ratio_multigraph'] = plot_ratio_root(
999  hists, roothist.GetXaxis().GetTitle(), options)
1000  xmin = hists[0].xedges[0]
1001  xmax = hists[0].xedges[-1]
1002  objects['ratio_multigraph'].GetXaxis().SetRangeUser(xmin, xmax)
1003  objects['pads'][0].cd()
1004  if options.logx:
1005  for pad in objects['pads']:
1006  pad.SetLogx(True)
1007  if options.logy:
1008  objects['pads'][0].SetLogy(True)
1009  if options.ratio_logy:
1010  if len(objects['pads']) > 1:
1011  objects['pads'][1].SetLogy(True)
1012  if options.numbering:
1013  display_page_number(options)
1014  if roothist.InheritsFrom('TH1'):
1015  if options.overflow:
1016  display_overflow(objects['stack'], roothist)
1017  if options.underflow:
1018  display_underflow(objects['stack'], roothist)
1019  if options.legend_location and dimension == 1:
1020  objects['legend'].Draw()
1021  exec(options.decoration_root)
1022  objects['hists'] = roothists
1023  return canvas, objects
1024 
1025 def plot_hists_mpl(hists, options):
1026  #### Create a plot.
1027  fig = plt.figure(1, figsize=options.size)
1028  fig.clf() # clear figure
1029  axes = plt.axes()
1030  objects = {'axes' : [axes, axes]}
1031  if options.ratio:
1032  if options.split:
1033  objects['axes'] = divide_axes(fig, axes, options.ratio_fraction)
1034  axes = objects['axes'][0]
1035  fig.sca(axes)
1036  else:
1037  hists = make_ratio_hists(hists, options, options.ratio - 1)
1038  refhist = hists[0]
1039  if refhist is None:
1040  refhist = hists[1]
1041  fullstack, objects['stack'] = HistStack(), HistStack()
1042  histmax, allempty = None, True
1043  for i, hist in enumerate(hists):
1044  if hist and hist.entries:
1045  allempty = False
1046  if isinstance(hist, Hist):
1047  # Avoid errors due to zero bins with log y axis
1048  if options.logy and options.plot_styles[i] != 'errorbar':
1049  for j in range(hist.nbins):
1050  hist.y[j] = max(hist.y[j], 1e-10)
1051  if options.plot_styles[i] in ['barh', 'barcluster', 'stack']:
1052  objects['stack'].add(hist, log=options.logy,
1053  hatch=options.fill_styles[i],
1054  linestyle=options.line_styles[i],
1055  edgecolor=options.line_colors[i],
1056  facecolor=options.fill_colors[i])
1057  fullstack.add(hist)
1058  if 'stack' in options.plot_styles:
1059  histmax = max(histmax, objects['stack'].stackmax())
1060  elif 'barh' in options.plot_styles or 'barcluster' in options.plot_styles:
1061  histmax = max(histmax, objects['stack'].max())
1062  for hist in fullstack:
1063  histmax = max(histmax, max(hist))
1064  if allempty:
1065  fig.text(0.5, 0.5, "No Entries", ha='center', va='center')
1066  elif isinstance(refhist, Hist):
1067  for i, hist in enumerate(hists):
1068  if hist:
1069  if options.plot_styles[i] == "errorbar":
1070  if options.logy:
1071  axes.set_yscale('log')
1072  # Logy would fail if hist all zeroes
1073  if not np.nonzero(hist.y)[0].tolist():
1074  continue
1075  # Errorbars get messed up when they extend down to zero
1076  for j in range(hist.nbins):
1077  yerr = hist.yerr[0][j]
1078  if (hist[j] - yerr) < (0.01 * yerr):
1079  hist.yerr[0][j] *= 0.99
1080  hist.errorbar(fmt=options.marker_styles[i],
1081  yerr=True,
1082  xerr=options.xerr,
1083  markersize=options.marker_sizes[i],
1084  color=options.fill_colors[i],
1085  ecolor=options.errorbar_colors[i],
1086  label_rotation=options.xlabel_rotation,
1087  label_alignment=options.xlabel_alignment)
1088  elif options.plot_styles[i] == "bar":
1089  hist.bar(alpha=options.alphas[i],
1090  log=options.logy,
1091  width=options.barwidth,
1092  hatch=options.fill_styles[i],
1093  edgecolor=options.line_colors[i],
1094  facecolor=options.fill_colors[i],
1095  label_rotation=options.xlabel_rotation,
1096  label_alignment=options.xlabel_alignment)
1097  elif 'hist' in options.plot_styles[i]:
1098  histtype = 'step'
1099  if 'fill' in options.plot_styles[i]:
1100  histtype = 'stepfilled'
1101  hist.hist(alpha=options.alphas[i],
1102  histtype=histtype,
1103  log=options.logy,
1104  hatch=options.fill_styles[i],
1105  edgecolor=options.line_colors[i],
1106  facecolor=options.fill_colors[i],
1107  label_rotation=options.xlabel_rotation,
1108  label_alignment=options.xlabel_alignment)
1109  if options.logx:
1110  for ax in objects['axes']:
1111  ax.set_xscale('log')
1112  if objects['stack'].hists:
1113  if 'stack' in options.plot_styles:
1114  objects['stack'].histstack(
1115  histtype='stepfilled',
1116  label_rotation=options.xlabel_rotation,
1117  label_alignment=options.xlabel_alignment)
1118  elif 'barh' in options.plot_styles:
1119  objects['stack'].barh(
1120  width=options.barwidth,
1121  label_rotation=options.xlabel_rotation,
1122  label_alignment=options.xlabel_alignmenth)
1123  elif 'barcluster' in options.plot_styles:
1124  objects['stack'].barcluster(
1125  width=options.barwidth,
1126  label_rotation=options.xlabel_rotation,
1127  label_alignment=options.xlabel_alignment)
1128  if 'barh' not in options.plot_styles:
1129  axes.set_xlim(refhist.xedges[0], refhist.xedges[-1])
1130  if options.logy:
1131  my_min = fullstack.min(threshold=1.1e-10)
1132  rounded_min = 1e100
1133  while (rounded_min > my_min):
1134  rounded_min /= 10
1135  axes.set_ylim(ymin=rounded_min)
1136  if options.xmin is not None:
1137  axes.set_xlim(xmin=options.xmin)
1138  if options.xmax is not None:
1139  axes.set_xlim(xmax=options.xmax)
1140  if options.ymin is not None:
1141  axes.set_ylim(ymin=options.ymin)
1142  if options.ymax is not None:
1143  axes.set_ylim(ymax=options.ymax)
1144  elif ('barh' not in options.plot_styles and
1145  histmax != 0 and not options.ymax):
1146  axes.set_ylim(ymax=histmax * options.top_padding_factor)
1147  if options.overflow:
1148  axes.text(hist.x[-1], axes.set_ylim()[0], options.overflow_text,
1149  rotation='vertical', ha='center',
1150  alpha=options.overflow_alpha, size=options.overflow_size)
1151  if options.underflow:
1152  axes.text(hist.x[0], axes.set_ylim()[0], options.underflow_text,
1153  rotation='vertical', ha='center',
1154  alpha=options.overflow_alpha, size=options.overflow_size)
1155  if options.gridx or options.grid:
1156  axes.xaxis.grid()
1157  if options.gridy or options.grid:
1158  axes.yaxis.grid()
1159  if (options.legend_location != 'None' or options.legend_axes_bbox or
1160  options.legend_figure_bbox):
1161  try:
1162  options.legend_location = int(options.legend_location)
1163  except ValueError:
1164  pass
1165  if options.legend_axes_bbox:
1166  kwargs = {'bbox_to_anchor' : options.legend_axes_bbox}
1167  elif options.legend_figure_bbox:
1168  kwargs = {'bbox_to_anchor' : options.legend_figure_bbox,
1169  'bbox_transform' : fig.transFigure}
1170  else:
1171  kwargs = {'loc' : options.legend_location}
1172  if options.legend_ncols:
1173  kwargs['ncol'] = int(options.legend_ncols)
1174  objects['legend'] = axes.legend(numpoints=1, **kwargs)
1175  elif isinstance(refhist, Hist2D):
1176  drawfunc = getattr(hist, options.draw2D)
1177  if 'col' in options.draw2D:
1178  if options.cmap:
1179  drawfunc(cmap=options.cmap)
1180  else:
1181  drawfunc()
1182  else:
1183  drawfunc(color=options.fill_colors[0])
1184  axes.set_title(r2m.replace(refhist.title, options.replace))
1185  if 'barh' in options.plot_styles:
1186  set_ylabel = objects['axes'][1].set_xlabel
1187  set_xlabel = axes.set_ylabel
1188  else:
1189  set_ylabel = axes.set_ylabel
1190  set_xlabel = objects['axes'][1].set_xlabel
1191  if options.ratio and not options.split and not options.ylabel:
1192  ratio_file = options.legend_entries[options.ratio - 1]
1193  if options.efficiency:
1194  set_ylabel(options.efficiency_label % locals())
1195  else:
1196  set_ylabel(options.ratio_label % locals())
1197  else:
1198  set_ylabel(r2m.replace(refhist.ylabel, options.replace))
1199  set_xlabel(r2m.replace(refhist.xlabel, options.replace))
1200  if options.split:
1201  fig.sca(objects['axes'][1])
1202  plot_ratio_mpl(objects['axes'][1], hists, options)
1203  fig.sca(objects['axes'][0])
1204  if options.numbering:
1205  fig.text(options.numbering_x_mpl, options.numbering_y_mpl,
1206  options.numbering, size=options.numbering_size_mpl,
1207  ha=options.numbering_ha_mpl, va=options.numbering_va_mpl)
1208  options.decoration_mpl(fig, objects['axes'], options.plotpath,
1209  options, hists)
1210  objects['hists'] = hists
1211  return fig, objects
1212 
1213 def initialize_hists(args, kwargs):
1214  options = fill_options(args, kwargs, scope='plot')
1215  if use_mpl:
1216  load_matplotlib(options.ext)
1217  nfiles = len(options.filenames)
1218  ntargets = len(options.targets)
1219  if nfiles < 1:
1220  raise TypeError("plot() takes at least 1 filename argument (0 given)")
1221  elif ntargets > 1 and nfiles > 1:
1222  raise TypeError("plot() takes exactly 1 target (%i given) when "
1223  "multiple files are specified (%i given)" %
1224  (nfiles, ntargets))
1225  options.nhists = max(nfiles, ntargets)
1226  process_options(options)
1227  files = [RootFile(filename) for filename in options.filenames]
1228  hists = []
1229  stack_integral = 0.
1230  for i, (f, target) in enumerate(cartesian_product(files, options.targets)):
1231  try:
1232  roothist = f.file.Get(target)
1233  except ReferenceError:
1234  hists.append(None)
1235  continue
1236  try:
1237  isTGraph = not roothist.InheritsFrom('TH1')
1238  except TypeError:
1239  raise TypeError("'%s' does not appear to be a valid target" %
1240  target)
1241  dimension = 1
1242  if use_mpl:
1243  stacked = 'stack' in options.plot_styles[i]
1244  else:
1245  stacked = 'stack' in options.draw_commands[i]
1246  if not isTGraph:
1247  dimension = roothist.GetDimension()
1248  roothist.Scale(options.scale[i])
1249  if options.rebin:
1250  roothist.Rebin(options.rebin)
1251  title, xlabel, ylabel = get_labels(roothist, options)
1252  if options.normalize:
1253  norm_file = options.legend_entries[options.normalize - 1]
1254  ylabel = options.target_normalized_title % locals()
1255  if options.area_normalize:
1256  ylabel = options.area_normalized_title
1257  if dimension == 1:
1258  hist = Hist(roothist, label=options.legend_entries[i],
1259  title=title, xlabel=xlabel, ylabel=ylabel)
1260  else:
1261  hist = Hist2D(roothist, label=options.legend_entries[i],
1262  title=title, xlabel=xlabel, ylabel=ylabel)
1263  if stacked:
1264  stack_integral += roothist.Integral()
1265  roothist.Delete()
1266  hists.append(hist)
1267  for i, hist in enumerate(hists):
1268  if use_mpl:
1269  stacked = 'stack' in options.plot_styles[i]
1270  else:
1271  stacked = 'stack' in options.draw_commands[i]
1272  if dimension == 1:
1273  if options.overflow:
1274  hist.y[-1] += hist.overflow
1275  if options.underflow:
1276  hist.y[0] += hist.underflow
1277  if options.area_normalize:
1278  if sum(hist.y):
1279  if stacked:
1280  hist.scale(1./stack_integral)
1281  else:
1282  hist.scale(1./sum(hist.y))
1283  if options.normalize:
1284  numerhist = hists[options.normalize - 1]
1285  if options.norm_range:
1286  lowbin, highbin = parse_range(hist.xedges,
1287  options.norm_range)
1288  numer = numerhist.TH1F().Integral(lowbin, highbin)
1289  denom = hist.TH1F().Integral(lowbin, highbin)
1290  else:
1291  numer = sum(numerhist.y)
1292  denom = sum(hist.y)
1293  if stacked:
1294  denom = stack_integral
1295  if denom:
1296  hist.scale(numer / denom)
1297  return hists, options
1298 
1299 def parse_size(size_option):
1300  #### Return a width and height parsed from size_option.
1301  try:
1302  xpos = size_option.find('x')
1303  return float(size_option[:xpos]), float(size_option[xpos+1:])
1304  except TypeError:
1305  return size_option
1306 
1307 def parse_color(color, tcolor=False):
1308  #### Return an rgb tuple or a ROOT TColor from a ROOT color index or
1309  #### an rgb(a) tuple.
1310  if color is None:
1311  return None
1312  elif color == 'none' or color == 'None':
1313  return 'none'
1314  r, g, b = 0, 0, 0
1315  try:
1316  color = ROOT.gROOT.GetColor(color)
1317  r, g, b = color.GetRed(), color.GetGreen(), color.GetBlue()
1318  except TypeError:
1319  try:
1320  if max(color) > 1.:
1321  color = [x/256. for x in color][0:3]
1322  except TypeError:
1323  pass
1324  try:
1325  color = mpal.colors.colorConverter.to_rgb(color)
1326  except NameError:
1327  pass
1328  r, g, b = color[0:3]
1329  if tcolor:
1330  return ROOT.TColor.GetColor(r, g, b)
1331  return (r, g, b)
1332 
1333 def get_labels(hist, options):
1334  #### Return the appropriate histogram and axis titles for hist.
1335  title = hist.GetTitle().split(';')[0]
1336  xlabel = hist.GetXaxis().GetTitle()
1337  ylabel = hist.GetYaxis().GetTitle()
1338  if options.title:
1339  if options.title.startswith('+'):
1340  title += options.title[1:]
1341  else:
1342  title = options.title
1343  if options.xlabel:
1344  if options.xlabel.startswith('+'):
1345  xlabel += options.xlabel[1:]
1346  else:
1347  xlabel = options.xlabel
1348  if options.ylabel:
1349  if options.ylabel.startswith('+'):
1350  ylabel += options.ylabel[1:]
1351  else:
1352  ylabel = options.ylabel
1353  return title, xlabel, ylabel
1354 
1355 def report_progress(counter, nplots, output, ext, divisor=1):
1356  #### Print the current number of finished plots.
1357  if counter % divisor == 0:
1358  print(("\r%i plots of %i written to %s/ in %s format" %
1359  (counter, nplots, output, ext)), end=' ')
1360  sys.stdout.flush()
1361 
1362 def merge_pdf(options):
1363  #### Merge together all the produced plots into one pdf file.
1364  destination = joined(options.output, 'allplots.pdf')
1365  paths = []
1366  for path, dirs, files in os.walk(options.output):
1367  paths += [joined(path, x) for x in files if x.endswith('.pdf')]
1368  if not paths:
1369  print("No output files, so no merged pdf was made")
1370  return
1371  print("Writing %s..." % destination)
1372  os.system('gs -q -dBATCH -dNOPAUSE -sDEVICE=pdfwrite '
1373  '-dAutoRotatePages=/All '
1374  '-sOutputFile=%s %s' % (destination, ' '.join(paths)))
1375 
1376 def display_page_number(options):
1377  #### Add a page number to the top corner of the canvas.
1378  page_text = ROOT.TText()
1379  page_text.SetTextSize(options.numbering_size_root)
1380  page_text.SetTextAlign(options.numbering_align_root)
1381  page_text.DrawTextNDC(options.numbering_x_root, options.numbering_y_root,
1382  '%i' % options.numbering)
1383 
1384 def display_overflow(stack, hist):
1385  #### Add the overflow to the last bin and print 'Overflow' on the bin.
1386  nbins = hist.GetNbinsX()
1387  x = 0.5 * (hist.GetBinLowEdge(nbins) +
1388  hist.GetBinLowEdge(nbins + 1))
1389  y = stack.GetMinimum('nostack')
1390  display_bin_text(x, y, nbins, 'Overflow')
1391 
1392 def display_underflow(stack, hist):
1393  #### Add the underflow to the first bin and print 'Underflow' on the bin.
1394  nbins = hist.GetNbinsX()
1395  x = 0.5 * (hist.GetBinLowEdge(1) +
1396  hist.GetBinLowEdge(2))
1397  y = stack.GetMinimum('nostack')
1398  display_bin_text(x, y, nbins, 'Underflow')
1399 
1400 def display_bin_text(x, y, nbins, text):
1401  #### Overlay TEXT on this bin.
1402  bin_text = ROOT.TText()
1403  bin_text.SetTextSize(min(1. / nbins, 0.04))
1404  bin_text.SetTextAlign(12)
1405  bin_text.SetTextAngle(90)
1406  bin_text.SetTextColor(13)
1407  bin_text.SetTextFont(42)
1408  bin_text.DrawText(x, y, text)
1409 
1410 def prep_first_draw(hist, histmax, options):
1411  #### Set all the pad attributes that depend on the first object drawn.
1412  hist.SetMaximum(histmax * options.top_padding_factor)
1413  if options.xmin is not None and options.xmax is not None:
1414  hist.GetXaxis().SetRangeUser(options.xmin, options.xmax)
1415  elif options.xmin is not None:
1416  original_max = hist.GetBinLowEdge(hist.GetNbinsX() + 1)
1417  hist.GetXaxis().SetRangeUser(options.xmin, original_max)
1418  elif options.xmax is not None:
1419  original_min = hist.GetBinLowEdge(1)
1420  hist.GetXaxis().SetRangeUser(original_min, options.xmax)
1421  if options.ymin is not None:
1422  hist.SetMinimum(options.ymin)
1423  if options.ymax is not None:
1424  hist.SetMaximum(options.ymax)
1425  if options.ratio:
1426  if options.split:
1427  hist.Draw()
1428  hist.GetXaxis().SetBinLabel(1, '') # Don't show tick labels
1429  if ';' in hist.GetTitle():
1430  # dealing with bug in THStack title handling
1431  titles = hist.GetTitle().split(';')
1432  if len(titles) > 1: titles[1] = ''
1433  hist.SetTitle(';'.join(titles))
1434  else:
1435  hist.GetXaxis().SetTitle('')
1436  ## Avoid overlap of y-axis numbers by supressing zero
1437  if (not options.logy and
1438  hist.GetMaximum() > 0 and
1439  hist.GetMinimum() / hist.GetMaximum() < 0.25):
1440  hist.SetMinimum(hist.GetMaximum() / 10000)
1441  else:
1442  ratio_file = options.legend_entries[options.ratio - 1]
1443  if options.efficiency:
1444  hist.GetYaxis().SetTitle(options.efficiency_label % locals())
1445  else:
1446  hist.GetYaxis().SetTitle(options.ratio_label % locals())
1447  return False
1448 
1449 def divide_canvas(canvas, ratio_fraction):
1450  #### Divide the canvas into two pads; the bottom is ratio_fraction tall.
1451  ## Both pads are set to the full canvas size to maintain font sizes
1452  ## Fill style 4000 used to ensure pad transparency because of this
1453  margins = [ROOT.gStyle.GetPadTopMargin(), ROOT.gStyle.GetPadBottomMargin()]
1454  useable_height = 1 - (margins[0] + margins[1])
1455  canvas.Clear()
1456  pad = ROOT.TPad('mainPad', 'mainPad', 0., 0., 1., 1.)
1457  pad.SetFillStyle(4000)
1458  pad.Draw()
1459  pad.SetBottomMargin(margins[1] + ratio_fraction * useable_height)
1460  pad_ratio = ROOT.TPad('ratioPad', 'ratioPad', 0., 0., 1., 1.);
1461  pad_ratio.SetFillStyle(4000)
1462  pad_ratio.Draw()
1463  pad_ratio.SetTopMargin(margins[0] + (1 - ratio_fraction) * useable_height)
1464  return pad, pad_ratio
1465 
1466 def divide_axes(fig, axes, ratio_fraction):
1467  #### Create two subaxes, the lower one taking up ratio_fraction of total.
1468  x1, y1, x2, y2 = axes.get_position().get_points().flatten().tolist()
1469  width = x2 - x1
1470  height = y2 - y1
1471  lower_height = height * ratio_fraction
1472  upper_height = height - lower_height
1473  lower_axes = fig.add_axes([x1, y1, width, lower_height], axisbg='None')
1474  upper_axes = fig.add_axes([x1, y1 + lower_height, width, upper_height],
1475  axisbg='None', sharex=lower_axes)
1476  ## Make original axes and the upper ticklabels invisible
1477  axes.set_xticks([])
1478  axes.set_yticks([])
1479  plt.setp(upper_axes.get_xticklabels(), visible=False)
1480  return upper_axes, lower_axes
1481 
1482 def make_ratio_hists(hists, options, ratio_index):
1483  denom = hists[ratio_index]
1484  if options.efficiency:
1485  ratios = [hist.divide_wilson(denom) for hist in hists]
1486  else:
1487  ratios = [hist.divide(denom) for hist in hists]
1488  ratios[ratio_index] = None
1489  return ratios
1490 
1491 def plot_ratio_root(hists, xlabel, options):
1492  #### Plot the ratio of each hist in hists to the ratio_indexth hist.
1493  ratio_index = options.ratio - 1
1494  ratio_file = options.legend_entries[ratio_index]
1495  if options.efficiency:
1496  ylabel = options.efficiency_label % locals()
1497  else:
1498  ylabel = options.ratio_label % locals()
1499  multigraph = ROOT.TMultiGraph("ratio_multi",
1500  ";%s;%s" % (xlabel, ylabel))
1501  if options.stack and options.data:
1502  numerator = hists[ratio_index]
1503  hists = hists[:]
1504  hists.pop(ratio_index)
1505  denominator = hists[0]
1506  for hist in hists[1:]:
1507  denominator += hist
1508  hists = [numerator, denominator]
1509  ratio_index = 1
1510  for i, ratio_hist in enumerate(make_ratio_hists(hists, options,
1511  ratio_index)):
1512  if i == ratio_index:
1513  continue
1514  graph = ratio_hist.TGraph()
1515  graph.SetLineColor(options.line_colors[i])
1516  graph.SetMarkerColor(options.marker_colors[i])
1517  graph.SetMarkerStyle(options.marker_styles[i])
1518  graph.SetMarkerSize(options.marker_sizes[i])
1519  multigraph.Add(graph)
1520  multigraph.Draw(options.draw_graph)
1521  multigraph.GetYaxis().SetNdivisions(507) # Avoids crowded labels
1522  if options.ratio_max is not None: multigraph.SetMaximum(options.ratio_max)
1523  if options.ratio_min is not None: multigraph.SetMinimum(options.ratio_min)
1524  multigraph.Draw(options.draw_graph)
1525  return multigraph
1526 
1527 def plot_ratio_mpl(axes, hists, options):
1528  #### Plot the ratio of each hist in hists to the ratio_indexth hist.
1529  ratio_index = options.ratio - 1
1530  stack = HistStack()
1531  if options.stack and options.data:
1532  numerator = hists[ratio_index]
1533  hists = hists[:]
1534  hists.pop(ratio_index)
1535  denominator = hists[0]
1536  for hist in hists[1:]:
1537  denominator += hist
1538  hists = [numerator, denominator]
1539  ratio_index = 1
1540  for i, ratio_hist in enumerate(make_ratio_hists(hists, options,
1541  ratio_index)):
1542  if i == ratio_index:
1543  continue
1544  ratio_hist.y = [item or 0 for item in ratio_hist.y] ## Avoid gaps
1545  stack.add(ratio_hist, fmt=options.marker_styles[i],
1546  color=options.fill_colors[i],
1547  ecolor=options.errorbar_colors[i])
1548  if options.ratio_logy:
1549  axes.set_yscale('log')
1550  stack.errorbar(yerr=True)
1551  axes.yaxis.set_major_locator(
1552  mpl.ticker.MaxNLocator(nbins=5, steps=[1, 2, 5, 10]))
1553  if options.ratio_max is not None: axes.set_ylim(ymax=options.ratio_max)
1554  if options.ratio_min is not None: axes.set_ylim(ymin=options.ratio_min)
1555  ratio_file = options.legend_entries[ratio_index]
1556  if options.efficiency:
1557  axes.set_ylabel(options.efficiency_label % locals())
1558  else:
1559  axes.set_ylabel(options.ratio_label % locals())
1560  axes.yaxis.tick_right()
1561  axes.yaxis.set_label_position('right')
1562  axes.yaxis.label.set_rotation(-90)
1563 
1564 def make_html_index(path, dirs, files, filetype, template, ncolumns,
1565  width, height):
1566  files = [x for x in files if x.endswith(filetype)]
1567  output_file = open(joined(path, 'index.html'), 'w')
1568  previous_dirs = [x for x in path.split('/') if x]
1569  ndirs = len(previous_dirs)
1570  back_nav = ['<a href="%s">%s</a>' %
1571  ('../' * (ndirs - i - 1) + 'index.html', previous_dirs[i])
1572  for i in range(ndirs)]
1573  back_nav = '/'.join(back_nav) + '/'
1574  forward_nav = ['<li><a href="%s/index.html">%s</a>/</li>' % (x, x)
1575  for x in dirs]
1576  forward_nav = '\n '.join(forward_nav)
1577  imgtemplate = '<a name="%(plot)s"><a href="index.html#%(plot)s">'
1578  if filetype.lower() == 'svg':
1579  imgtemplate += ('<object type="image/svg+xml" data="%(plot)s" '
1580  'width=%(width)i height=%(height)i></object>')
1581  else:
1582  imgtemplate += '<img src="%(plot)s" height=%(height)i width=%(width)i>'
1583  imgtemplate += '</a></a>'
1584  plots = '\n'
1585  for plot in files:
1586  plots += imgtemplate % locals() + '\n'
1587  plots = re.sub('((\\n<a.*){%i})' % ncolumns, r'\1<br>', plots)
1588  output_file.write(template % locals())
1589  output_file.close()
1590 
1591 def parse_range(xedges, expression):
1592  #### Returns the indices of the low and high bins indicated in expression.
1593  closest = lambda l,y: l.index(min(l, key=lambda x:abs(x-y)))
1594  match = re.match(r'([^x]*)x([^x]*)', expression)
1595  lower, upper = float(match.group(1)), float(match.group(2))
1596  lowbin = closest(xedges, lower) + 1
1597  highbin = closest(xedges, upper)
1598  return lowbin, highbin
1599 
1600 def parse_legend_root(options):
1601  #### Return the corners to use for the legend based on options.
1602  legend_height = min(options.legend_entry_height * options.nhists + 0.02,
1603  options.max_legend_height)
1604  if isinstance(options.legend_location, int):
1605  options.legend_location = options.legend_codes[options.legend_location]
1606  elif options.legend_location.lower() == 'none':
1607  options.legend_location = None
1608  if options.legend_location:
1609  if 'upper' in options.legend_location:
1610  top = options.legend_upper_bound
1611  bottom = options.legend_upper_bound - legend_height
1612  elif 'lower' in options.legend_location:
1613  bottom = options.legend_lower_bound
1614  top = options.legend_lower_bound + legend_height
1615  else:
1616  top = 0.5 + legend_height / 2
1617  bottom = 0.5 - legend_height / 2
1618  if 'left' in options.legend_location:
1619  left = options.legend_left_bound
1620  right = options.legend_left_bound + options.legend_width
1621  elif 'right' in options.legend_location:
1622  right = options.legend_right_bound
1623  left = options.legend_right_bound - options.legend_width
1624  else:
1625  right = 0.5 + options.legend_width / 2
1626  left = 0.5 - options.legend_width / 2
1627  return [left, bottom, right, top]
1628  return [0, 0, 0, 0]
1629 
1631  if 'mpl' not in globals().keys():
1632  global r2m, mpl, np, plt
1633  try:
1634  import matplotlib as mpl
1635  except ImportError:
1636  print("Unable to access matplotlib")
1637  sys.exit(1)
1638  import numpy as np
1639  mpldict = {'png' : 'AGG',
1640  'pdf' : 'PDF',
1641  'ps' : 'PS',
1642  'svg' : 'SVG'}
1643  if ext not in mpldict:
1644  raise ValueError("%s is not an output type recognized by "
1645  "matplotlib" % ext)
1646  mpl.use(mpldict[ext])
1647  global Hist, Hist2D, HistStack
1648  import rootplot.root2matplotlib as r2m
1649  from rootplot.root2matplotlib import Hist, Hist2D, HistStack
1650  import matplotlib.pyplot as plt
1651 
1652 def samebase(targets):
1653  for target in targets:
1654  if os.path.basename(target) != os.path.basename(targets[0]):
1655  return False
1656  return True
1657 
1658 def allsame(targets):
1659  for target in targets:
1660  if target != targets[0]:
1661  return False
1662  return True
1663 
1664 def diffpart(targets):
1665  targets = [target.split('/') for target in targets]
1666  for i in range(len(targets[0])):
1667  for target in targets:
1668  if target[i] != targets[0][i]:
1669  return ['/'.join(target[i:]) for target in targets]
1670 
1671 def get_plotpath(filenames, targets):
1672  if len(targets) >= 2:
1673  diffs = diffpart(targets)
1674  if not allsame([d.split('/')[-1] for d in diffs]):
1675  plotpath = 'plot'
1676  title = 'plot'
1677  legentries = diffs
1678  else:
1679  plotpath = '/'.join(diffs[0].split('/')[1:])
1680  title = diffs[0].split('/')[-1]
1681  legentries = [d.split('/')[0] for d in diffs]
1682  else:
1683  plotpath = targets[0]
1684  title = ''
1685  legentries = [f[:-5] for f in filenames]
1686  return plotpath, title, legentries
1687 
1688 def process_options(options):
1689  #### Refine options for this specific plot, based on plotname
1690  def comma_separator(obj, objtype, nhists):
1691  #### Split a comma-separated string into a list.
1692  if isinstance(obj, list):
1693  return obj
1694  if isinstance(obj, str) and ',' in obj:
1695  try:
1696  return [objtype(x) for x in obj.split(',')]
1697  except TypeError:
1698  return [eval(objtype)(x) for x in obj.split(',')]
1699  try:
1700  return [objtype(obj) for i in range(nhists)]
1701  except TypeError:
1702  return [eval(objtype)(obj) for i in range(nhists)]
1703  nhists = options.nhists
1704  if options.targets:
1705  plotpath, title, legentries = get_plotpath(options.filenames,
1706  options.targets)
1707  options.plotpath = plotpath
1708  if not options.title:
1709  options.title = title
1710  if not options.legend_entries:
1711  options.legend_entries = legentries
1712  options.legend_entries = comma_separator(options.legend_entries,
1713  str, nhists)
1714  options.scale = comma_separator(options.scale, float, nhists)
1715  if options.efficiency_split: options.ratio_max = 1.
1716  if nhists > 1: options.draw2D = None
1717  if use_mpl:
1718  plot_style = 'histfill'
1719  if options.bar: plot_style = 'bar'
1720  elif options.barh: plot_style = 'barh'
1721  elif options.barcluster: plot_style = 'barcluster'
1722  elif options.errorbar: plot_style = 'errorbar'
1723  elif options.hist: plot_style = 'hist'
1724  elif options.histfill: plot_style = 'histfill'
1725  if options.stack: plot_style = 'stack'
1726  if not options.markers and use_mpl:
1727  options.marker_styles = ['o' for i in range(nhists)]
1728  if not options.line_colors:
1729  options.line_colors = options.colors
1730  if not options.fill_colors:
1731  options.fill_colors = options.colors
1732  if not options.marker_colors:
1733  options.marker_colors = options.colors
1734  if use_mpl:
1735  if not options.line_styles:
1736  options.line_styles = ['solid' for i in range(nhists)]
1737  if not options.plot_styles:
1738  options.plot_styles = [plot_style for i in range(nhists)]
1739  if not options.errorbar_colors:
1740  options.errorbar_colors = [None for i in range(nhists)]
1741  if not options.alphas:
1742  options.alphas = [options.alpha for i in range(nhists)]
1743  else:
1744  if not options.line_styles:
1745  options.line_styles = [1 for i in range(nhists)]
1746  if not options.draw_commands:
1747  if options.stack:
1748  options.draw_commands = ['stack ' + options.draw
1749  for i in range(nhists)]
1750  else:
1751  options.draw_commands = [options.draw
1752  for i in range(nhists)]
1753  if not options.fill_styles:
1754  if use_mpl:
1755  options.fill_styles = [None for i in range(nhists)]
1756  else:
1757  if options.fill:
1758  options.fill_styles = [1001 for i in range(nhists)]
1759  else:
1760  options.fill_styles = [0 for i in range(nhists)]
1761  if not options.marker_sizes:
1762  if options.markers:
1763  if use_mpl: size = mpl.rcParams['lines.markersize']
1764  else: size = ROOT.gStyle.GetMarkerSize()
1765  else:
1766  size = 0
1767  options.marker_sizes = [size for i in range(nhists)]
1768  if options.data:
1769  i = options.data - 1
1770  options.line_styles[i] = options.data_linestyle
1771  options.line_colors[i] = options.data_color
1772  options.fill_colors[i] = options.data_color
1773  options.marker_colors[i] = options.data_color
1774  if use_mpl:
1775  options.plot_styles[i] = 'errorbar'
1776  else:
1777  options.fill_styles[i] = 0
1778  options.draw_commands[i] = 'e'
1779  options.marker_styles[i] = options.data_marker
1780  if not options.marker_sizes[i]:
1781  if use_mpl:
1782  options.marker_sizes[i] = mpl.rcParams['lines.markersize']
1783  else:
1784  options.marker_sizes[i] = ROOT.gStyle.GetMarkerSize()
1785  if nhists == 2 and options.mc_color:
1786  options.fill_colors[(i+1)%2] = options.mc_color
1787  for opt in [x for x in options.keys() if 'colors' in x]:
1788  try:
1789  colors = options[opt]
1790  options[opt] = [parse_color(x, not use_mpl) for x in colors]
1791  except AttributeError:
1792  pass
1793  if options.targets:
1794  #### Apply extra options based on hist name
1795  plotname = os.path.basename(options.plotpath)
1796  for option, value, regexes in options.options_by_histname:
1797  for regex in regexes:
1798  if re.match(regex, plotname):
1799  setattr(options, option, value)
1800  #### Final setup
1801  if options.logy:
1802  if options.ymin <= 0:
1803  options.ymin = None
1804  options.top_padding_factor = options.top_padding_factor_log
1805 
1806 def cartesian_product(*args, **kwds):
1807  # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
1808  # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
1809  pools = map(tuple, args) * kwds.get('repeat', 1)
1810  result = [[]]
1811  for pool in pools:
1812  result = [x+[y] for x in result for y in pool]
1813  for prod in result:
1814  yield tuple(prod)
1815 
1816 def parse_arguments(argv, scope='global'):
1817  if use_mpl:
1818  import matplotlib as mpl
1819  figsize = 'x'.join([str(x) for x in mpl.rcParams['figure.figsize']])
1820  else:
1821  figsize = 'x'.join([str(ROOT.gStyle.GetCanvasDefW()),
1822  str(ROOT.gStyle.GetCanvasDefH())])
1823  def opt(**kwargs):
1824  return kwargs
1825  def addopt(group, *args, **kwargs):
1826  if use_mpl and 'mpl' in kwargs:
1827  opts = kwargs['mpl']
1828  kwargs = dict(kwargs, **opts)
1829  if not use_mpl and 'root' in kwargs:
1830  opts = kwargs['root']
1831  kwargs = dict(kwargs, **opts)
1832  if 'opts' in locals():
1833  del kwargs['mpl']
1834  del kwargs['root']
1835  if 'metadefault' in kwargs:
1836  val = kwargs.pop('metadefault')
1837  kwargs['default'] = val
1838  kwargs['metavar'] = val
1839  if 'metavar' in kwargs and ' ' in str(kwargs['metavar']):
1840  kwargs['metavar']="'%s'" % kwargs['metavar']
1841  group.add_option(*args, **kwargs)
1842 
1843  help_formatter = optparse.IndentedHelpFormatter()
1844  parser = optparse.OptionParser(usage=usage, formatter=help_formatter,
1845  version='%s %s' % ('%prog', __version__))
1846  Group = optparse.OptionGroup
1847  g_control = Group(parser, "Control the overall behavior of rootplot")
1848  g_output = Group(parser, "Control the output format")
1849  g_hist = Group(parser, "Manipulate your histograms")
1850  g_style = Group(parser, "Fine-tune your style")
1851  parser.add_option_group(g_control)
1852  parser.add_option_group(g_output)
1853  parser.add_option_group(g_hist)
1854  parser.add_option_group(g_style)
1855  #### Process control options
1856  addopt(g_control, '--config', action='store_true',
1857  help="do nothing but write a template configuration file "
1858  "called rootplot_config.py")
1859  addopt(g_control, '--debug', action='store_true',
1860  help="turn off error-catching to more easily identify errors")
1861  addopt(g_control, '--path', metavar="'.*'", default='.*',
1862  help="only process plot(s) matching this regular expression")
1863  addopt(g_control, '--processors', type='int',
1864  metadefault=find_num_processors(),
1865  help="the number of parallel plotting processes to create")
1866  #### Output options
1867  addopt(g_output, '-e', '--ext', metadefault='png',
1868  help="choose an output extension")
1869  addopt(g_output, '--merge', action='store_true', default=False,
1870  help="sets --ext=pdf and creates a merged file "
1871  "containing all plots")
1872  addopt(g_output, '--noclean', action='store_true', default=False,
1873  help="skips destroying the output directory before drawing")
1874  addopt(g_output, '--output', metadefault='plots',
1875  help="name of output directory")
1876  addopt(g_output, '--numbering', action='store_true', default=False,
1877  help="print page numbers on images and prepend them to file names; "
1878  "numbering will respect the order of objects in the ROOT file")
1879  addopt(g_output, '--size', metadefault=figsize,
1880  root=opt(help="set the canvas size to 'width x height' in pixels"),
1881  mpl=opt(help="set the canvas size to 'width x height' in inches"))
1882  if use_mpl:
1883  addopt(g_output, '--dpi', type=float,
1884  metadefault=mpl.rcParams['figure.dpi'],
1885  help="set the resolution of the output files")
1886  addopt(g_output, '--transparent', action="store_true", default=False,
1887  help="use a transparent background")
1888  #### Histogram manipulation options
1889  addopt(g_hist, '-n', '--area-normalize', action='store_true',
1890  default=False, help="area-normalize the histograms")
1891  addopt(g_hist, '--scale', metavar='VAL', default=1.,
1892  help="normalize all histograms by VAL, or by individual values "
1893  "if VAL is a comma-separated list")
1894  addopt(g_hist, '--normalize', metavar='NUM', type='int', default=0,
1895  help="normalize to the NUMth target (starting with 1)")
1896  addopt(g_hist, '--norm-range', metavar='LOWxHIGH',
1897  help="only use the specified data range in determining "
1898  "the normalization")
1899  addopt(g_hist, '--rebin', metavar="N", type=int,
1900  help="group together bins in sets of N")
1901  addopt(g_hist, '--ratio', type='int', default=0, metavar='NUM',
1902  help="divide histograms by the NUMth target (starting from 1)")
1903  addopt(g_hist, '--ratio-split', type='int', default=0, metavar='NUM',
1904  help="same as --ratio, but split the figure in two, displaying "
1905  "the normal figure on top and the ratio on the bottom")
1906  addopt(g_hist, '--efficiency', type='int', default=0, metavar='NUM',
1907  help="same as --ratio, but with errors computed by the Wilson "
1908  "score interval")
1909  addopt(g_hist, '--efficiency-split', type='int', default=0, metavar='NUM',
1910  help="same as --ratio-split, but with errors computed by the Wilson "
1911  "score interval")
1912  #### Style options
1913  if not use_mpl:
1914  addopt(g_style, '--draw', metadefault='p H',
1915  help="argument to pass to ROOT's Draw command; try 'e' for "
1916  "errorbars, or 'hist' to make sure no errorbars appear")
1917  addopt(g_style, '--draw2D',
1918  root=opt(metadefault='box',
1919  help="argument to pass to TH2::Draw (ignored if multiple "
1920  "targets specified); set "
1921  'to "" to turn off 2D drawing'),
1922  mpl=opt(metadefault='box',
1923  help="command to use for plotting 2D hists; (ignored if "
1924  "multiple targets specified) "
1925  "choose from 'contour', 'col', 'colz', and 'box'")
1926  )
1927  if not use_mpl:
1928  addopt(g_style, '-f', '--fill', action='store_true', default=False,
1929  help="Histograms will have a color fill")
1930  if use_mpl:
1931  addopt(g_style, '--errorbar', action="store_true", default=False,
1932  help="output a matplotlib errorbar graph")
1933  addopt(g_style, '--barcluster', action="store_true", default=False,
1934  help="output a clustered bar graph")
1935  addopt(g_style, '--barh', action="store_true", default=False,
1936  help="output a horizontal clustered bar graph")
1937  addopt(g_style, '--bar', action="store_true", default=False,
1938  help="output a bar graph with all histograms overlaid")
1939  addopt(g_style, '--hist', action="store_true", default=False,
1940  help="output a matplotlib hist graph (no fill)")
1941  addopt(g_style, '--histfill', action="store_true", default=False,
1942  help="output a matplotlib hist graph with solid fill")
1943  addopt(g_style, '--stack', action="store_true", default=False,
1944  help="stack histograms")
1945  addopt(g_style, '-m', '--markers', action='store_true', default=False,
1946  help="add markers to histograms")
1947  addopt(g_style, '--xerr', action="store_true", default=False,
1948  help="show width of bins")
1949  addopt(g_style, '--data', type='int', default=0, metavar='NUM',
1950  root=opt(help="treat the NUMth target (starting from 1) "
1951  "specially, drawing it as black datapoints; to achieve "
1952  "a classic data vs. MC plot, try this along with "
1953  "--stack and --fill"),
1954  mpl=opt(help="treat the NUMth target (starting from 1) "
1955  "specially, drawing it as black datapoints; to achieve "
1956  "a classic data vs. MC plot, try this along with --stack"))
1957  addopt(g_style, '--xmax', type='float', default=None,
1958  help="set the maximum value of the x-axis")
1959  addopt(g_style, '--xmin', type='float', default=None,
1960  help="set the minimum value of the x-axis")
1961  addopt(g_style, '--ymax', type='float', default=None,
1962  help="set the maximum value of the y-axis")
1963  addopt(g_style, '--ymin', type='float', default=None,
1964  help="set the minimum value of the y-axis")
1965  addopt(g_style, '--legend-location', metavar='upper right', default=1,
1966  help="Place legend in LOC, according to matplotlib "
1967  "location codes; try 'lower left' or 'center'; "
1968  "to turn off, set to 'None'")
1969  addopt(g_style, '--legend-entries', default=None, metavar="''",
1970  help="A comma-separated string giving the labels to "
1971  "appear in the legend")
1972  if use_mpl:
1973  addopt(g_style, '--legend-ncols', default=None, metavar=1,
1974  help="Number of columns to use in the legend")
1975  addopt(g_style, '--title', default=None,
1976  help="replace the plot titles, or append to them by "
1977  "preceeding with a '+'")
1978  addopt(g_style, '--xlabel', default=None,
1979  help="replace the x-axis labels, or append to them by "
1980  "preceeding with a '+'")
1981  addopt(g_style, '--ylabel', default=None,
1982  help="replace the y-axis labels, or append to them by "
1983  "preceeding with a '+'")
1984  addopt(g_style, '--grid', action='store_true', default=False,
1985  help="toggle the grid on or off for both axes")
1986  addopt(g_style, '--gridx', action='store_true', default=False,
1987  help="toggle the grid on or off for the x axis")
1988  addopt(g_style, '--gridy', action='store_true', default=False,
1989  help="toggle the grid on or off for the y axis")
1990  if use_mpl:
1991  addopt(g_style, '--cmap',
1992  help="matplotlib colormap to use for 2D plots")
1993  addopt(g_style, '--barwidth', metadefault=1.0, type=float,
1994  help="fraction of the bin width for bars to fill")
1995  addopt(g_style, '--alpha', type='float', metadefault=0.5,
1996  help="set the opacity of fills")
1997  addopt(g_style, '--logx', action='store_true', default=False,
1998  help="force log scale for x axis")
1999  addopt(g_style, '--logy', action='store_true', default=False,
2000  help="force log scale for y axis")
2001  addopt(g_style, '--overflow', action='store_true', default=False,
2002  help="display overflow content in the highest bin")
2003  addopt(g_style, '--underflow', action='store_true', default=False,
2004  help="display underflow content in the lowest bin")
2005  #### Do immediate processing of arguments
2006  options, arguments = parser.parse_args(list(argv))
2007  options = Options(options, arguments, scope=scope)
2008  options.replace = [] # must have default in case not using mpl
2009  if options.processors == 1 or options.ext == 'C':
2010  global use_multiprocessing
2011  use_multiprocessing = False
2012  if options.merge: options.ext = 'pdf'
2013  return options
def add_from_config_files(options, configs)
Definition: core.py:821
def __getattr__(self, key)
Definition: core.py:103
def loadROOT(batch=True)
Define additional helping functions.
Definition: utilities.py:435
def parse_color(color, tcolor=False)
Definition: core.py:1307
def divide_canvas(canvas, ratio_fraction)
Definition: core.py:1449
def display_underflow(stack, hist)
Definition: core.py:1392
def walk_rootfile(path, rootfile, options)
Definition: core.py:849
def merge_pdf(options)
Definition: core.py:1362
def process_options(options)
Definition: core.py:1688
def write_config()
Definition: core.py:810
def __init__(self, options, arguments, scope='global')
Definition: core.py:90
def display_overflow(stack, hist)
Definition: core.py:1384
def process_configs(self, scope)
Definition: core.py:126
def make_ratio_hists(hists, options, ratio_index)
Definition: core.py:1482
def rootplot(args, kwargs)
Definition: core.py:644
def display_bin_text(x, y, nbins, text)
Definition: core.py:1400
def get_plotpath(filenames, targets)
Definition: core.py:1671
def parse_legend_root(options)
Definition: core.py:1600
def plotmpl(args, kwargs)
The Application Programming Interface ###############################.
Definition: core.py:561
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
def parse_size(size_option)
Definition: core.py:1299
def cli_rootplot()
Definition: core.py:540
def make_html_index(path, dirs, files, filetype, template, ncolumns, width, height)
Definition: core.py:1565
def __setattr__(self, key, value)
Definition: core.py:101
def display_page_number(options)
Definition: core.py:1376
def cartesian_product(args, kwds)
Definition: core.py:1806
def plot_ratio_mpl(axes, hists, options)
Definition: core.py:1527
def samebase(targets)
Definition: core.py:1652
def write_to_file(script, destination)
Implementation ######################################################.
Definition: core.py:778
def initialize_hists(args, kwargs)
Definition: core.py:1213
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
def plot_hists_root(hists, options)
Definition: core.py:913
def plot_hists_mpl(hists, options)
Definition: core.py:1025
T min(T a, T b)
Definition: MathUtil.h:58
def parse_range(xedges, expression)
Definition: core.py:1591
def get_plot_inputs(files, options)
Definition: core.py:874
void add(std::map< std::string, TH1 * > &h, TH1 *hist)
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def prep_first_draw(hist, histmax, options)
Definition: core.py:1410
def rootplotmpl(args, kwargs)
Definition: core.py:613
def config_string()
Definition: core.py:798
def report_progress(counter, nplots, output, ext, divisor=1)
Definition: core.py:1355
Classes #############################################################.
Definition: core.py:89
def diffpart(targets)
Definition: core.py:1664
def plot_ratio_root(hists, xlabel, options)
Definition: core.py:1491
def plot(args, kwargs)
Definition: core.py:586
def option_diff(default, modified)
Definition: core.py:787
def make_calls(api_call, save_call)
Definition: core.py:783
def allsame(targets)
Definition: core.py:1658
dbl *** dir
Definition: mlp_gen.cc:35
def get_labels(hist, options)
Definition: core.py:1333
def divide_axes(fig, axes, ratio_fraction)
Definition: core.py:1466
#define str(s)
def cli_rootplotmpl()
remove leading blank lines from the templates
Definition: core.py:530
double split
Definition: MVATrainer.cc:139
def fill_options(args, kwargs, scope)
Definition: core.py:899
def append_from_package(self, package, scope)
Definition: core.py:119
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run
def load_matplotlib(ext)
Definition: core.py:1630
def parse_arguments(argv, scope='global')
Definition: core.py:1816