File indexing completed on 2024-11-26 02:34:39
0001
0002
0003 from builtins import range
0004 import FWCore.ParameterSet.Config as cms
0005 import sys
0006 import os
0007 import math
0008 import re
0009 import Validation.RecoTau.RecoTauValidation_cfi as validation
0010 from optparse import OptionParser
0011 from ROOT import gStyle, TDirectory, TDirectoryFile, TLatex, TH1F, TLegend, TPaveText, TPad
0012
0013 __author__ = "Mauro Verzetti (mauro.verzetti@cern.ch) and Lucia Perrini (lucia.perrini@cern.ch)"
0014 __doc__ = """Script to plot the content of a Validation .root file and compare it to a different file:\n\n
0015 Usage: MultipleCompare.py -T testFile -R refFile [options] [search strings that you want to apply '*' is supported as special character]"""
0016
0017 def LoadCommandlineOptions(argv):
0018 sys.argv = argv
0019 parser = OptionParser(description=__doc__)
0020 parser.add_option('--myhelp',metavar='', action="store_true",help='prints this output message',dest='help',default = False)
0021 parser.add_option('--TestFile','-T',metavar='testFile', type=str,help='Sets the test file',dest='test',default = '')
0022 parser.add_option('--RefFile','-R',metavar='refFile', type=str,help='Sets the reference file',dest='ref',default = None)
0023 parser.add_option('--output','-o',metavar='outputFile', type=str,help='Sets the output file',dest='out',default = 'MultipleCompare.png')
0024 parser.add_option('--logScaleY',action="store_true", dest="logScaleY", default=False, help="Sets the log scale in the plot (Y axis)")
0025 parser.add_option('--logScaleX',action="store_true", dest="logScaleX", default=False, help="Sets the log scale in the plot (X axis)")
0026 parser.add_option('--fakeRate','-f',action="store_true", dest="fakeRate", default=False, help="Sets the fake rate options and put the correct label (implies --logScale)")
0027 parser.add_option('--testLabel','-t',metavar='testLabel', type=str,help='Sets the label to put in the plots for test file',dest='testLabel',default = None)
0028 parser.add_option('--refLabel','-r',metavar='refLabel', type=str,help='Sets the label to put in the plots for ref file',dest='refLabel',default = None)
0029 parser.add_option('--sampleLabel','-s',metavar='sampleLabel', type=str,help='Sets the label to indicate the sample used',dest='sampleLabel',default = None)
0030 parser.add_option('--maxLogX',metavar='number', type=float,help='Sets the maximum of the scale in log scale both in the main and in the sub pad (requires --logScale or -f to work)',dest='maxLogX',default = 100)
0031 parser.add_option('--minLogX',metavar='number', type=float,help='Sets the minimum of the scale in log scale (requires --logScale or -f to work)',dest='minLogX',default = 0.001)
0032 parser.add_option('--minLogY',metavar='number', type=float,help='Sets the minimum of the scale in log scale (requires --logScale or -f to work)',dest='minLogY',default = 0.0001)
0033 parser.add_option('--maxLogY',metavar='number', type=float,help='Sets the maximum of the scale in log scale (requires --logScale or -f to work)',dest='maxLogY',default = 3)
0034 parser.add_option('--minYR',metavar='number', type=float,help='Sets the minimum of the scale in sub pad',dest='minYR',default = 0)
0035 parser.add_option('--maxYR',metavar='number', type=float,help='Sets the maximum of the scale in sub pad',dest='maxYR',default = 1.2)
0036
0037
0038
0039
0040 parser.add_option('--logDiv',action="store_true", dest="logDiv", default=False, help="Sets the log scale in the plot")
0041 parser.add_option('--normalize',action="store_true", dest="normalize", default=False, help="plot normalized")
0042 parser.add_option('--maxRange',metavar='number',type=float, dest="maxRange", default=1.6, help="Sets the maximum range in linear plots")
0043 parser.add_option('--maxXaxis',metavar='number',type=float, dest="maxXaxis", default=800, help="Sets the maximum range on x axis in the main pad")
0044 parser.add_option('--minXaxis',metavar='number',type=float,help="Sets the minimum range on x axis in the main pad",dest="minXaxis", default=-3)
0045 parser.add_option('--maxYaxis',metavar='number',type=float, dest="maxYaxis", default=2, help="Sets the maximum range on Y axis in the main pad")
0046 parser.add_option('--minYaxis',metavar='number',type=float, dest="minYaxis", default=0, help="Sets the minimum range on Y axis in the main pad")
0047 parser.add_option('--rebin', dest="rebin", type=int, default=-1, help="Sets the rebinning scale")
0048 parser.add_option('--branding','-b',metavar='branding', type=str,help='Define a branding to label the plots (in the top right corner)',dest='branding',default = None)
0049
0050
0051 (options,toPlot) = parser.parse_args()
0052 if options.help:
0053 parser.print_help()
0054 sys.exit(0)
0055 return [options, toPlot]
0056
0057 def GetContent(dir):
0058 tempList = dir.GetListOfKeys()
0059 retList = []
0060 for it in range(0,tempList.GetSize()):
0061 retList.append(tempList.At(it).ReadObj())
0062 return retList
0063
0064 def MapDirStructure( directory, dirName, objectList ):
0065 dirContent = GetContent(directory)
0066 for entry in dirContent:
0067 if isinstance(entry, TDirectory) or isinstance(entry, TDirectoryFile):
0068 subdirName = os.path.join(dirName,entry.GetName())
0069 MapDirStructure(entry, subdirName,objectList)
0070 else:
0071 pathname = os.path.join(dirName,entry.GetName())
0072 objectList.append(pathname)
0073
0074 def Match(required, got):
0075 for part in required.split('*'):
0076 if got.find(part) == -1:
0077 return False
0078 return True
0079
0080 def Divide(hNum,hDen):
0081 ret = hNum.Clone('Division')
0082 ret.GetYaxis().SetTitle('Ratio')
0083 for binI in range(hNum.GetNbinsX()+1):
0084 denVal = hDen.GetBinContent(binI)
0085 denErr = hDen.GetBinError(binI)
0086 numErr = hNum.GetBinError(binI)
0087 numVal = hNum.GetBinContent(binI)
0088 if denVal == 0:
0089 ret.SetBinContent(binI,0)
0090 ret.SetBinError(binI,0)
0091 else:
0092 ret.SetBinContent(binI,numVal/denVal)
0093 if numVal==0:
0094 ret.SetBinError(binI,1)
0095 else:
0096 ret.SetBinError(binI,(numVal/denVal)*math.sqrt(math.pow(numErr/numVal,2) + math.pow(denErr/denVal,2) ) )
0097 return ret
0098
0099 def DetermineHistType(name):
0100
0101 type = ''
0102 label = ''
0103 prefix = ''
0104
0105 matches = re.match(r'.*/(.*)_(.*)_(.*)', name)
0106 if matches:
0107 prefix = matches.group(1)
0108 label = matches.group(3)
0109 knowntypes = (['pTRatio','SumPt','Size'])
0110 for knowntype in knowntypes:
0111 if matches.group(2) == knowntype:
0112 type = knowntype
0113 if not type:
0114 type = 'Eff'
0115 else:
0116 type = 'Eff'
0117
0118 prefixParts = prefix.partition('Discrimination')
0119 if prefixParts[2] != '':
0120 prefix = prefixParts[2]
0121 prefixParts = prefix.partition('By')
0122 if prefixParts[2] != '':
0123 prefix = prefixParts[2]
0124
0125
0126 return [type, label, prefix]
0127
0128 def DrawTitle(text):
0129 title = TLatex()
0130 title.SetNDC()
0131 title.SetTextAlign(12)
0132 title.SetTextSize(.035)
0133 leftMargin = gStyle.GetPadLeftMargin()
0134 topMargin = 1 - 0.5*gStyle.GetPadTopMargin()
0135 title.DrawLatex(leftMargin, topMargin, text)
0136
0137 def DrawBranding(options, label=''):
0138 if options.branding != None or label != '':
0139 text = TLatex()
0140 text.SetNDC();
0141 text.SetTextAlign(11)
0142 text.SetTextSize(.025)
0143 text.SetTextColor(13)
0144 if options.out.find(".eps")!=-1:
0145 text.SetTextAngle(-91.0)
0146 else:
0147 text.SetTextAngle(-90.0)
0148 rightMargin = 1 - gStyle.GetPadRightMargin()
0149 topMargin = 1 - gStyle.GetPadTopMargin()
0150 if label!='':
0151 label += ': '
0152 text.DrawLatex(rightMargin+.01, topMargin+0.025, label+options.branding);
0153
0154
0155 def FindParents(histoPath):
0156 root = histoPath[:histoPath.find('_')]
0157 par = histoPath[histoPath.find('Eff')+3:]
0158 validationPlots = validation.proc.efficiencies.plots._Parameterizable__parameterNames
0159 found =0
0160 num = ''
0161 den = ''
0162 for efficiency in validationPlots:
0163 effpset = getattr(validation.proc.efficiencies.plots,efficiency)
0164 effName = effpset.efficiency.value()
0165 effNameCut = effName[effName.find('_'):effName.find('#')]
0166 if effNameCut in histoPath:
0167 if found == 1:
0168 print('More than one pair of parents found for ' + histopath + ':')
0169 assert(False)
0170 num = root + effpset.numerator.value()[effName.find('_'):].replace('#PAR#',par)
0171 den = root + effpset.denominator.value()[effName.find('_'):].replace('#PAR#',par)
0172 found += 1
0173 return [num,den]
0174
0175 def Rebin(tfile, histoPath, rebinVal):
0176 parents = FindParents(histoPath)
0177 num = tfile.Get(parents[0])
0178 if not isinstance(num, TH1F):
0179 print('Looking for ' + num)
0180 print('Plot now found! What the hell are you doing? Exiting...')
0181 sys.exit()
0182 denSingle = tfile.Get(parents[1])
0183 if not isinstance(denSingle, TH1F):
0184 print('Looking for '+denSingle)
0185 print('Plot now found! What the hell are you doing? Exiting...')
0186 sys.exit()
0187 num.Rebin(rebinVal)
0188 den = denSingle.Rebin(rebinVal,'denClone')
0189 retVal = num.Clone(histoPath+'Rebin%s'%rebinVal)
0190
0191
0192
0193 retVal.Divide(num,den,1,1,'B')
0194 return retVal
0195
0196 def findRange(hists, min0=-1, max0=-1):
0197 if len(hists) < 1:
0198 return
0199
0200 min = min0
0201 max = max0
0202 if min0 == -1 or max0 == -1:
0203 for hist in hists:
0204 if min0 == -1:
0205
0206 minTmp = getMinimumIncludingErrors(hist)
0207 if minTmp < min or min == -1:
0208 min = minTmp
0209 if max0 == -1:
0210 maxTmp = getMaximumIncludingErrors(hist)
0211 if maxTmp > max or max == -1:
0212 max = maxTmp
0213 return [min, max]
0214
0215 def optimizeRangeMainPad(argv, pad, hists, maxLogX_, minX_, maxX_, maxLogY_, minY_, maxY_):
0216 pad.Update()
0217 if pad.GetLogy():
0218 if maxLogY_ > 0:
0219 maxLogY = maxLogY_
0220 else:
0221 maxLogY = -1
0222 minY, maxY = findRange(hists, -1, maxLogY)
0223 else:
0224 minY, maxY = findRange(hists, minY_, maxY_)
0225
0226 if pad.GetLogy():
0227 if minY == 0:
0228 minY = 0.001
0229 else:
0230 if minY < 0.7:
0231 minY = minY
0232 if maxY <= 1.1 and maxY > 0.7:
0233 maxY = 1.2
0234 hists[0].SetAxisRange(minY, maxY, "Y")
0235
0236 if pad.GetLogx():
0237 if maxLogX_ > 0:
0238 maxLogX = maxLogX_
0239 else:
0240 maxLogX = -1
0241 minX, maxX = findRange(hists, -1, maxLogX)
0242 else:
0243 minX, maxX = findRange(hists, minX_, maxX_)
0244
0245 if pad.GetLogx():
0246 if minX == 0:
0247 minX = 0.001
0248 else:
0249 if minX < 0.7:
0250 minX = minX
0251 if maxX <= 1.1 and maxX > 0.7:
0252 maxX = 1.2
0253 hists[0].SetAxisRange(minX, maxX, "X")
0254
0255 def optimizeRangeSubPad(argv, pad, hists, maxLogX_, minX_, maxX_, minYRatio_, maxYRatio_):
0256 pad.Update()
0257 if pad.GetLogx():
0258 if maxLogX_ > 0:
0259 maxLogX = maxLogX_
0260 else:
0261 maxLogX = -1
0262 minX, maxX = findRange(hists, -1, maxLogX)
0263 else:
0264 minX, maxX = findRange(hists, minX_, maxX_)
0265 if pad.GetLogx():
0266 if minX == 0:
0267 minX = 0.001
0268 else:
0269 if minX < 0.7:
0270 minX = minX
0271 if maxX <= 1.1 and maxX > 0.7:
0272 maxX = 1.2
0273 hists[0].SetAxisRange(minX, maxX, "X")
0274
0275 min = -1
0276 max = -1
0277 if minYRatio_ > 0:
0278 min = minYRatio_
0279 if maxYRatio_ > 0:
0280 max = maxYRatio_
0281 min, max = findRange(hists, min, max)
0282 if max > 2:
0283 max = 2
0284 hists[0].SetAxisRange(min, max, "Y")
0285
0286 def getMaximumIncludingErrors(hist):
0287
0288 distance = 1.
0289 max = -1
0290 pos = 0
0291 for i in range(1, hist.GetNbinsX()):
0292 if hist.GetBinContent(i) > max:
0293 max = hist.GetBinContent(i)
0294 pos = i
0295 return max + distance*hist.GetBinError(pos)
0296
0297 def getMinimumIncludingErrors(hist):
0298
0299
0300 distance = 1.
0301 min = -1
0302 pos = 0
0303 for i in range(1, hist.GetNbinsX()):
0304 if hist.GetBinContent(i)<=0.:
0305 continue
0306 if hist.GetBinContent(i) < min or min==-1:
0307 min = hist.GetBinContent(i)
0308 pos = i
0309 if min < 0:
0310 min = 0
0311 return min - distance*hist.GetBinError(pos)
0312
0313
0314 def main(argv=None):
0315 if argv is None:
0316 argv = sys.argv
0317
0318 options, toPlot = LoadCommandlineOptions(argv)
0319
0320 gROOT.SetStyle('Plain')
0321 gROOT.SetBatch()
0322 gStyle.SetPalette(1)
0323 gStyle.SetOptStat(0)
0324 gStyle.SetPadGridX(True)
0325 gStyle.SetPadGridY(True)
0326 gStyle.SetOptTitle(0)
0327 gStyle.SetPadTopMargin(0.1)
0328 gStyle.SetPadBottomMargin(0.1)
0329 gStyle.SetPadLeftMargin(0.13)
0330 gStyle.SetPadRightMargin(0.07)
0331
0332
0333 testFile = TFile(options.test)
0334 refFile = None
0335 if options.ref != None:
0336 refFile = TFile(options.ref)
0337
0338
0339 plotList = []
0340 MapDirStructure( testFile,'',plotList)
0341
0342 histoList = []
0343 for plot in toPlot:
0344 for path in plotList:
0345 if Match(plot.lower(),path.lower()):
0346 histoList.append(path)
0347
0348
0349
0350 print(histoList)
0351
0352 if len(histoList)<1:
0353 print('\tError: Please specify at least one histogram.')
0354 if len(toPlot)>0:
0355 print('Check your plot list:', toPlot)
0356 sys.exit()
0357
0358
0359
0360 histType, label, prefix = DetermineHistType(histoList[0])
0361
0362
0363 scaleToIntegral = False
0364 if options.normalize:
0365 scaleToIntegral = True
0366
0367 ylabel = 'Efficiency'
0368
0369 if options.fakeRate:
0370 ylabel = 'Fake rate'
0371
0372 drawStats = False
0373 if histType=='pTRatio' and len(histoList)<3:
0374 drawStats = True
0375
0376
0377 x1 = 0.33
0378 x2 = 1-gStyle.GetPadRightMargin()
0379 y2 = 1-gStyle.GetPadTopMargin()
0380 lineHeight = .055
0381 if len(histoList) == 1:
0382 lineHeight = .05
0383 y1 = y2 - lineHeight*len(histoList)
0384 legend = TLegend(x1,y1,x2,y2)
0385 legend.SetHeader(label)
0386 legend.SetFillColor(0)
0387 legend.SetTextSize(0.032)
0388 if drawStats:
0389 y2 = y1
0390 y1 = y2 - .07*len(histoList)
0391 statsBox = TPaveText(x1,y1,x2,y2,"NDC")
0392 statsBox.SetFillColor(0)
0393 statsBox.SetTextAlign(12)
0394 statsBox.SetMargin(0.05)
0395 statsBox.SetBorderSize(1)
0396
0397
0398 canvas = TCanvas('MultiPlot','MultiPlot',validation.standardDrawingStuff.canvasSizeX.value(),832)
0399 effPad = TPad('effPad','effPad',0.01,0.35,0.99,0.99)
0400 effPad.SetBottomMargin(0.0)
0401
0402
0403
0404 effPad.Draw()
0405 header = ''
0406 if options.sampleLabel != None:
0407 header += 'Sample: '+options.sampleLabel
0408 if options.testLabel != None:
0409 header += ' Dots: '+options.testLabel
0410 if options.refLabel != None:
0411 header += ' Line: '+options.refLabel
0412 DrawTitle(header)
0413 DrawBranding(options)
0414 diffPad = TPad('diffPad','diffPad',0.01,0.01,0.99,0.32)
0415 diffPad.SetTopMargin(0.00);
0416 diffPad.SetBottomMargin(0.30);
0417 diffPad.Draw()
0418 colors = [2,3,4,6,5,7,28,1,2,3,4,6,5,7,28,1,2,3,4,6,5,7,28,1,2,3,4,6,5,7,28,1,2,3,4,6,5,7,28,1]
0419 first = True
0420 divHistos = []
0421 statTemplate = '%s Mean: %.3f RMS: %.3f'
0422 testHs = []
0423 refHs = []
0424 for histoPath,color in zip(histoList,colors):
0425 if(options.rebin == -1):
0426 testH = testFile.Get(histoPath)
0427 else:
0428 testH = Rebin(testFile,histoPath,options.rebin)
0429 if not isinstance(testH, TH1F):
0430 print('Looking for '+histoPath)
0431 print('Test plot now found! What the hell are you doing? Exiting...')
0432 sys.exit()
0433 testHs.append(testH)
0434 xAx = histoPath[histoPath.find('Eff')+len('Eff'):]
0435 effPad.cd()
0436 if not testH.GetXaxis().GetTitle():
0437 if hasattr(validation.standardDrawingStuff.xAxes,xAx):
0438 testH.GetXaxis().SetTitle( getattr(validation.standardDrawingStuff.xAxes,xAx).xAxisTitle.value())
0439 if not testH.GetYaxis().GetTitle():
0440 testH.GetYaxis().SetTitle(ylabel)
0441 if label!='':
0442 testH.GetXaxis().SetTitle(label+': '+testH.GetXaxis().GetTitle())
0443 testH.GetXaxis().SetTitleOffset(1.1)
0444 testH.GetXaxis().SetRangeUser(options.minXaxis,options.maxXaxis)
0445 testH.GetYaxis().SetTitleOffset(1.1)
0446
0447
0448 testH.SetMarkerSize(1)
0449 testH.SetMarkerStyle(20)
0450 testH.SetMarkerColor(color)
0451 if histType == 'Eff':
0452 legend.AddEntry(testH,histoPath[histoPath.rfind('/')+1:histoPath.find(histType)],'p')
0453 else:
0454 legend.AddEntry(testH,DetermineHistType(histoPath)[2],'p')
0455 if drawStats:
0456 text = statsBox.AddText(statTemplate % ('Dots',testH.GetMean(), testH.GetRMS()) )
0457 text.SetTextColor(color)
0458 if first:
0459 first = False
0460 if options.logScaleY:
0461 effPad.SetLogy()
0462 if options.logScaleX:
0463 effPad.SetLogx()
0464 diffPad.SetLogx()
0465 if scaleToIntegral:
0466 if testH.GetEntries() > 0:
0467 if not testH.GetSumw2N():
0468 testH.Sumw2()
0469 testH.DrawNormalized('ex0 P')
0470 else:
0471 print("--> Warning! You tried to normalize a histogram which seems to be already scaled properly. Draw it unscaled.")
0472 scaleToIntegral = False
0473 testH.Draw('ex0')
0474 else:
0475 testH.Draw('ex0')
0476 else:
0477 if scaleToIntegral:
0478 if testH.GetEntries() > 0:
0479 testH.DrawNormalized('same p')
0480 else:
0481 testH.Draw('same ex0 l')
0482 if refFile == None:
0483 continue
0484 if(options.rebin == -1):
0485 refH = refFile.Get(histoPath)
0486 else:
0487 refH = Rebin(refFile,histoPath,options.rebin)
0488 if not isinstance(refH, TH1F):
0489 continue
0490 refHs.append(refH)
0491 refH.SetLineColor(color)
0492 refH.SetLineWidth(1)
0493 if scaleToIntegral:
0494 if testH.GetEntries() > 0:
0495 refH.DrawNormalized('same hist')
0496 else:
0497 refH.DrawCopy('same hist')
0498 if drawStats:
0499 text = statsBox.AddText(statTemplate % ('Line',refH.GetMean(), refH.GetRMS()) )
0500 text.SetTextColor(color)
0501
0502
0503 if scaleToIntegral:
0504 entries = testH.GetEntries()
0505 if entries > 0:
0506 testH.Scale(1./entries)
0507 entries = refH.GetEntries()
0508 refH.Sumw2()
0509 if entries > 0:
0510 refH.Scale(1./entries)
0511 refH.Draw('same hist')
0512 divHistos.append(Divide(testH,refH))
0513
0514 if options.maxLogY > 0:
0515 maxlY=options.maxLogY
0516 if options.maxLogX > 0:
0517 maxlX=options.maxLogX
0518
0519 tmpHists = []
0520 tmpHists.extend(testHs)
0521 tmpHists.extend(refHs)
0522 optimizeRangeMainPad(argv, effPad, tmpHists, maxlX, options.minXaxis, options.maxXaxis, maxlY, options.minYaxis, options.maxYaxis)
0523
0524 firstD = True
0525 if refFile != None:
0526 for histo,color in zip(divHistos,colors):
0527 diffPad.cd()
0528 histo.SetMarkerSize(1)
0529 histo.SetMarkerStyle(20)
0530 histo.SetMarkerColor(color)
0531 histo.GetYaxis().SetLabelSize(0.07)
0532 histo.GetYaxis().SetTitleOffset(0.75)
0533 histo.GetYaxis().SetTitleSize(0.08)
0534 histo.GetXaxis().SetLabelSize(0.08)
0535 histo.GetXaxis().SetTitleSize(0.08)
0536
0537
0538
0539 if firstD:
0540 histo.Draw('ex0')
0541 firstD = False
0542 else:
0543 histo.Draw('same ex0')
0544 diffPad.Update()
0545
0546 if options.maxLogX > 0:
0547 maxlX=options.maxLogX
0548 optimizeRangeSubPad(argv, diffPad, divHistos, maxlX, options.minXaxis, options.maxXaxis, options.minYR, options.maxYR)
0549
0550 effPad.cd()
0551 legend.Draw()
0552
0553 if drawStats:
0554 statsBox.Draw()
0555
0556 canvas.Print(options.out)
0557
0558
0559 if __name__ == '__main__':
0560 sys.exit(main())