4 from __future__
import absolute_import
5 from __future__
import print_function
7 from builtins
import range
9 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu> 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: 18 The above copyright notice and this permission notice shall be included in 19 all copies or substantial portions of the Software. 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 42 from .
import argparse
43 from os.path
import join
as joined
44 from .utilities
import rootglob, loadROOT
51 from .version
import __version__
59 def __init__(self, filename, path='', scale=1., scale_error=None):
67 def newadd(outfile, targets, dest_path=""):
69 if allsame([x.filename
for x
in targets]):
70 f = ROOT.TFile(targets[0].filename,
'read')
71 paths = [x.path
for x
in targets]
72 scales = [x.scale
for x
in targets]
73 scale_errors = [x.scale_error
for x
in targets]
74 if f.GetDirectory(paths[0]):
76 for h
in [os.path.basename(x)
for x
in 78 hists = [f.GetDirectory(x).Get(h)
for x
in paths]
79 if not alltrue([x
and x.InheritsFrom(
'TH1')
for x
in hists]):
81 dest = joined(destdir, h)
82 add(outfile, dest, hists, scales, dest_path, scale_errors=scale_errors)
84 hists = [f.Get(x)
for x
in paths]
85 if alltrue([x
and x.InheritsFrom(
'TH1')
for x
in hists]):
87 add(outfile, dest, hists, scales, scale_errors=scale_errors)
91 for target
in targets:
92 dict_targets.setdefault(target.filename, []).
append((target.path, target.scale))
93 if (target.filename
not in dict_tfiles):
95 dict_tfiles[target.filename] = ROOT.TFile(target.filename,
'read')
99 f = ROOT.TFile(targets[0].filename,
'read')
100 if f.GetDirectory(targets[0].path):
103 histnames = [os.path.basename(x)
for x
in 104 rootglob(f, targets[0].path +
'/*')]
108 for histname
in histnames:
111 for filename
in dict_targets:
112 tfile_cur = dict_tfiles[filename]
113 for path, scale
in dict_targets[filename]:
114 hists.append(tfile_cur.GetDirectory(path).Get(histname))
117 if not alltrue([x
and x.InheritsFrom(
'TH1')
for x
in hists]):
119 dest = joined(destdir, histname)
120 add(outfile, dest, hists, scales, dest_path)
122 print(
"Code not written yet to add histograms from multiple files")
132 keys = rootfile.GetDirectory(path).GetListOfKeys()
133 folders, objects = [], []
136 classname = key.GetClassName()
137 newpath = joined(path, name)
139 if 'TDirectory' in classname:
143 yield path, folders, objects
144 for folder
in folders:
149 for element
in iterable:
150 if element != iterable[0]:
155 for element
in iterable:
162 Return the appropriate destination for an object. 164 In all cases, the result will be placed in the deepest directory shared by 165 all paths. If the histogram names are the same, the result will be named 166 based on the first directories that they do not share. Otherwise, the 167 result will be named based on the names of the other histograms. 169 >>> pathdiff(['/dirA/dirB/dirX/hist', '/dirA/dirB/dirY/hist'], '_div_') 170 '/dirA/dirB/dirX_div_dirY' 171 >>> pathdiff(['/dirA/hist1', '/dirA/hist2', '/dirA/hist3'], '_plus_') 172 '/dirA/hist1_plus_hist2_plus_hist3' 173 >>> pathdiff(['/hist1', '/dirA/hist2'], '_minus_') 176 paths = [x.split(
'/')
for x
in paths]
178 for i
in range(len(paths[0])):
179 if allsame([p[i]
for p
in paths]):
180 dest = joined(dest, paths[0][i])
183 name = joiner.join([p[-1]
for p
in paths])
184 if allsame([p[-1]
for p
in paths]):
185 for i
in range(len(paths[0])):
186 if not allsame([p[i]
for p
in paths]):
187 name = joiner.join([p[i]
for p
in paths])
188 return joined(dest, name)
194 paths = [x.split(
'/')
for x
in paths]
196 for i
in range(len(paths[0])):
197 if allsame([p[i]
for p
in paths]):
198 commonbeginning = joined(commonbeginning, paths[0][i])
202 for i
in range(-1, -1 * len(paths[0]), -1):
203 if allsame([p[i]
for p
in paths]):
204 commonending = joined(paths[0][i], commonending)
211 return joined(commonbeginning, commonending)
215 Return the appropriate destination for an object. 217 If the final objects in each path match, then the return value will be the 218 matching part of the paths. Otherwise, the output path will simply be those 219 names joined together with *joiner*. See the examples below. 221 >>> pathdiff3(['/dirA/dirX/hist', '/dirA/dirY/hist']) 223 >>> pathdiff3(['/dirA/dirX/dirB/hist', '/dirA/dirY/dirB/hist']) 225 >>> pathdiff3(['/dirA/hist1', '/dirA/hist2', '/dirA/hist3'], '_plus_') 226 '/hist1_plus_hist2_plus_hist3' 227 >>> pathdiff3(['/hist1', '/dirA/hist2'], '_div_') 230 paths = [x.split(
'/')
for x
in paths]
231 if allsame([x[-1]
for x
in paths]):
233 for i
in range(-2,
min([len(x)
for x
in paths]) * -1, -1):
234 if allsame([p[i]
for p
in paths]):
235 dest = joined(paths[0][i], dest)
240 return '/' + joiner.join([x[-1]
for x
in paths])
243 def newfunc(outfile, dest, hists, scales=None, dest_path="", scale_errors=None):
245 for d
in os.path.dirname(dest).
split(
'/'):
246 if not ROOT.gDirectory.GetDirectory(d):
247 ROOT.gDirectory.mkdir(d)
248 ROOT.gDirectory.cd(d)
249 fn(outfile, dest, hists, scales, dest_path, scale_errors)
253 '''Scale a histogram by a scale factor that has an error. 254 This takes into account the scale error to set new error bars.''' 255 hist_new = hist.Clone()
257 for i
in range(hist_new.GetNbinsX()+2):
258 hist_new.SetBinContent(i, scale)
259 hist_new.SetBinError(i, scale_error)
260 hist_new.Multiply(hist)
262 hist_new.Scale(scale)
266 def add(outfile, dest, hists, scales=None, dest_path="", scale_errors=None):
268 scales = [1.
for i
in range(len(hists))]
270 scale_errors = [
None for i
in range(len(hists))]
271 sumhist = hists[0].Clone(os.path.basename(dest))
274 for i
in range(1,len(hists)):
279 if not ROOT.gDirectory.GetDirectory(dest_path):
280 ROOT.gDirectory.mkdir(dest_path)
281 ROOT.gDirectory.cd(dest_path)
283 ROOT.gDirectory.cd(
"/")
287 diffhist = hists[0].Clone(os.path.basename(dest))
288 for hist
in hists[1:]:
289 diffhist.Add(hist, -1)
294 quotient = numer.Clone(os.path.basename(dest))
295 quotient.Divide(numer, denom)
300 quotient = ROOT.TGraphAsymmErrors()
301 quotient.SetName(os.path.basename(dest))
302 quotient.BayesDivide(numer, denom)
306 parser = argparse.ArgumentParser()
307 parser.add_argument(
'filenames', type=str, nargs=
'+',
308 help=
'root files to process')
309 parser.add_argument(
'--dirs', type=str, nargs=
'+', default=[
'/'],
310 help=
'target directories in the root files; paths to ' 311 'histograms will be relative to these')
312 parser.add_argument(
'--add', default=[], action=
'append', nargs=
'*',
313 help=
'a list of directories or histograms to add')
314 parser.add_argument(
'--subtract', default=[], action=
'append', nargs=
'*',
315 help=
'a list of directories or histograms to subtract')
316 parser.add_argument(
'--divide', default=[], action=
'append', nargs=
'*',
317 help=
'2 directories or histograms to divide')
318 parser.add_argument(
'--bayes-divide', default=[], action=
'append', nargs=
'*',
319 help=
'2 directories or histograms from which to make ' 320 'an efficiency plot')
321 args = parser.parse_args()
322 separators = {
'add' :
'_plus_',
323 'subtract' :
'_minus_',
325 'bayes_divide' :
'_eff_'}
327 files = [ROOT.TFile(x,
'read')
for x
in args.filenames]
328 outfile = ROOT.TFile(
'out.root',
'recreate')
336 for operation_type, separator
in separators.items():
337 for arg_set
in getattr(args, operation_type):
338 paths = [joined(thisdir, x)
for x
in arg_set]
339 if f.GetDirectory(paths[0]):
340 destdir =
pathdiff(paths, separator)
341 for target
in [os.path.basename(x)
for x
in 343 hists = [f.GetDirectory(x).Get(target)
345 if not alltrue([x
and x.InheritsFrom(
'TH1')
348 dest = joined(destdir, target)
349 math_func = globals()[operation_type]
350 math_func(outfile, dest, hists)
352 hists = [f.GetDirectory(thisdir).Get(x)
for x
in paths]
353 if not alltrue([x
and x.InheritsFrom(
'TH1')
357 math_func = globals()[operation_type]
358 math_func(outfile, dest, hists)
360 for operation_type, separator
in separators.items():
361 arg_sets = getattr(args, operation_type)
362 if arg_sets
and arg_sets != [[]]:
363 raise ValueError(
"No arguments to --%s allowed when multiple " 364 "files are specified" % operation_type)
366 if 'divide' in operation_type
and len(files) != 2:
367 raise ValueError(
"Exactly 2 files are expected with --%s; " 368 "%i given" % (operation_type, len(files)))
371 hists = [x.GetDirectory(path).Get(obj)
for x
in files]
372 if not alltrue([x
and x.InheritsFrom(
'TH1')
375 math_func = globals()[operation_type]
376 math_func(outfile, joined(path, obj), hists)
380 if __name__ ==
'__main__':
def loadROOT(batch=True)
Define additional helping functions.
def pathdiff2(paths, joiner='__', truncate=False)
def bayes_divide(outfile, dest, numer, denom)
def pathdiff3(paths, joiner='__')
def divide(outfile, dest, numer, denom)
S & print(S &os, JobReport::InputFile const &f)
def pathdiff(paths, joiner)
def scale_with_error(hist, scale, scale_error=None)
def __init__(self, filename, path='', scale=1., scale_error=None)
def subtract(outfile, dest, hists)
def add(outfile, dest, hists, scales=None, dest_path="", scale_errors=None)
def walk_rootfile(rootfile, path='')
Implementation ######################################################.
Classes #############################################################.
def rootglob(tdirectory, pathname)
def newadd(outfile, targets, dest_path="")