4 Tools for histogram sampling, in particular inverse transform sampling which is
5 missing from ROOT's TH2 classes.
8 __author__ =
"Andy Buckley <andy.buckley@cern.ch>"
10 import random, copy, ROOT
15 Load a histogram from a filename/TFile and histo name. If a single arg is
16 provided, it has to be a histo object and will be cloned before return.
19 if len(args) == 1
and issubclass(
type(args[0]), ROOT.TH1):
22 if isinstance(args[0], str)
and isinstance(args[1], str) :
23 f = ROOT.TFile.Open(args[0])
24 h = copy.deepcopy(f.Get(args[1]).Clone())
26 elif type(args[0])
is ROOT.TFile
and type(args[1])
is str:
27 h = args[0].
Get(args[1]).Clone()
29 raise Exception(
"Error in histogram loading from " + args)
35 Get the following from a histogram h, since the ROOT API sucks:
36 * list of global bin IDs (not even contiguous for 2D, gee thanks ROOT)
37 * dict mapping global bin IDs to a tuple of axis bin IDs
38 * list of nbins+1 cumulative bin values, in the same order as globalbins
40 globalbin_to_axisbin = {}
43 if issubclass(
type(h), ROOT.TH1):
44 for ix
in range(1, h.GetNbinsX()+1):
45 iglobal = h.GetBin(ix)
46 globalbins.append(iglobal)
47 globalbin_to_axisbin[iglobal] = (ix,)
48 cheights.append(cheights[-1] + h.GetBinContent(iglobal))
49 elif issubclass(
type(h), ROOT.TH2):
50 for ix
in range(1, h.GetNbinsX()+1):
51 for iy
in range(1, h.GetNbinsY()+1):
52 iglobal = h.GetBin(ix, iy)
53 globalbins.append(iglobal)
54 globalbin_to_axisbin[iglobal] = (ix, iy)
55 cheights.append(cheights[-1] + h.GetBinContent(iglobal))
56 return globalbins, globalbin_to_axisbin, cheights
61 Choose a random bin from the cumulative distribution list of nbins+1 entries.
63 TODO: Search more efficiently (lin and log guesses, then lin search or
64 binary split depending on vector size).
66 assert len(cheights) == len(globalbins)+1
67 randomheight = random.uniform(0, cheights[-1])
68 for i, iglobal
in enumerate(globalbins):
69 if randomheight >= cheights[i]
and randomheight < cheights[i+1]:
71 raise Exception(
"Sample fell outside range of cumulative distribution?!?!")
76 Choose a random bin via get_random_bin, then pick a uniform random x
77 point in that bin (without any attempt at estimating the in-bin distribution).
80 axisids = globalbin_to_axisbin.get(irand)
81 assert axisids
is not None
82 xrand = random.uniform(h.GetXaxis().GetBinLowEdge(axisids[0]), h.GetXaxis().GetBinUpEdge(axisids[0]))
88 Choose a random bin via get_random_bin, then pick a uniform random x,y
89 point in that bin (without any attempt at estimating the in-bin distribution).
92 axisids = globalbin_to_axisbin.get(irand)
93 assert axisids
is not None
94 xrand = random.uniform(h2.GetXaxis().GetBinLowEdge(axisids[0]), h2.GetXaxis().GetBinUpEdge(axisids[0]))
95 yrand = random.uniform(h2.GetYaxis().GetBinLowEdge(axisids[1]), h2.GetYaxis().GetBinUpEdge(axisids[1]))
100 "Minimal wrapper for ROOT TH1, for sampling consistency and easy loading"
104 self.globalbins, self.globalbin_to_axisbin, self.
cheights =
None,
None,
None
107 "A GetRandom that works for TH1s and uses Python random numbers"
108 if self.globalbins
is None or self.globalbin_to_axisbin
is None or self.
cheights is None:
113 "Forward all attributes to the contained TH1"
114 return getattr(self.
th1, attr)
118 "Minimal wrapper for ROOT TH2, for easy loading and to allow 2D sampling"
122 self.globalbins, self.globalbin_to_axisbin, self.
cheights =
None,
None,
None
125 "A GetRandom that works for TH2s"
126 if self.globalbins
is None or self.globalbin_to_axisbin
is None or self.
cheights is None:
131 "Forward other attributes to the contained TH2"
132 return getattr(self.
th2, attr)