1 |
bortigno |
1.1 |
#!/usr/bin/env python
|
2 |
|
|
import re
|
3 |
|
|
from sys import argv, stdout, stderr, exit
|
4 |
bortigno |
1.5 |
from myutils import StackMaker
|
5 |
bortigno |
1.1 |
from optparse import OptionParser
|
6 |
bortigno |
1.3 |
from HiggsAnalysis.CombinedLimit.DatacardParser import *
|
7 |
|
|
from HiggsAnalysis.CombinedLimit.ShapeTools import *
|
8 |
|
|
from copy import copy,deepcopy
|
9 |
bortigno |
1.4 |
from numpy import matrix
|
10 |
bortigno |
1.3 |
|
11 |
|
|
|
12 |
bortigno |
1.5 |
def conversion_y(y):
|
13 |
|
|
u = (y-ROOT.gPad.GetY1())/(ROOT.gPad.GetY2()-ROOT.gPad.GetY1())
|
14 |
|
|
return u;
|
15 |
|
|
|
16 |
|
|
def conversion_x(x):
|
17 |
|
|
u = (x-ROOT.gPad.GetX1())/(ROOT.gPad.GetX2()-ROOT.gPad.GetX1())
|
18 |
|
|
return u;
|
19 |
|
|
|
20 |
|
|
|
21 |
bortigno |
1.4 |
def trunc(f, n):
|
22 |
|
|
'''Truncates/pads a float f to n decimal places without rounding'''
|
23 |
|
|
slen = len('%.*f' % (n, f))
|
24 |
|
|
return str(f)[:slen]
|
25 |
|
|
|
26 |
bortigno |
1.3 |
|
27 |
|
|
def removeDouble(seq):
|
28 |
|
|
seen = set()
|
29 |
|
|
seen_add = seen.add
|
30 |
|
|
return [ x for x in seq if x not in seen and not seen_add(x)]
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
def getInputSigma(options):
|
34 |
|
|
opts = copy(options)
|
35 |
|
|
|
36 |
|
|
file = open(opts.dc, "r")
|
37 |
|
|
# os.chdir(os.path.dirname(opts.dc))
|
38 |
|
|
opts.bin = True
|
39 |
|
|
opts.noJMax = None
|
40 |
|
|
opts.nuisancesToExclude = []
|
41 |
|
|
opts.stat = False
|
42 |
|
|
opts.excludeSyst = ['SF']
|
43 |
|
|
DC = parseCard(file, opts)
|
44 |
|
|
|
45 |
|
|
theSyst = {}
|
46 |
|
|
nuiVar = {}
|
47 |
|
|
# if not opts.bin in DC.bins: raise RuntimeError, "Cannot open find %s in bins %s of %s" % (opts.bin,DC.bins,opts.dc)
|
48 |
|
|
b = DC.bins[0]
|
49 |
|
|
exps = {}
|
50 |
|
|
expNui = {}
|
51 |
|
|
for (p,e) in DC.exp[b].items(): # so that we get only self.DC.processes contributing to this bin
|
52 |
|
|
exps[p] = [ e, [] ]
|
53 |
|
|
expNui[p] = [ e, [] ]
|
54 |
|
|
# print DC.systs
|
55 |
|
|
for (lsyst,nofloat,pdf,pdfargs,errline) in DC.systs:
|
56 |
|
|
if pdf in ('param', 'flatParam'): continue
|
57 |
|
|
# begin skip systematics
|
58 |
|
|
skipme = False
|
59 |
|
|
for xs in opts.excludeSyst:
|
60 |
|
|
if not re.search(xs, lsyst):
|
61 |
|
|
skipme = True
|
62 |
|
|
if skipme: continue
|
63 |
|
|
# end skip systematics
|
64 |
|
|
counter = 0
|
65 |
|
|
for p in DC.exp[b].keys(): # so that we get only self.DC.processes contributing to this bin
|
66 |
|
|
if errline[b][p] == 0: continue
|
67 |
|
|
if p == 'QCD': continue
|
68 |
|
|
if pdf == 'gmN':
|
69 |
|
|
exps[p][1].append(1/sqrt(pdfargs[0]+1));
|
70 |
|
|
elif pdf == 'gmM':
|
71 |
|
|
exps[p][1].append(errline[b][p]);
|
72 |
|
|
elif type(errline[b][p]) == list:
|
73 |
|
|
kmax = max(errline[b][p][0], errline[b][p][1], 1.0/errline[b][p][0], 1.0/errline[b][p][1]);
|
74 |
|
|
exps[p][1].append(kmax-1.);
|
75 |
|
|
elif pdf == 'lnN':
|
76 |
|
|
exps[p][1].append(max(errline[b][p], 1.0/errline[b][p])-1.);
|
77 |
|
|
return exps
|
78 |
|
|
|
79 |
|
|
|
80 |
|
|
|
81 |
bortigno |
1.4 |
def get_scale_factors(channel,labels,shift,v_b,input_sigma,nuisances):
|
82 |
|
|
print 'Channel ' + channel
|
83 |
bortigno |
1.3 |
sf=[]
|
84 |
|
|
sf_e=[]
|
85 |
bortigno |
1.4 |
# correspondency_dictionary = {"TT":"TT","s_Top":"s_Top","Zj0b":"Z0b","Zj1b":"Z1b","Zj2b":"Z2b","Wj0b":"Wj0b","Wj1b":"Wj1b","Wj2b":"Wj2b","Zj1HF":"Z1b","Zj2HF":"Z2b","ZjLF":"Z0b"}
|
86 |
|
|
# correspondency_dictionary = {"TT":"TT","s_Top":"s_Top","Zj0b":"Zj0b","Zj1b":"Zj1b","Zj2b":"Zj2b","Wj0b":"Wj0b","Wj1b":"Wj1b","Wj2b":"Wj2b","Zj1HF":"Z1b","Zj2HF":"Z2b","ZjLF":"Z0b","s_Top":"s_Top"}
|
87 |
bortigno |
1.3 |
correspondency_dictionary = {"TT":"TT","s_Top":"s_Top","Zj0b":"Zj0b","Zj1b":"Zj1b","Zj2b":"Zj2b","Wj0b":"Wj0b","Wj1b":"Wj1b","Wj2b":"Wj2b","Zj1HF":"Z1b","Zj2HF":"Z2b","ZjLF":"Z0b","s_Top":"s_Top"}
|
88 |
|
|
# print input_sigma
|
89 |
|
|
# print input_sigma['TT'][1][0]
|
90 |
|
|
# initial_uncertainty=0.2 # initial uncertainty. @TO FIX: this can go in a config or as input arg
|
91 |
|
|
count=0
|
92 |
|
|
print labels
|
93 |
bortigno |
1.4 |
# channels = ['high','High','low','Low','Med','med']
|
94 |
|
|
# channels = ['Zee','Zmm']
|
95 |
|
|
channels = ['Zll']
|
96 |
bortigno |
1.3 |
for i in v_b:
|
97 |
|
|
print 'Nuisances ' + nuisances[count]
|
98 |
|
|
for h in channels:
|
99 |
|
|
if h in channel and h in re.sub('M','m',re.sub('L','l',re.sub('H','h',nuisances[count]))):
|
100 |
|
|
print count
|
101 |
|
|
try:
|
102 |
|
|
print 'Label : ' + str(labels[count])
|
103 |
|
|
print 'Relative SF : ' + i[0]
|
104 |
|
|
print 'Relative Error : ' + i[1]
|
105 |
|
|
print 'Correspondance : ' + str(correspondency_dictionary[labels[count]])
|
106 |
|
|
print 'Input sigma list : ' + str(input_sigma[correspondency_dictionary[labels[count]]])
|
107 |
|
|
print 'Input sigma value : ' + str(input_sigma[correspondency_dictionary[labels[count]]][1][0])
|
108 |
|
|
sf.append(1+input_sigma[correspondency_dictionary[labels[count]]][1][0]*eval(i[0])) # calculate the actual value for the scale factors
|
109 |
|
|
sf_e.append(input_sigma[correspondency_dictionary[labels[count]]][1][0]*eval(i[1])) # calculate the actula value for the uncertainties
|
110 |
|
|
print sf
|
111 |
|
|
print sf_e
|
112 |
|
|
except:
|
113 |
|
|
print 'Problem evaluating the SF or the SF errors'
|
114 |
|
|
count+=1
|
115 |
|
|
print sf
|
116 |
|
|
print sf_e
|
117 |
bortigno |
1.4 |
return [sf,sf_e]
|
118 |
|
|
|
119 |
|
|
|
120 |
|
|
|
121 |
|
|
def getGraph(channel,labels,shift,v_b,input_sigma,x_position,y_position,nuisances):
|
122 |
bortigno |
1.3 |
|
123 |
bortigno |
1.4 |
sf = get_scale_factors(channel,labels,shift,v_b,input_sigma,nuisances)[0]
|
124 |
|
|
sf_e = get_scale_factors(channel,labels,shift,v_b,input_sigma,nuisances)[1]
|
125 |
bortigno |
1.3 |
d = numpy.array(sf) # store scale factors in array
|
126 |
|
|
e = numpy.array(sf_e) # store scale factors errors in array
|
127 |
|
|
p = numpy.array(y_position)
|
128 |
|
|
zero = numpy.array(x_position)
|
129 |
|
|
|
130 |
|
|
# for i in range(len(labels)):
|
131 |
|
|
# print 'CMSSW_vhbb_'+labels[i]+'_Zll_SF_8TeV '+ str(d[i]) + ' +/- ' + str(e[i])
|
132 |
|
|
|
133 |
|
|
markerStyle = 20
|
134 |
|
|
if ('high' in channel or 'High' in channel ):
|
135 |
|
|
markerStyle = 21
|
136 |
|
|
if ('med' in channel or 'Med' in channel ):
|
137 |
|
|
markerStyle = 22
|
138 |
bortigno |
1.4 |
if ('Zee' in channel ):
|
139 |
|
|
markerStyle = 21
|
140 |
bortigno |
1.3 |
|
141 |
|
|
for i in range(len(p)): p[i] = p[i]+shift
|
142 |
|
|
print 'POSITIONS: '
|
143 |
|
|
print p
|
144 |
|
|
g = ROOT.TGraphErrors(n,d,p,e,zero)
|
145 |
|
|
g.SetFillColor(0)
|
146 |
|
|
g.SetLineColor(2)
|
147 |
|
|
g.SetLineWidth(3)
|
148 |
|
|
g.SetMarkerStyle(markerStyle)
|
149 |
bortigno |
1.4 |
print 'Ok'
|
150 |
bortigno |
1.3 |
return g
|
151 |
|
|
|
152 |
|
|
|
153 |
|
|
|
154 |
bortigno |
1.1 |
|
155 |
|
|
# import ROOT with a fix to get batch mode (http://root.cern.ch/phpBB3/viewtopic.php?t=3198)
|
156 |
|
|
hasHelp = False
|
157 |
|
|
for X in ("-h", "-?", "--help"):
|
158 |
|
|
if X in argv:
|
159 |
|
|
hasHelp = True
|
160 |
|
|
argv.remove(X)
|
161 |
|
|
argv.append( '-b-' )
|
162 |
|
|
import ROOT
|
163 |
|
|
ROOT.gROOT.SetBatch(True)
|
164 |
|
|
ROOT.gSystem.Load("libHiggsAnalysisCombinedLimit.so")
|
165 |
|
|
argv.remove( '-b-' )
|
166 |
|
|
if hasHelp: argv.append("-h")
|
167 |
|
|
|
168 |
|
|
parser = OptionParser(usage="usage: %prog [options] in.root \nrun with --help to get list of options")
|
169 |
|
|
parser.add_option("--vtol", "--val-tolerance", dest="vtol", default=0.30, type="float", help="Report nuisances whose value changes by more than this amount of sigmas")
|
170 |
|
|
parser.add_option("--stol", "--sig-tolerance", dest="stol", default=0.10, type="float", help="Report nuisances whose sigma changes by more than this amount")
|
171 |
|
|
parser.add_option("--vtol2", "--val-tolerance2", dest="vtol2", default=2.0, type="float", help="Report severely nuisances whose value changes by more than this amount of sigmas")
|
172 |
|
|
parser.add_option("--stol2", "--sig-tolerance2", dest="stol2", default=0.50, type="float", help="Report severely nuisances whose sigma changes by more than this amount")
|
173 |
|
|
parser.add_option("-a", "--all", dest="all", default=False, action="store_true", help="Print all nuisances, even the ones which are unchanged w.r.t. pre-fit values.")
|
174 |
|
|
parser.add_option("-A", "--absolute", dest="abs", default=False, action="store_true", help="Report also absolute values of nuisance values and errors, not only the ones normalized to the input sigma")
|
175 |
|
|
parser.add_option("-p", "--poi", dest="poi", default="r", type="string", help="Name of signal strength parameter (default is 'r' as per text2workspace.py)")
|
176 |
|
|
parser.add_option("-f", "--format", dest="format", default="text", type="string", help="Output format ('text', 'latex', 'twiki'")
|
177 |
|
|
parser.add_option("-g", "--histogram", dest="plotfile", default=None, type="string", help="If true, plot the pulls of the nuisances to the given file.")
|
178 |
|
|
parser.add_option("--sf", "--scale-factor", dest="plotsf", default=None, type="string", help="If true, plot the scale factors in a histograms.")
|
179 |
bortigno |
1.3 |
parser.add_option("-D", "--datacard", dest="dc", default="", help="Datacard to be plotted")
|
180 |
bortigno |
1.1 |
|
181 |
|
|
(options, args) = parser.parse_args()
|
182 |
|
|
if len(args) == 0:
|
183 |
|
|
parser.print_usage()
|
184 |
|
|
exit(1)
|
185 |
|
|
|
186 |
|
|
file = ROOT.TFile(args[0])
|
187 |
|
|
if file == None: raise RuntimeError, "Cannot open file %s" % args[0]
|
188 |
|
|
fit_s = file.Get("fit_s")
|
189 |
|
|
fit_b = file.Get("fit_b")
|
190 |
|
|
prefit = file.Get("nuisances_prefit")
|
191 |
|
|
if fit_s == None or fit_s.ClassName() != "RooFitResult": raise RuntimeError, "File %s does not contain the output of the signal fit 'fit_s'" % args[0]
|
192 |
|
|
if fit_b == None or fit_b.ClassName() != "RooFitResult": raise RuntimeError, "File %s does not contain the output of the background fit 'fit_b'" % args[0]
|
193 |
|
|
if prefit == None or prefit.ClassName() != "RooArgSet": raise RuntimeError, "File %s does not contain the prefit nuisances 'nuisances_prefit'" % args[0]
|
194 |
|
|
|
195 |
|
|
isFlagged = {}
|
196 |
|
|
table = {}
|
197 |
|
|
fpf_b = fit_b.floatParsFinal()
|
198 |
|
|
fpf_s = fit_s.floatParsFinal()
|
199 |
|
|
pulls = []
|
200 |
bortigno |
1.4 |
|
201 |
|
|
correlation_matrix_name=[]
|
202 |
|
|
correlation_matrix=[[]]
|
203 |
|
|
|
204 |
bortigno |
1.1 |
for i in range(fpf_s.getSize()):
|
205 |
|
|
nuis_s = fpf_s.at(i)
|
206 |
|
|
name = nuis_s.GetName();
|
207 |
|
|
nuis_b = fpf_b.find(name)
|
208 |
|
|
nuis_p = prefit.find(name)
|
209 |
bortigno |
1.4 |
if( name.find('SF') > 0. ):
|
210 |
|
|
correlation_matrix_name.append(name)
|
211 |
bortigno |
1.1 |
row = []
|
212 |
|
|
flag = False;
|
213 |
|
|
mean_p, sigma_p = 0,0
|
214 |
|
|
if nuis_p == None:
|
215 |
|
|
if not options.abs: continue
|
216 |
|
|
row += [ "[%.2f, %.2f]" % (nuis_s.getMin(), nuis_s.getMax()) ]
|
217 |
|
|
else:
|
218 |
|
|
mean_p, sigma_p = (nuis_p.getVal(), nuis_p.getError())
|
219 |
|
|
if options.abs: row += [ "%.2f +/- %.2f" % (nuis_p.getVal(), nuis_p.getError()) ]
|
220 |
|
|
for fit_name, nuis_x in [('b', nuis_b), ('s',nuis_s)]:
|
221 |
|
|
if nuis_x == None:
|
222 |
|
|
row += [ " n/a " ]
|
223 |
|
|
else:
|
224 |
|
|
row += [ "%+.2f +/- %.2f" % (nuis_x.getVal(), nuis_x.getError()) ]
|
225 |
|
|
if nuis_p != None:
|
226 |
|
|
valShift = (nuis_x.getVal() - mean_p)/sigma_p
|
227 |
|
|
if fit_name == 'b':
|
228 |
|
|
pulls.append(valShift)
|
229 |
|
|
sigShift = nuis_x.getError()/sigma_p
|
230 |
bortigno |
1.3 |
# print name
|
231 |
|
|
# print nuis_x.getError()
|
232 |
|
|
# print nuis_p.getVal()
|
233 |
|
|
# print sigShift
|
234 |
bortigno |
1.1 |
if options.abs:
|
235 |
|
|
row[-1] += " (%+4.2fsig, %4.2f)" % (valShift, sigShift)
|
236 |
|
|
else:
|
237 |
|
|
row[-1] = " %+4.2f, %4.2f" % (valShift, sigShift)
|
238 |
|
|
if (abs(valShift) > options.vtol2 or abs(sigShift-1) > options.stol2):
|
239 |
|
|
isFlagged[(name,fit_name)] = 2
|
240 |
|
|
flag = True
|
241 |
|
|
elif (abs(valShift) > options.vtol or abs(sigShift-1) > options.stol):
|
242 |
|
|
if options.all: isFlagged[(name,fit_name)] = 1
|
243 |
|
|
flag = True
|
244 |
|
|
elif options.all:
|
245 |
|
|
flag = True
|
246 |
|
|
row += [ "%+4.2f" % fit_s.correlation(name, options.poi) ]
|
247 |
|
|
if flag or options.all: table[name] = row
|
248 |
bortigno |
1.4 |
# filling correlation table
|
249 |
|
|
correlation_matrix_line=[]
|
250 |
|
|
for j in range(fpf_b.getSize()):
|
251 |
|
|
_nuis_b = fpf_b.at(j)
|
252 |
|
|
_name = _nuis_b.GetName();
|
253 |
|
|
if name.find('SF') > 0 and ( _name.find('SF') > 0 or options.all ) :
|
254 |
|
|
# print name + ' __CORR__ ' + _name
|
255 |
|
|
# print fit_b.correlation(name,_name)
|
256 |
|
|
# print _nuis_b.getError()
|
257 |
|
|
# print fit_b.correlation(name,_name)*_nuis_b.getError()
|
258 |
|
|
print name
|
259 |
|
|
print _name
|
260 |
|
|
print fit_b.correlation(name,_name)
|
261 |
|
|
correlation_matrix_line.append(trunc(fit_b.correlation(name,_name),2))
|
262 |
|
|
if len(correlation_matrix_line) > 0:
|
263 |
|
|
correlation_matrix.append(correlation_matrix_line)
|
264 |
|
|
|
265 |
|
|
print correlation_matrix_name
|
266 |
|
|
print correlation_matrix[1]
|
267 |
|
|
print correlation_matrix[2]
|
268 |
|
|
print correlation_matrix[3]
|
269 |
|
|
print correlation_matrix[4]
|
270 |
bortigno |
1.1 |
|
271 |
|
|
fmtstring = "%-40s %15s %15s %10s"
|
272 |
|
|
highlight = "*%s*"
|
273 |
|
|
morelight = "!%s!"
|
274 |
|
|
pmsub, sigsub = None, None
|
275 |
|
|
if options.format == 'text':
|
276 |
|
|
if options.abs:
|
277 |
|
|
fmtstring = "%-40s %15s %30s %30s %10s"
|
278 |
|
|
print fmtstring % ('name', 'pre fit', 'b-only fit', 's+b fit', 'rho')
|
279 |
|
|
else:
|
280 |
|
|
print fmtstring % ('name', 'b-only fit', 's+b fit', 'rho')
|
281 |
|
|
elif options.format == 'latex':
|
282 |
|
|
pmsub = (r"(\S+) \+/- (\S+)", r"$\1 \\pm \2$")
|
283 |
|
|
sigsub = ("sig", r"$\\sigma$")
|
284 |
|
|
highlight = "\\textbf{%s}"
|
285 |
|
|
morelight = "{{\\color{red}\\textbf{%s}}}"
|
286 |
|
|
if options.abs:
|
287 |
|
|
fmtstring = "%-40s & %15s & %30s & %30s & %6s \\\\"
|
288 |
|
|
print "\\begin{tabular}{|l|r|r|r|r|} \\hline ";
|
289 |
|
|
print (fmtstring % ('name', 'pre fit', '$b$-only fit', '$s+b$ fit', r'$\rho(\theta, \mu)$')), " \\hline"
|
290 |
|
|
else:
|
291 |
|
|
fmtstring = "%-40s & %15s & %15s & %6s \\\\"
|
292 |
|
|
print "\\begin{tabular}{|l|r|r|r|} \\hline ";
|
293 |
|
|
#what = r"$(x_\text{out} - x_\text{in})/\sigma_{\text{in}}$, $\sigma_{\text{out}}/\sigma_{\text{in}}$"
|
294 |
|
|
what = r"\Delta x/\sigma_{\text{in}}$, $\sigma_{\text{out}}/\sigma_{\text{in}}$"
|
295 |
|
|
print fmtstring % ('', '$b$-only fit', '$s+b$ fit', '')
|
296 |
|
|
print (fmtstring % ('name', what, what, r'$\rho(\theta, \mu)$')), " \\hline"
|
297 |
|
|
elif options.format == 'twiki':
|
298 |
|
|
pmsub = (r"(\S+) \+/- (\S+)", r"\1 ± \2")
|
299 |
|
|
sigsub = ("sig", r"σ")
|
300 |
|
|
highlight = "<b>%s</b>"
|
301 |
|
|
morelight = "<b style='color:red;'>%s</b>"
|
302 |
|
|
if options.abs:
|
303 |
|
|
fmtstring = "| <verbatim>%-40s</verbatim> | %-15s | %-30s | %-30s | %-15s |"
|
304 |
|
|
print "| *name* | *pre fit* | *b-only fit* | *s+b fit* | "
|
305 |
|
|
else:
|
306 |
|
|
fmtstring = "| <verbatim>%-40s</verbatim> | %-15s | %-15s | %-15s |"
|
307 |
|
|
print "| *name* | *b-only fit* | *s+b fit* | *corr.* |"
|
308 |
|
|
elif options.format == 'html':
|
309 |
|
|
pmsub = (r"(\S+) \+/- (\S+)", r"\1 ± \2")
|
310 |
|
|
sigsub = ("sig", r"σ")
|
311 |
|
|
highlight = "<b>%s</b>"
|
312 |
|
|
morelight = "<strong>%s</strong>"
|
313 |
|
|
print """
|
314 |
|
|
<html><head><title>Comparison of nuisances</title>
|
315 |
|
|
<style type="text/css">
|
316 |
|
|
td, th { border-bottom: 1px solid black; padding: 1px 1em; }
|
317 |
|
|
td { font-family: 'Consolas', 'Courier New', courier, monospace; }
|
318 |
|
|
strong { color: red; font-weight: bolder; }
|
319 |
|
|
</style>
|
320 |
|
|
</head><body style="font-family: 'Verdana', sans-serif; font-size: 10pt;"><h1>Comparison of nuisances</h1>
|
321 |
|
|
<table>
|
322 |
|
|
"""
|
323 |
|
|
if options.abs:
|
324 |
|
|
print "<tr><th>nuisance</th><th>pre fit</th><th>background fit </th><th>signal fit</th><th>correlation</th></tr>"
|
325 |
|
|
fmtstring = "<tr><td><tt>%-40s</tt> </td><td> %-15s </td><td> %-30s </td><td> %-30s </td><td> %-15s </td></tr>"
|
326 |
|
|
else:
|
327 |
|
|
what = "Δx/σ<sub>in</sub>, σ<sub>out</sub>/σ<sub>in</sub>";
|
328 |
|
|
print "<tr><th>nuisance</th><th>background fit<br/>%s </th><th>signal fit<br/>%s</th><th>ρ(μ, θ)</tr>" % (what,what)
|
329 |
|
|
fmtstring = "<tr><td><tt>%-40s</tt> </td><td> %-15s </td><td> %-15s </td><td> %-15s </td></tr>"
|
330 |
|
|
|
331 |
|
|
names = table.keys()
|
332 |
|
|
names.sort()
|
333 |
|
|
highlighters = { 1:highlight, 2:morelight };
|
334 |
|
|
for n in names:
|
335 |
|
|
v = table[n]
|
336 |
|
|
if options.format == "latex": n = n.replace(r"_", r"\_")
|
337 |
|
|
if pmsub != None: v = [ re.sub(pmsub[0], pmsub[1], i) for i in v ]
|
338 |
|
|
if sigsub != None: v = [ re.sub(sigsub[0], sigsub[1], i) for i in v ]
|
339 |
|
|
if (n,'b') in isFlagged: v[-3] = highlighters[isFlagged[(n,'b')]] % v[-3]
|
340 |
|
|
if (n,'s') in isFlagged: v[-2] = highlighters[isFlagged[(n,'s')]] % v[-2]
|
341 |
|
|
if options.abs:
|
342 |
|
|
print fmtstring % (n, v[0], v[1], v[2], v[3])
|
343 |
|
|
else:
|
344 |
|
|
print fmtstring % (n, v[0], v[1], v[2])
|
345 |
|
|
|
346 |
|
|
if options.format == "latex":
|
347 |
|
|
print " \\hline\n\end{tabular}"
|
348 |
|
|
elif options.format == "html":
|
349 |
|
|
print "</table></body></html>"
|
350 |
|
|
|
351 |
|
|
if options.plotfile:
|
352 |
|
|
import ROOT
|
353 |
|
|
ROOT.gROOT.SetStyle("Plain")
|
354 |
|
|
ROOT.gStyle.SetOptFit(1)
|
355 |
|
|
histogram = ROOT.TH1F("pulls", "Pulls", 60, -3, 3)
|
356 |
|
|
for pull in pulls:
|
357 |
|
|
histogram.Fill(pull)
|
358 |
|
|
canvas = ROOT.TCanvas("asdf", "asdf", 800, 800)
|
359 |
|
|
histogram.GetXaxis().SetTitle("pull")
|
360 |
|
|
histogram.SetTitle("Post-fit nuisance pull distribution")
|
361 |
|
|
histogram.SetMarkerStyle(20)
|
362 |
|
|
histogram.SetMarkerSize(2)
|
363 |
|
|
#histogram.Fit("gaus")
|
364 |
|
|
histogram.Draw("pe")
|
365 |
|
|
canvas.SaveAs(options.plotfile)
|
366 |
|
|
|
367 |
|
|
|
368 |
bortigno |
1.2 |
########### SCALE FACTOR PLOT ###############
|
369 |
|
|
## If you fit scale factors this part of ##
|
370 |
|
|
## will create a plt reading from the ##
|
371 |
|
|
## nuisances. All the nuisance cointaning ##
|
372 |
|
|
## "SF" in their name will be plotted ##
|
373 |
|
|
#############################################
|
374 |
|
|
|
375 |
bortigno |
1.1 |
import numpy
|
376 |
bortigno |
1.3 |
if options.plotsf and options.dc:
|
377 |
bortigno |
1.1 |
n=0
|
378 |
|
|
labels=[]
|
379 |
bortigno |
1.2 |
v_s=[] # dX/sigma_in values
|
380 |
|
|
v_b=[] # sigma_out/sigma_in values
|
381 |
|
|
rho=[] # rho
|
382 |
|
|
sys=[] # systematics. Not properly filled yet
|
383 |
|
|
x_position=[] # x position in the plot, basically the SF value
|
384 |
|
|
y_position=[] # y position in the plot. @TO IMPLEMENT: According to the number of entries per background the spacing is different.
|
385 |
bortigno |
1.3 |
#!! loop on all the nuisances
|
386 |
bortigno |
1.1 |
for name in names:
|
387 |
bortigno |
1.3 |
#!! take only the nuisances which have SF in their name
|
388 |
bortigno |
1.1 |
if ('SF' in name ):
|
389 |
|
|
print name
|
390 |
|
|
labels.append(name)
|
391 |
|
|
n+=1
|
392 |
bortigno |
1.3 |
#!! take the values
|
393 |
|
|
#!! the usual order is: dX/sigma_in for background only - sigma_out/sigma_in for background only - dX/sigma_in for background+signal - sigma_out/sigma_in for background+signal - rho
|
394 |
bortigno |
1.2 |
v = table[name]
|
395 |
bortigno |
1.3 |
#!! forget about the flag
|
396 |
bortigno |
1.1 |
v = [ re.sub('!','',i) for i in v ]
|
397 |
bortigno |
1.3 |
v = [ re.sub('\*','',i) for i in v ]
|
398 |
bortigno |
1.1 |
if pmsub != None: v = [ re.sub(pmsub[0], pmsub[1], i) for i in v ]
|
399 |
|
|
if sigsub != None: v = [ re.sub(sigsub[0], sigsub[1], i) for i in v ]
|
400 |
|
|
v_b.append(v[0].split(','))
|
401 |
|
|
v_s.append(v[1].split(','))
|
402 |
|
|
rho.append(v[2].split(','))
|
403 |
|
|
x_position.append(0.)
|
404 |
bortigno |
1.2 |
y_position.append(n-1.)
|
405 |
bortigno |
1.3 |
sys.append(0.1) #@TO FIX: need to decide how to quote the systematics and where to take them from
|
406 |
bortigno |
1.2 |
|
407 |
|
|
# count the number of different channels
|
408 |
bortigno |
1.3 |
channels = [0.,0.,0.,0.,0.]
|
409 |
bortigno |
1.4 |
ch={'Zll':0.,'Zll low Pt':0.,'Zll high Pt':0.,'Wln':0.,'Wln low Pt':0.,'Wln high Pt':0.,'Znn':0.,'Znn low Pt':0.,'Znn high Pt':0.,'Znn med Pt':0.,'Zee':0.,'Zmm':0.}
|
410 |
bortigno |
1.2 |
print labels
|
411 |
|
|
for label in labels:
|
412 |
bortigno |
1.3 |
#!! create channel list and labels for legend
|
413 |
bortigno |
1.4 |
if label.find('Zee') > 0. :
|
414 |
|
|
ch['Zee'] = 1.
|
415 |
|
|
if label.find('Zmm') > 0. :
|
416 |
|
|
ch['Zmm'] = 1.
|
417 |
bortigno |
1.3 |
if label.find('Zll') > 0. :
|
418 |
|
|
if label.find('lowPt') > 0.:
|
419 |
|
|
ch['Zll low Pt'] = 1.
|
420 |
|
|
elif label.find('highPt') > 0.:
|
421 |
|
|
ch['Zll high Pt'] = 1.
|
422 |
|
|
else:
|
423 |
|
|
ch['Zll'] = 1.
|
424 |
|
|
if label.find('Wln') > 0. :
|
425 |
|
|
if label.find('lowPt') > 0.:
|
426 |
|
|
ch['Wln low Pt'] = 1.
|
427 |
|
|
elif label.find('highPt') > 0.:
|
428 |
|
|
ch['Wln high Pt'] = 1.
|
429 |
|
|
else:
|
430 |
|
|
ch['Wln'] = 1.
|
431 |
|
|
if label.find('Znunu') > 0. :
|
432 |
|
|
if label.find('LowPt') > 0.:
|
433 |
|
|
ch['Znunu low Pt'] = 1.
|
434 |
|
|
elif label.find('MedPt') > 0.:
|
435 |
|
|
ch['Znunu med Pt'] = 1.
|
436 |
|
|
elif label.find('HighPt') > 0.:
|
437 |
|
|
ch['Znunu high Pt'] = 1.
|
438 |
|
|
else:
|
439 |
|
|
ch['Znunu'] = 1.
|
440 |
|
|
|
441 |
bortigno |
1.2 |
# calculate the shift on the y position
|
442 |
|
|
shift=0.
|
443 |
bortigno |
1.3 |
for channel,active in ch.iteritems(): shift+=active;
|
444 |
bortigno |
1.2 |
shift=1./(shift+1)
|
445 |
bortigno |
1.3 |
|
446 |
bortigno |
1.2 |
#shift the elements in the array
|
447 |
bortigno |
1.3 |
# for i in range(0,len(y_position)): y_position[i]+=shift
|
448 |
|
|
|
449 |
|
|
print 'Y_POSITION'
|
450 |
bortigno |
1.2 |
print y_position
|
451 |
bortigno |
1.3 |
# clean the labels
|
452 |
|
|
nuisances = labels
|
453 |
bortigno |
1.2 |
labels = [re.sub('CMS_vhbb_','',label) for label in labels ]
|
454 |
|
|
try:
|
455 |
bortigno |
1.3 |
labels = [re.sub('_Zll_SF_','',label) for label in labels ]
|
456 |
|
|
labels = [re.sub('_Wln_SF_','',label) for label in labels ]
|
457 |
bortigno |
1.4 |
labels = [re.sub('_Zee_SF_','',label) for label in labels ]
|
458 |
|
|
labels = [re.sub('_Zmm_SF_','',label) for label in labels ]
|
459 |
bortigno |
1.3 |
labels = [re.sub('_SF_Znunu','',label) for label in labels ]
|
460 |
|
|
labels = [re.sub('lowPt_8TeV','',label) for label in labels ]
|
461 |
|
|
labels = [re.sub('highPt_8TeV','',label) for label in labels ]
|
462 |
|
|
labels = [re.sub('medPt_8TeV','',label) for label in labels ]
|
463 |
|
|
labels = [re.sub('_Zll_lowPt_SF_8TeV','',label) for label in labels ]
|
464 |
|
|
labels = [re.sub('_Zll_highPt_SF_8TeV','',label) for label in labels ]
|
465 |
bortigno |
1.4 |
labels = [re.sub('_LowPt','',label) for label in labels ]
|
466 |
|
|
labels = [re.sub('_HighPt','',label) for label in labels ]
|
467 |
bortigno |
1.3 |
labels = [re.sub('LowPt_8TeV','',label) for label in labels ]
|
468 |
|
|
labels = [re.sub('MedPt_8TeV','',label) for label in labels ]
|
469 |
|
|
labels = [re.sub('HighPt_8TeV','',label) for label in labels ]
|
470 |
|
|
labels = [re.sub('8TeV','',label) for label in labels ]
|
471 |
|
|
labels = [re.sub('8TeV','',label) for label in labels ]
|
472 |
bortigno |
1.2 |
except:
|
473 |
|
|
print '@WARNING: No usual naming for datacard scale factors nuisances'
|
474 |
|
|
print labels
|
475 |
|
|
|
476 |
|
|
#CMS_vhbb_TT_Zll_SF_8TeV ! +0.56, 0.13! ! +0.54, 0.13! -0.00
|
477 |
|
|
#CMS_vhbb_ZjHF_Zll_SF_8TeV +1.79, 1.14 +1.80, 1.13 +0.00
|
478 |
|
|
#CMS_vhbb_ZjLF_Zll_SF_8TeV ! +3.51, 0.94! ! +3.42, 0.95! -0.00
|
479 |
|
|
|
480 |
bortigno |
1.1 |
print n
|
481 |
|
|
print labels
|
482 |
|
|
print v_s
|
483 |
|
|
print v_b
|
484 |
|
|
print rho
|
485 |
bortigno |
1.3 |
|
486 |
|
|
label_dictionary = {"TT":"t#bar{t}","ZjHF":"Z+bX","ZjLF":"Z+udscg","Zj1HF":"Z+b","Zj2HF":"Z+b#bar{b}","Wj0b":"W+light","Wj1b":"W+b","Wj2b":"W+b#bar{b}","Zj0b":"Z+light","Zj1b":"Z+b","Zj2b":"Z+b#bar{b}","s_Top":"t"}
|
487 |
bortigno |
1.1 |
c = ROOT.TCanvas("c","c",600,600)
|
488 |
|
|
|
489 |
bortigno |
1.3 |
input_sigma = getInputSigma(options)
|
490 |
|
|
print 'Input sigma'
|
491 |
|
|
print input_sigma
|
492 |
|
|
|
493 |
|
|
graphs={}
|
494 |
bortigno |
1.5 |
latex={}
|
495 |
bortigno |
1.3 |
j=1
|
496 |
|
|
for channel,active in ch.iteritems():
|
497 |
|
|
print channel
|
498 |
|
|
print active
|
499 |
|
|
if active > 0.:
|
500 |
|
|
graphs[channel] = getGraph(channel,labels,j*shift,v_b,input_sigma,x_position,y_position,nuisances) # create the graph with the scale factors
|
501 |
bortigno |
1.5 |
sf = get_scale_factors(channel,labels,shift,v_b,input_sigma,nuisances)[0]
|
502 |
|
|
sf_e = get_scale_factors(channel,labels,shift,v_b,input_sigma,nuisances)[1]
|
503 |
|
|
for i in range(0,len(sf)):
|
504 |
|
|
latex[labels[i]] = [labels[i],sf[i],sf_e[i],y_position[i]+0.35*shift]
|
505 |
|
|
print "%s %s pm %s" %(labels[i],sf[i],sf_e[i])
|
506 |
bortigno |
1.3 |
j+=1
|
507 |
|
|
|
508 |
|
|
print graphs
|
509 |
|
|
|
510 |
bortigno |
1.5 |
xmin = 0.25
|
511 |
|
|
xmax = 2.5
|
512 |
bortigno |
1.3 |
labels = removeDouble(labels)
|
513 |
|
|
n= len(labels)
|
514 |
bortigno |
1.5 |
h2 = ROOT.TH2F("h2","",1,xmin,xmax,n,0,n) # x min - max values.
|
515 |
bortigno |
1.1 |
h2.GetXaxis().SetTitle("Scale factor")
|
516 |
|
|
|
517 |
|
|
for i in range(n):
|
518 |
bortigno |
1.2 |
h2.GetYaxis().SetBinLabel(i+1,label_dictionary[labels[i]])
|
519 |
bortigno |
1.1 |
|
520 |
bortigno |
1.3 |
|
521 |
|
|
drawSys=False
|
522 |
|
|
if(drawSys):
|
523 |
|
|
#for the moment just random systematics
|
524 |
|
|
sys_e = numpy.array(sys)
|
525 |
|
|
#!! Create the graph for the systematics, if any. It will show only error brackets, no points
|
526 |
|
|
g2 = ROOT.TGraphErrors(n,d,p,sys_e,zero)
|
527 |
|
|
g2.SetFillColor(0)
|
528 |
|
|
g2.SetLineWidth(3);
|
529 |
bortigno |
1.1 |
|
530 |
|
|
h2.Draw(); ROOT.gStyle.SetOptStat(0);
|
531 |
|
|
h2.GetXaxis().SetTitleSize(0.04);
|
532 |
|
|
h2.GetXaxis().SetLabelSize(0.04);
|
533 |
|
|
h2.GetYaxis().SetLabelSize(0.06);
|
534 |
|
|
#h2.SetFillStyle(4000)
|
535 |
|
|
c.SetFillStyle(4000)
|
536 |
|
|
|
537 |
|
|
globalFitBand = ROOT.TBox(1.0, 0., 1.5, n);
|
538 |
|
|
globalFitBand.SetFillStyle(3013);
|
539 |
|
|
globalFitBand.SetFillColor(65);
|
540 |
|
|
globalFitBand.SetLineStyle(0);
|
541 |
|
|
#globalFitBand.Draw("same");
|
542 |
bortigno |
1.2 |
globalFitLine = ROOT.TLine(1.0, 0., 1.0, n);
|
543 |
bortigno |
1.1 |
globalFitLine.SetLineWidth(2);
|
544 |
|
|
globalFitLine.SetLineColor(214);#214
|
545 |
|
|
globalFitLine.Draw("same");
|
546 |
bortigno |
1.3 |
|
547 |
|
|
#!! Legend
|
548 |
bortigno |
1.5 |
l2 = ROOT.TLegend(0.68, 0.80,0.80,0.85)
|
549 |
bortigno |
1.1 |
l2.SetLineWidth(2)
|
550 |
|
|
l2.SetBorderSize(0)
|
551 |
|
|
l2.SetFillColor(0)
|
552 |
|
|
l2.SetFillStyle(4000)
|
553 |
|
|
l2.SetTextFont(62)
|
554 |
bortigno |
1.3 |
for channel,g in graphs.iteritems():
|
555 |
|
|
print channel
|
556 |
bortigno |
1.5 |
l2.AddEntry(g,'ZH, Z#rightarrowl^{+}l^{-}',"pl")
|
557 |
|
|
# l2.AddEntry(g,channel,"pl")
|
558 |
bortigno |
1.3 |
#l2.AddEntry(g,"Stat.","l")
|
559 |
|
|
if(drawSys) : l2.AddEntry(g2,"Syst.","l")
|
560 |
bortigno |
1.1 |
l2.SetTextSize(0.035)
|
561 |
bortigno |
1.3 |
# l2.SetNColumns(3)
|
562 |
bortigno |
1.1 |
l2.Draw("same")
|
563 |
bortigno |
1.4 |
for channel,g in graphs.iteritems():
|
564 |
|
|
print channel
|
565 |
|
|
g.Draw("P same")
|
566 |
bortigno |
1.5 |
for label in labels:
|
567 |
|
|
StackMaker.myText("%.2f #pm %.2f" %(latex[label][1],latex[label][2]),conversion_x(xmin)-0.02,conversion_y(latex[label][3]),0.5)
|
568 |
bortigno |
1.3 |
if(drawSys) : g2.Draw("[] same")
|
569 |
bortigno |
1.5 |
StackMaker.myText("CMS Preliminary",conversion_x(xmin)+0.1,0.95,0.6)
|
570 |
|
|
StackMaker.myText("#sqrt{s} = 8TeV, L = 19.0 fb^{-1}",conversion_x(xmin)+0.1,0.92,0.6)
|
571 |
|
|
|
572 |
bortigno |
1.1 |
ROOT.gPad.SetLeftMargin(0.2)
|
573 |
|
|
ROOT.gPad.Update()
|
574 |
|
|
c.Print("histo.pdf")
|
575 |
|
|
|
576 |
bortigno |
1.3 |
|
577 |
|
|
|
578 |
|
|
|