Crombie Tools
PlotStack.py
Go to the documentation of this file.
1 import os
2 import sqlite3
3 from .. import Load, DirFromEnv, Nminus1Cut
4 
5 newStackPlotter = Load('PlotStack')
6 plotter = newStackPlotter()
7 
8 
9 def SetupFromEnv(aPlotter = plotter):
10  """A function that sets up a plotter after sourcing a config file.
11 
12  @param aPlotter is the plotter to setup. Defaults to plotter in this module.
13  """
14  from ..CommonTools.FileConfigReader import SetupConfigFromEnv, SetFunctionFromEnv
15 
16  SetupConfigFromEnv(aPlotter)
17 
18  DirFromEnv('CrombieOutPlotDir')
20  (aPlotter.SetOutDirectory, 'CrombieOutPlotDir'),
21  (aPlotter.SetNumThreads, 'CrombieNLocalProcs')
22  ])
23 
24 
25 def SetCuts(category, region, aPlotter = plotter):
26  """ Sets cuts based on category and region.
27 
28  @param category is the category of the analysis being used.
29  @param region is the region of the plot being set.
30  @param aPlotter is the plotter that is having its cuts set.
31  Default is the plotter defined in this module.
32  """
33  from ..LoadConfig import cuts
34  aPlotter.SetDefaultWeight(cuts.cut(category, region))
35  aPlotter.SetMCWeight(cuts.dataMCCuts(region, False))
36  aPlotter.SetDataWeight(cuts.dataMCCuts(region, True))
37 
38 
39 def ReadExceptionConfig(region, aPlotter = plotter):
40  """ Reads an [exception configuation](@ref formatmc) file from the [environment](@ref envconfig).
41 
42  @param region is the region that needs a different configuration that's about to be plotted.
43  @param aPlotter is the plotter that will be adjusted. The default is the plotter defined in this module.
44  """
45  from .. import LoadConfig
46  if os.environ.get('CrombieExcept_' + region) == None:
47  print 'Region ' + region + ' does not have an except config set!'
48  else:
49  aPlotter.ReadMCConfig(os.environ['CrombieExcept_' + region])
50 
51 
53  """A class that holds a PlotStack and copies it.
54 
55  Makes the N minus 1 cuts on the object before calling PlotStack::MakeCanvas()
56  """
57 
58  def __init__(self, plotter, overwrite, showCutLines, limitHistsDir, systematics=None):
59  self.Plotter = plotter
60  self.Overwrite = overwrite
61  self.ShowCutLines = showCutLines
62  self.LimitHistsDir = limitHistsDir
63  self.Systematics = systematics or {}
64 
65  def Copy(self):
66  return ParallelStackContainer(plotter.Copy(), self.Overwrite, self.ShowCutLines, self.LimitHistsDir)
67 
68  def MakePlot(self, category, region, exprArg):
69  """Adjusts cut to N minus 1 and plots.
70 
71  @param category is a string
72  @param region is a string
73  @param exprArg is a list of arguments for the plotter used in PlotStack.MakePlots()
74  """
75 
76  if os.environ.get('FIT'):
77  self.Plotter.SetPostFitFile(os.environ['FIT'], region)
78  else:
79  self.Plotter.SetPostFitFile('', '')
80 
81  SetCuts(category, region, self.Plotter)
82  holdCut = self.Plotter.GetDefaultWeight()
83 
84  expr = list(exprArg)
85  kwargs = {}
86 
87  if type(expr[-1]) == dict:
88  kwargs = expr.pop()
89 
90  # Different expressions for data and MC
91  self.Plotter.SetDataExpression(kwargs.get('data_expr', ''))
92 
93  # If not skipping Nminus1 cuts
94  if kwargs.get('doNminus1', True):
95  # List cut lines to draw in a plot
96  for cut_line in kwargs.get('cut_lines', Nminus1Cut(holdCut, expr[0], True) if self.ShowCutLines else []):
97  self.Plotter.AddCutLine(cut_line)
98 
99  self.Plotter.SetDefaultWeight(Nminus1Cut(holdCut, expr[0]))
100 
101  d_expr = expr[0]
102  if '__' in region:
103  syst = region.split('__')[1]
104  syst = syst[:-2] if syst.endswith('Up') else syst[:-4]
105  if d_expr in self.Systematics.get(syst, []):
106  d_expr = '%s_%s' % (d_expr, region.split('__')[1])
107 
108  self.Plotter.SetDefaultExpr(d_expr)
109 
110  if self.LimitHistsDir:
111  self.Plotter.SetDumpFileName(os.path.join(self.LimitHistsDir, '%s_%s_%s.root' % (category, region, expr[0])))
112  self.Plotter.SetHistSuff('__%s__%s' % (category, region))
113 
114  expr[0] = '_'.join([category, region, kwargs.get('var_name', expr[0]).replace(',', '__').replace(')', '__').replace('(', '__')])
115 
116  if (self.Overwrite or
117  not os.path.exists(str(self.Plotter.GetOutDirectory()) + expr[0] + '.pdf')):
118  self.Plotter.MakeCanvas(*expr)
119 
120  self.Plotter.SetDefaultWeight(holdCut)
121  # Reset and exceptional values for this plot
122  self.Plotter.SetDataExpression('')
123  self.Plotter.ResetCutLines()
124 
125 
126 def MakePlots(categories, regions, exprArgs, overwrite=True, parallel=True, showCutLines=True, limitHistsDir='', aPlotter=plotter, systematics=None):
127  """ Shortcut to make plots for multiple categories and regions with the same setup.
128 
129  @param categories is a list of categories to plot.
130  @param regions is a list of the regions to plot.
131  @param exprArgs is a list of lists of parameters to be used in PlotStack::MakeCanvas().
132  Normally, the first expression is the default expression to be plotted.
133  The basename of the output file is automatically set by this.
134  If the last argument is actually a dictionary, it will be passed as
135  key word arguments to ParallelStackContainer.MakePlot() instead.
136  Key words supported in dictionary and types:
137  - data_expr takes a string (default '')
138  - cut_lines takes a list of floats (default from Nminus1Cut)
139  - var_name takes a string (default variable in exprArgs)
140  - doNminus1 takes a bool (default True)
141  @param overwrite overwrites old plots with the same name if set to true.
142  Otherwise, those plots are skipped.
143  @param parallel determines whether or not to run the plots in a Multiprocess fashion.
144  @param showCutLines determines whether or not to show the cut lines that would otherwise exist in
145  any N - 1 plots generated.
146  @param limitHistsDir is the directory to place files with the histograms for the limits fit.
147  @param aPlotter is the plotter to use to plot. The default is the plotter defined in this module.
148  @param systematics is a dictionary pointing to a list of affected branches for each systematic
149  """
150 
151  if not type(categories) == list:
152  MakePlots([categories], regions, exprArgs, overwrite, parallel, showCutLines, limitHistsDir, aPlotter)
153 
154  elif not type(regions) == list:
155  MakePlots(categories, [regions], exprArgs, overwrite, parallel, showCutLines, limitHistsDir, aPlotter)
156 
157  else:
158  passToParallel = []
159  for category in categories:
160  for region in regions:
161  for exprArg in exprArgs:
162  passToParallel.append([category, region, exprArg])
163 
164  if limitHistsDir and not os.path.exists(limitHistsDir):
165  os.makedirs(limitHistsDir)
166 
167  bPlotter = ParallelStackContainer(aPlotter, overwrite, showCutLines, limitHistsDir, systematics)
168 
169  if parallel:
170  from ..Parallelization import RunParallel
171  RunParallel(bPlotter, 'MakePlot', passToParallel)
172 
173  else:
174  for args in passToParallel:
175  bPlotter.MakePlot(*args)
176 
177 
178 def PreparePlots(categories, regions, exprArgs, systematics=None, envelope=None):
179  """
180  Prepares plotter with plots to make
181  @param categories list of categories
182  @param regions list of regions to plot
183  @param exprArgs list of tuples containing (expr, nbins, xbins) or (expr, nbins, min, max)
184  @param systematics is a dictionary pointing to a list of affected branches for each systematic
185  @param envelope is a dictionary that points to lists of weight branches to generate an envelope
186  """
187 
188  from ..LoadConfig import cuts
189  from . import AddOutDir
190 
191  if type(categories) != list:
192  return PreparePlots([categories], regions, exprArgs, systematics, envelope)
193 
194  elif type(regions) != list:
195  return PreparePlots(categories, [regions], exprArgs, systematics, envelope)
196 
197  systematics = systematics or {}
198  envelope = envelope or {}
199 
200  for cat in categories:
201  for region in regions:
202  for expr in exprArgs:
203  d_expr = expr[0]
204 
205  if '__' in region:
206  syst = region.split('__')[1]
207  syst = syst[:-2] if syst.endswith('Up') else syst[:-4]
208  if d_expr in systematics.get(syst, []):
209  d_expr = '%s_%s' % (d_expr, region.split('__')[1])
210  elif syst in envelope:
211  plotter.StartEnvelope(region.endswith('Up'))
212  for env in envelope[syst]:
213  if isinstance(env, str):
214  plotter.AddEnvelopeWeight(env)
215  else:
216  plotter.AddEnvelopeWeight(*env)
217 
218  cut = cuts.cut(cat, region)
219  if not os.environ.get('blind'): # Don't do this with blinded plots (for limits)
220  cut = Nminus1Cut(cut, d_expr)
221 
222  args = ['_'.join([cat, region,
223  expr[0].replace(',', '__').replace(')', '__').replace('(', '__')])] + \
224  expr[1:] + [d_expr, d_expr, cut, cuts.dataMCCuts(region, True), cuts.dataMCCuts(region, False)]
225  plotter.AddHist(*args)