plotter.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/env python3
  2. import matplotlib as mpl
  3. mpl.rc('font', **{'family': 'sans-serif', 'sans-serif': ['Helvetica']})
  4. mpl.rc('font', **{'family': 'serif', 'serif': ['Palatino']})
  5. mpl.rc('text', usetex=True)
  6. mpl.rc('savefig', dpi=120)
  7. class StackHist:
  8. def __init__(self, title=""):
  9. self.title = title
  10. self.xlabel = ""
  11. self.ylabel = ""
  12. self.xlim = (None, None)
  13. self.ylim = (None, None)
  14. self.logx = False
  15. self.logy = False
  16. self.backgrounds = []
  17. self.signal = None
  18. self.signal_stack = True
  19. self.data = None
  20. @staticmethod
  21. def to_bin_list(th1, scale=1):
  22. bins = []
  23. for i in range(th1.GetNbinsX()):
  24. center = th1.GetBinCenter(i + 1)
  25. width = th1.GetBinWidth(i + 1)
  26. content = th1.GetBinContent(i + 1)
  27. bins.append((center-width/2, center+width/2, content*scale))
  28. return bins
  29. def add_mc_background(self, th1, label, lumi=None):
  30. self.backgrounds.append((label, lumi, self.to_bin_list(th1)))
  31. def set_mc_signal(self, th1, label, lumi=None, stack=True, scale=1):
  32. if scale != 1:
  33. label = r"{}$\times{:02d}$".format(label, scale)
  34. self.signal = (label, lumi, self.to_bin_list(th1, scale))
  35. self.signal_stack = stack
  36. def set_data(self, th1, lumi=None):
  37. self.data = ('data', lumi, self.to_bin_list(th1))
  38. self.luminosity = lumi
  39. def _verify_binning_match(self):
  40. bins_count = [len(bins) for label, lumi, bins in self.backgrounds]
  41. if self.signal is not None:
  42. bins_count.append(len(self.signal[2]))
  43. if self.data is not None:
  44. bins_count.append(len(self.data[2]))
  45. n_bins = bins_count[0]
  46. if any(bin_count != n_bins for bin_count in bins_count):
  47. raise ValueError("all histograms must have the same number of bins")
  48. return n_bins
  49. def _add_decorations(self, axes):
  50. cms_prelim = r'{\raggedright{}\textsf{\textbf{CMS}}\\ \emph{Preliminary}}'
  51. axes.text(0.01, 0.99, cms_prelim,
  52. horizontalalignment='left',
  53. verticalalignment='top',
  54. transform=axes.transAxes)
  55. lumi = ""
  56. energy = ""
  57. if self.luminosity is not None:
  58. lumi = r'${} \mathrm{{fb}}^{{-1}}$'.format(self.luminosity)
  59. if self.energy is not None:
  60. energy = r'({} TeV)'.format(self.energy)
  61. axes.text(1, 1, ' '.join([lumi, energy]),
  62. horizontalalignment='right',
  63. verticalalignment='bottom',
  64. transform=axes.transAxes)
  65. def draw(self, axes):
  66. n_bins = self._verify_binning_match()
  67. bottoms = [0]*n_bins
  68. if self.logx:
  69. axes.set_xscale('log')
  70. if self.logy:
  71. axes.set_yscale('log')
  72. def draw_bar(label, lumi, bins, stack=True, **kwargs):
  73. if stack:
  74. lefts = []
  75. widths = []
  76. heights = []
  77. for left, right, content in bins:
  78. lefts.append(left)
  79. widths.append(right-left)
  80. if lumi is not None:
  81. content *= self.luminosity/lumi
  82. heights.append(content)
  83. axes.bar(lefts, heights, widths, bottoms, label=label, **kwargs)
  84. for i, (_, _, content) in enumerate(bins):
  85. if lumi is not None:
  86. content *= self.luminosity/lumi
  87. bottoms[i] += content
  88. else:
  89. xs = [bins[0][0] - (bins[0][1]-bins[0][0])/2]
  90. ys = [0]
  91. for left, right, content in bins:
  92. width2 = (right-left)/2
  93. if lumi is not None:
  94. content *= self.luminosity/lumi
  95. xs.append(left-width2)
  96. ys.append(content)
  97. xs.append(right-width2)
  98. ys.append(content)
  99. xs.append(bins[-1][0] + (bins[-1][1]-bins[-1][0])/2)
  100. ys.append(0)
  101. axes.plot(xs, ys, label=label, **kwargs)
  102. if self.signal is not None and self.signal_stack:
  103. draw_bar(*self.signal, hatch='/')
  104. for background in self.backgrounds:
  105. draw_bar(*background)
  106. if self.signal is not None and not self.signal_stack:
  107. draw_bar(*self.signal, stack=False, color='k')
  108. axes.set_title(self.title)
  109. axes.set_xlabel(self.xlabel)
  110. axes.set_ylabel(self.ylabel)
  111. axes.set_xlim(*self.xlim)
  112. # axes.set_ylim(*self.ylim)
  113. axes.set_ylim(None, max(bottoms)*1.2)
  114. axes.legend(frameon=True, ncol=2)
  115. self._add_decorations(axes)
  116. if __name__ == '__main__':
  117. import matplotlib.pyplot as plt
  118. from utils import ResultSet
  119. rs_TTZ = ResultSet("TTZ", "../data/TTZToLLNuNu_treeProducerSusyMultilepton_tree.root")
  120. rs_TTW = ResultSet("TTW", "../data/TTWToLNu_treeProducerSusyMultilepton_tree.root")
  121. rs_TTH = ResultSet("TTH", "../data/TTHnobb_mWCutfix_ext1_treeProducerSusyMultilepton_tree.root")
  122. rs_TTTT = ResultSet("TTTT", "../data/TTTT_ext_treeProducerSusyMultilepton_tree.root")
  123. sh = StackHist('B-Jet Multiplicity')
  124. sh.add_mc_background(rs_TTZ.b_jet_count, 'TTZ', lumi=40)
  125. sh.add_mc_background(rs_TTW.b_jet_count, 'TTW', lumi=40)
  126. sh.add_mc_background(rs_TTH.b_jet_count, 'TTH', lumi=40)
  127. sh.set_mc_signal(rs_TTTT.b_jet_count, 'TTTT', lumi=40, scale=10)
  128. sh.luminosity = 40
  129. sh.energy = 13
  130. sh.xlabel = 'B-Jet Count'
  131. sh.ylabel = r'\# Events'
  132. sh.xlim = (-.5, 9.5)
  133. sh.signal_stack = False
  134. fig = plt.figure()
  135. sh.draw(fig.gca())
  136. plt.show()
  137. # sh.add_data(rs_TTZ.b_jet_count, 'TTZ')