ATLAS Offline Software
merge_scale_histograms.py
Go to the documentation of this file.
1 #!/bin/env python
2 
3 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
4 
5 
6 from array import array
7 import logging
8 import ROOT
9 doc = """
10 This is an utility to merge histograms. The common case is when you have
11 old scale factors for the whole calorimeter and new scale factor only for
12 a subpart.
13 """
14 
15 ROOT.PyConfig.IgnoreCommandLineOptions = True
16 logging.basicConfig(level=logging.INFO)
17 
18 
19 def merge_histograms(old, new, merge_error=True):
20  print("old binning: " + ", ".join(("%.3f" % old.GetBinLowEdge(ibin))
21  for ibin in range(1, old.GetNbinsX()
22  + 2)))
23  print("new binning: " + ", ".join(("%.3f" % new.GetBinLowEdge(ibin))
24  for ibin in range(1, new.GetNbinsX()
25  + 2)))
26 
27  new_binning = []
28  new_values = []
29  new_errors = []
30  UNDERFLOW = 0
31  OVERFLOW = new.GetNbinsX() + 1
32 
33  for iold in range(1, old.GetNbinsX()):
34  low = old.GetBinLowEdge(iold)
35  r = low + old.GetBinWidth(iold)
36 
37  il_new = new.FindFixBin(low)
38  ir_new = new.FindFixBin(r)
39  remainer = None
40 
41  if il_new == UNDERFLOW and ir_new == UNDERFLOW:
42  print("1. adding %.3f - %.3f from old" % (low, r))
43  new_binning.append((low, r))
44  new_values.append(old.GetBinContent(iold))
45  new_errors.append(old.GetBinError(iold))
46 
47  elif il_new == UNDERFLOW and ir_new > UNDERFLOW:
48  if abs(new.GetBinLowEdge(1) - low) < 1E-100:
49  continue
50  new_binning.append((low, new.GetBinLowEdge(1)))
51  new_values.append(old.GetBinContent(iold))
52  new_errors.append(old.GetBinError(iold))
53  if ir_new == OVERFLOW:
54  remainer = iold # noqa: F841
55  print("breaking")
56  break
57  last_old = iold
58 
59  for inew in range(1, new.GetNbinsX() + 1):
60  low = new.GetBinLowEdge(inew)
61  r = low + new.GetBinWidth(inew)
62  print("2. adding %.3f - %.3f from new" % (low, r))
63  new_binning.append((low, r))
64  new_values.append(new.GetBinContent(inew))
65  new_errors.append(new.GetBinError(inew))
66 
67  for iold in range(last_old, old.GetNbinsX() + 1):
68  low = old.GetBinLowEdge(iold)
69  r = low + old.GetBinWidth(iold)
70 
71  il_new = new.FindFixBin(low)
72  ir_new = new.FindFixBin(r)
73 
74  if il_new == OVERFLOW and ir_new == OVERFLOW:
75  print("4. adding %.3f - %.3f from old" % (low, r))
76  new_binning.append((low, r))
77  new_values.append(old.GetBinContent(iold))
78  new_errors.append(old.GetBinError(iold))
79  elif il_new < OVERFLOW and ir_new == OVERFLOW:
80  if abs(new.GetBinLowEdge(new.GetNbinsX() + 1) - r) < 1E-100:
81  continue
82  new_binning.append((new.GetBinLowEdge(new.GetNbinsX() + 1), r))
83  new_values.append(old.GetBinContent(iold))
84  new_errors.append(old.GetBinError(iold))
85 
86  print(new_binning)
87  new_edges = array('f', [x[0] for x in new_binning] + [new_binning[-1][1]])
88  histo_type = type(new)
89  result = histo_type(new.GetName(), new.GetTitle(),
90  len(new_edges) - 1, new_edges)
91  for i, (v, e) in enumerate(zip(new_values, new_errors), 1):
92  result.SetBinContent(i, v)
93  if merge_error:
94  result.SetBinError(i, e)
95 
96  print("merged binning: " + ", ".join(("%.3f" % result.GetBinLowEdge(ibin))
97  for ibin in range(1, result.GetNbinsX()
98  + 1)))
99 
100  return result
101 
102 
103 if __name__ == "__main__":
104  try:
105  ROOT.gROOT.ProcessLine(".x ~/atlasstyle/AtlasStyle.C")
106  ROOT.SetAtlasStyle()
107  except AttributeError:
108  pass
109 
110  import argparse
111 
112  parser = argparse.ArgumentParser(description=doc,
113  formatter_class=argparse.RawTextHelpFormatter)
114  parser.add_argument('histo_old')
115  parser.add_argument('histo_new')
116  parser.add_argument('--title', help='title of the new histogram')
117  parser.add_argument('--name', help='name of the new histogram')
118  parser.add_argument(
119  '--recreate', help='create a new file', action='store_true')
120  parser.add_argument('--output-filename', default='output.root')
121 
122  args = parser.parse_args()
123 
124  file_old = ROOT.TFile.Open(args.histo_old.split(":")[0])
125  file_new = ROOT.TFile.Open(args.histo_new.split(":")[0])
126 
127  histo_old = file_old.Get(args.histo_old.split(":")[1])
128  histo_new = file_new.Get(args.histo_new.split(":")[1])
129 
130  if not histo_old:
131  raise IOError("cannot find histogram %s" % args.histo_old)
132  if not histo_new:
133  raise IOError("cannot find histogram %s" % args.histo_new)
134 
135  logging.info("going to merge %s with %s",
136  histo_old.GetName(), histo_new.GetName())
137  histo_merged = merge_histograms(histo_old, histo_new)
138 
139  canvas = ROOT.TCanvas()
140  histo_merged.SetFillColor(ROOT.kBlue)
141  histo_old.SetMarkerColor(ROOT.kRed)
142  histo_new.SetMarkerColor(ROOT.kGreen)
143  for histo in histo_old, histo_new:
144  histo.SetMarkerStyle(20)
145  histo.SetMarkerSize(1)
146  histo_merged.Draw("histo")
147  histo_old.Draw("Psame")
148  histo_new.Draw("Psame")
149 
150  legend = ROOT.TLegend(0.6, 0.7, 0.9, 0.9)
151  legend.AddEntry(histo_old, "old")
152  legend.AddEntry(histo_new, "new")
153  legend.SetBorderSize(0)
154  legend.Draw()
155 
156  fout = ROOT.TFile.Open(args.output_filename,
157  "recreate" if args.recreate else "update")
158  if args.title is not None:
159  histo_merged.SetTitle(args.title)
160  name = args.name or histo_old.GetName()
161  histo_merged.SetName(name)
162  histo_merged.Write()
163 
164  input("press a key")
merge_scale_histograms.merge_histograms
def merge_histograms(old, new, merge_error=True)
Definition: merge_scale_histograms.py:19
PlotPulseshapeFromCool.input
input
Definition: PlotPulseshapeFromCool.py:106
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
array
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
dbg::print
void print(std::FILE *stream, std::format_string< Args... > fmt, Args &&... args)
Definition: SGImplSvc.cxx:70