4 from __future__
import absolute_import
7 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu> 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 40 from .
import argparse
41 from os.path
import join
as joined
42 from .utilities
import rootglob, loadROOT
49 from .version
import __version__
57 def __init__(self, filename, path='', scale=1., scale_error=None):
65 def newadd(outfile, targets, dest_path=""):
67 if allsame([x.filename
for x
in targets]):
68 f = ROOT.TFile(targets[0].filename,
'read')
69 paths = [x.path
for x
in targets]
70 scales = [x.scale
for x
in targets]
71 scale_errors = [x.scale_error
for x
in targets]
72 if f.GetDirectory(paths[0]):
74 for h
in [os.path.basename(x)
for x
in 76 hists = [f.GetDirectory(x).Get(h)
for x
in paths]
77 if not alltrue([x
and x.InheritsFrom(
'TH1')
for x
in hists]):
79 dest = joined(destdir, h)
80 add(outfile, dest, hists, scales, dest_path, scale_errors=scale_errors)
82 hists = [f.Get(x)
for x
in paths]
83 if alltrue([x
and x.InheritsFrom(
'TH1')
for x
in hists]):
85 add(outfile, dest, hists, scales, scale_errors=scale_errors)
89 for target
in targets:
90 dict_targets.setdefault(target.filename, []).
append((target.path, target.scale))
91 if (target.filename
not in dict_tfiles):
93 dict_tfiles[target.filename] = ROOT.TFile(target.filename,
'read')
97 f = ROOT.TFile(targets[0].filename,
'read')
98 if f.GetDirectory(targets[0].path):
101 histnames = [os.path.basename(x)
for x
in 102 rootglob(f, targets[0].path +
'/*')]
106 for histname
in histnames:
109 for filename
in dict_targets:
110 tfile_cur = dict_tfiles[filename]
111 for path, scale
in dict_targets[filename]:
112 hists.append(tfile_cur.GetDirectory(path).Get(histname))
115 if not alltrue([x
and x.InheritsFrom(
'TH1')
for x
in hists]):
117 dest = joined(destdir, histname)
118 add(outfile, dest, hists, scales, dest_path)
120 print "Code not written yet to add histograms from multiple files" 130 keys = rootfile.GetDirectory(path).GetListOfKeys()
131 folders, objects = [], []
134 classname = key.GetClassName()
135 newpath = joined(path, name)
137 if 'TDirectory' in classname:
141 yield path, folders, objects
142 for folder
in folders:
147 for element
in iterable:
148 if element != iterable[0]:
153 for element
in iterable:
160 Return the appropriate destination for an object. 162 In all cases, the result will be placed in the deepest directory shared by 163 all paths. If the histogram names are the same, the result will be named 164 based on the first directories that they do not share. Otherwise, the 165 result will be named based on the names of the other histograms. 167 >>> pathdiff(['/dirA/dirB/dirX/hist', '/dirA/dirB/dirY/hist'], '_div_') 168 '/dirA/dirB/dirX_div_dirY' 169 >>> pathdiff(['/dirA/hist1', '/dirA/hist2', '/dirA/hist3'], '_plus_') 170 '/dirA/hist1_plus_hist2_plus_hist3' 171 >>> pathdiff(['/hist1', '/dirA/hist2'], '_minus_') 174 paths = [x.split(
'/')
for x
in paths]
176 for i
in range(len(paths[0])):
177 if allsame([p[i]
for p
in paths]):
178 dest = joined(dest, paths[0][i])
181 name = joiner.join([p[-1]
for p
in paths])
182 if allsame([p[-1]
for p
in paths]):
183 for i
in range(len(paths[0])):
184 if not allsame([p[i]
for p
in paths]):
185 name = joiner.join([p[i]
for p
in paths])
186 return joined(dest, name)
192 paths = [x.split(
'/')
for x
in paths]
194 for i
in range(len(paths[0])):
195 if allsame([p[i]
for p
in paths]):
196 commonbeginning = joined(commonbeginning, paths[0][i])
200 for i
in range(-1, -1 * len(paths[0]), -1):
201 if allsame([p[i]
for p
in paths]):
202 commonending = joined(paths[0][i], commonending)
209 return joined(commonbeginning, commonending)
213 Return the appropriate destination for an object. 215 If the final objects in each path match, then the return value will be the 216 matching part of the paths. Otherwise, the output path will simply be those 217 names joined together with *joiner*. See the examples below. 219 >>> pathdiff3(['/dirA/dirX/hist', '/dirA/dirY/hist']) 221 >>> pathdiff3(['/dirA/dirX/dirB/hist', '/dirA/dirY/dirB/hist']) 223 >>> pathdiff3(['/dirA/hist1', '/dirA/hist2', '/dirA/hist3'], '_plus_') 224 '/hist1_plus_hist2_plus_hist3' 225 >>> pathdiff3(['/hist1', '/dirA/hist2'], '_div_') 228 paths = [x.split(
'/')
for x
in paths]
229 if allsame([x[-1]
for x
in paths]):
231 for i
in range(-2,
min([len(x)
for x
in paths]) * -1, -1):
232 if allsame([p[i]
for p
in paths]):
233 dest = joined(paths[0][i], dest)
238 return '/' + joiner.join([x[-1]
for x
in paths])
241 def newfunc(outfile, dest, hists, scales=None, dest_path="", scale_errors=None):
243 for d
in os.path.dirname(dest).
split(
'/'):
244 if not ROOT.gDirectory.GetDirectory(d):
245 ROOT.gDirectory.mkdir(d)
246 ROOT.gDirectory.cd(d)
247 fn(outfile, dest, hists, scales, dest_path, scale_errors)
251 '''Scale a histogram by a scale factor that has an error. 252 This takes into account the scale error to set new error bars.''' 253 hist_new = hist.Clone()
255 for i
in range(hist_new.GetNbinsX()+2):
256 hist_new.SetBinContent(i, scale)
257 hist_new.SetBinError(i, scale_error)
258 hist_new.Multiply(hist)
260 hist_new.Scale(scale)
264 def add(outfile, dest, hists, scales=None, dest_path="", scale_errors=None):
266 scales = [1.
for i
in range(len(hists))]
268 scale_errors = [
None for i
in range(len(hists))]
269 sumhist = hists[0].Clone(os.path.basename(dest))
272 for i
in range(1,len(hists)):
277 if not ROOT.gDirectory.GetDirectory(dest_path):
278 ROOT.gDirectory.mkdir(dest_path)
279 ROOT.gDirectory.cd(dest_path)
281 ROOT.gDirectory.cd(
"/")
285 diffhist = hists[0].Clone(os.path.basename(dest))
286 for hist
in hists[1:]:
287 diffhist.Add(hist, -1)
292 quotient = numer.Clone(os.path.basename(dest))
293 quotient.Divide(numer, denom)
298 quotient = ROOT.TGraphAsymmErrors()
299 quotient.SetName(os.path.basename(dest))
300 quotient.BayesDivide(numer, denom)
304 parser = argparse.ArgumentParser()
305 parser.add_argument(
'filenames', type=str, nargs=
'+',
306 help=
'root files to process')
307 parser.add_argument(
'--dirs', type=str, nargs=
'+', default=[
'/'],
308 help=
'target directories in the root files; paths to ' 309 'histograms will be relative to these')
310 parser.add_argument(
'--add', default=[], action=
'append', nargs=
'*',
311 help=
'a list of directories or histograms to add')
312 parser.add_argument(
'--subtract', default=[], action=
'append', nargs=
'*',
313 help=
'a list of directories or histograms to subtract')
314 parser.add_argument(
'--divide', default=[], action=
'append', nargs=
'*',
315 help=
'2 directories or histograms to divide')
316 parser.add_argument(
'--bayes-divide', default=[], action=
'append', nargs=
'*',
317 help=
'2 directories or histograms from which to make ' 318 'an efficiency plot')
319 args = parser.parse_args()
320 separators = {
'add' :
'_plus_',
321 'subtract' :
'_minus_',
323 'bayes_divide' :
'_eff_'}
325 files = [ROOT.TFile(x,
'read')
for x
in args.filenames]
326 outfile = ROOT.TFile(
'out.root',
'recreate')
334 for operation_type, separator
in separators.items():
335 for arg_set
in getattr(args, operation_type):
336 paths = [joined(thisdir, x)
for x
in arg_set]
337 if f.GetDirectory(paths[0]):
338 destdir =
pathdiff(paths, separator)
339 for target
in [os.path.basename(x)
for x
in 341 hists = [f.GetDirectory(x).Get(target)
343 if not alltrue([x
and x.InheritsFrom(
'TH1')
346 dest = joined(destdir, target)
347 math_func = globals()[operation_type]
348 math_func(outfile, dest, hists)
350 hists = [f.GetDirectory(thisdir).Get(x)
for x
in paths]
351 if not alltrue([x
and x.InheritsFrom(
'TH1')
355 math_func = globals()[operation_type]
356 math_func(outfile, dest, hists)
358 for operation_type, separator
in separators.items():
359 arg_sets = getattr(args, operation_type)
360 if arg_sets
and arg_sets != [[]]:
361 raise ValueError(
"No arguments to --%s allowed when multiple " 362 "files are specified" % operation_type)
364 if 'divide' in operation_type
and len(files) != 2:
365 raise ValueError(
"Exactly 2 files are expected with --%s; " 366 "%i given" % (operation_type, len(files)))
369 hists = [x.GetDirectory(path).Get(obj)
for x
in files]
370 if not alltrue([x
and x.InheritsFrom(
'TH1')
373 math_func = globals()[operation_type]
374 math_func(outfile, joined(path, obj), hists)
378 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)
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="")