00001
00002
00003 import FWCore.ParameterSet.Config as cms
00004 import sys
00005 import os
00006 import math
00007 import re
00008 import Validation.RecoTau.RecoTauValidation_cfi as validation
00009 from optparse import OptionParser
00010 from ROOT import *
00011
00012 __author__ = "Mauro Verzetti (mauro.verzetti@cern.ch) and Lucia Perrini (lucia.perrini@cern.ch)"
00013 __doc__ = """Script to plot the content of a Validation .root file and compare it to a different file:\n\n
00014 Usage: MultipleCompare.py -T testFile -R refFile [options] [search strings that you want to apply '*' is supported as special character]"""
00015
00016 def LoadCommandlineOptions(argv):
00017 sys.argv = argv
00018 parser = OptionParser(description=__doc__)
00019 parser.add_option('--myhelp',metavar='', action="store_true",help='prints this output message',dest='help',default = False)
00020 parser.add_option('--TestFile','-T',metavar='testFile', type=str,help='Sets the test file',dest='test',default = '')
00021 parser.add_option('--RefFile','-R',metavar='refFile', type=str,help='Sets the reference file',dest='ref',default = None)
00022 parser.add_option('--output','-o',metavar='outputFile', type=str,help='Sets the output file',dest='out',default = 'MultipleCompare.png')
00023 parser.add_option('--logScaleY',action="store_true", dest="logScaleY", default=False, help="Sets the log scale in the plot (Y axis)")
00024 parser.add_option('--logScaleX',action="store_true", dest="logScaleX", default=False, help="Sets the log scale in the plot (X axis)")
00025 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)")
00026 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)
00027 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)
00028 parser.add_option('--sampleLabel','-s',metavar='sampleLabel', type=str,help='Sets the label to indicate the sample used',dest='sampleLabel',default = None)
00029 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)
00030 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)
00031 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)
00032 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)
00033 parser.add_option('--minYR',metavar='number', type=float,help='Sets the minimum of the scale in sub pad',dest='minYR',default = 0)
00034 parser.add_option('--maxYR',metavar='number', type=float,help='Sets the maximum of the scale in sub pad',dest='maxYR',default = 1.2)
00035
00036
00037
00038
00039 parser.add_option('--logDiv',action="store_true", dest="logDiv", default=False, help="Sets the log scale in the plot")
00040 parser.add_option('--normalize',action="store_true", dest="normalize", default=False, help="plot normalized")
00041 parser.add_option('--maxRange',metavar='number',type=float, dest="maxRange", default=1.6, help="Sets the maximum range in linear plots")
00042 parser.add_option('--maxXaxis',metavar='number',type=float, dest="maxXaxis", default=800, help="Sets the maximum range on x axis in the main pad")
00043 parser.add_option('--minXaxis',metavar='number',type=float,help="Sets the minimum range on x axis in the main pad",dest="minXaxis", default=-3)
00044 parser.add_option('--maxYaxis',metavar='number',type=float, dest="maxYaxis", default=2, help="Sets the maximum range on Y axis in the main pad")
00045 parser.add_option('--minYaxis',metavar='number',type=float, dest="minYaxis", default=0, help="Sets the minimum range on Y axis in the main pad")
00046 parser.add_option('--rebin', dest="rebin", type=int, default=-1, help="Sets the rebinning scale")
00047 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)
00048
00049
00050 (options,toPlot) = parser.parse_args()
00051 if options.help:
00052 parser.print_help()
00053 sys.exit(0)
00054 return [options, toPlot]
00055
00056 def GetContent(dir):
00057 tempList = dir.GetListOfKeys()
00058 retList = []
00059 for it in range(0,tempList.GetSize()):
00060 retList.append(tempList.At(it).ReadObj())
00061 return retList
00062
00063 def MapDirStructure( directory, dirName, objectList ):
00064 dirContent = GetContent(directory)
00065 for entry in dirContent:
00066 if type(entry) is TDirectory or type(entry) is TDirectoryFile:
00067 subdirName = os.path.join(dirName,entry.GetName())
00068 MapDirStructure(entry, subdirName,objectList)
00069 else:
00070 pathname = os.path.join(dirName,entry.GetName())
00071 objectList.append(pathname)
00072
00073 def Match(required, got):
00074 for part in required.split('*'):
00075 if got.find(part) == -1:
00076 return False
00077 return True
00078
00079 def Divide(hNum,hDen):
00080 ret = hNum.Clone('Division')
00081 ret.GetYaxis().SetTitle('Ratio')
00082 for binI in range(hNum.GetNbinsX()+1):
00083 denVal = hDen.GetBinContent(binI)
00084 denErr = hDen.GetBinError(binI)
00085 numErr = hNum.GetBinError(binI)
00086 numVal = hNum.GetBinContent(binI)
00087 if denVal == 0:
00088 ret.SetBinContent(binI,0)
00089 ret.SetBinError(binI,0)
00090 else:
00091 ret.SetBinContent(binI,numVal/denVal)
00092 if numVal==0:
00093 ret.SetBinError(binI,1)
00094 else:
00095 ret.SetBinError(binI,(numVal/denVal)*math.sqrt(math.pow(numErr/numVal,2) + math.pow(denErr/denVal,2) ) )
00096 return ret
00097
00098 def DetermineHistType(name):
00099
00100 type = ''
00101 label = ''
00102 prefix = ''
00103
00104 matches = re.match(r'.*/(.*)_(.*)_(.*)', name)
00105 if matches:
00106 prefix = matches.group(1)
00107 label = matches.group(3)
00108 knowntypes = (['pTRatio','SumPt','Size'])
00109 for knowntype in knowntypes:
00110 if matches.group(2) == knowntype:
00111 type = knowntype
00112 if not type:
00113 type = 'Eff'
00114 else:
00115 type = 'Eff'
00116
00117 prefixParts = prefix.partition('Discrimination')
00118 if prefixParts[2] != '':
00119 prefix = prefixParts[2]
00120 prefixParts = prefix.partition('By')
00121 if prefixParts[2] != '':
00122 prefix = prefixParts[2]
00123
00124
00125 return [type, label, prefix]
00126
00127 def DrawTitle(text):
00128 title = TLatex()
00129 title.SetNDC()
00130 title.SetTextAlign(12)
00131 title.SetTextSize(.035)
00132 leftMargin = gStyle.GetPadLeftMargin()
00133 topMargin = 1 - 0.5*gStyle.GetPadTopMargin()
00134 title.DrawLatex(leftMargin, topMargin, text)
00135
00136 def DrawBranding(options, label=''):
00137 if options.branding != None or label != '':
00138 text = TLatex()
00139 text.SetNDC();
00140 text.SetTextAlign(11)
00141 text.SetTextSize(.025)
00142 text.SetTextColor(13)
00143 if options.out.find(".eps")!=-1:
00144 text.SetTextAngle(-91.0)
00145 else:
00146 text.SetTextAngle(-90.0)
00147 rightMargin = 1 - gStyle.GetPadRightMargin()
00148 topMargin = 1 - gStyle.GetPadTopMargin()
00149 if label!='':
00150 label += ': '
00151 text.DrawLatex(rightMargin+.01, topMargin+0.025, label+options.branding);
00152
00153
00154 def FindParents(histoPath):
00155 root = histoPath[:histoPath.find('_')]
00156 par = histoPath[histoPath.find('Eff')+3:]
00157 validationPlots = validation.proc.efficiencies.plots._Parameterizable__parameterNames
00158 found =0
00159 num = ''
00160 den = ''
00161 for efficiency in validationPlots:
00162 effpset = getattr(validation.proc.efficiencies.plots,efficiency)
00163 effName = effpset.efficiency.value()
00164 effNameCut = effName[effName.find('_'):effName.find('#')]
00165 if effNameCut in histoPath:
00166 if found == 1:
00167 print 'More than one pair of parents found for ' + histopath + ':'
00168 assert(False)
00169 num = root + effpset.numerator.value()[effName.find('_'):].replace('#PAR#',par)
00170 den = root + effpset.denominator.value()[effName.find('_'):].replace('#PAR#',par)
00171 found += 1
00172 return [num,den]
00173
00174 def Rebin(tfile, histoPath, rebinVal):
00175 parents = FindParents(histoPath)
00176 num = tfile.Get(parents[0])
00177 if type(num) != TH1F:
00178 print 'Looking for ' + num
00179 print 'Plot now found! What the hell are you doing? Exiting...'
00180 sys.exit()
00181 denSingle = tfile.Get(parents[1])
00182 if type(denSingle) != TH1F:
00183 print 'Looking for '+denSingle
00184 print 'Plot now found! What the hell are you doing? Exiting...'
00185 sys.exit()
00186 num.Rebin(rebinVal)
00187 den = denSingle.Rebin(rebinVal,'denClone')
00188 retVal = num.Clone(histoPath+'Rebin%s'%rebinVal)
00189
00190
00191
00192 retVal.Divide(num,den,1,1,'B')
00193 return retVal
00194
00195 def findRange(hists, min0=-1, max0=-1):
00196 if len(hists) < 1:
00197 return
00198
00199 min = min0
00200 max = max0
00201 if min0 == -1 or max0 == -1:
00202 for hist in hists:
00203 if min0 == -1:
00204
00205 minTmp = getMinimumIncludingErrors(hist)
00206 if minTmp < min or min == -1:
00207 min = minTmp
00208 if max0 == -1:
00209 maxTmp = getMaximumIncludingErrors(hist)
00210 if maxTmp > max or max == -1:
00211 max = maxTmp
00212 return [min, max]
00213
00214 def optimizeRangeMainPad(argv, pad, hists, maxLogX_, minX_, maxX_, maxLogY_, minY_, maxY_):
00215 pad.Update()
00216 if pad.GetLogy():
00217 if maxLogY_ > 0:
00218 maxLogY = maxLogY_
00219 else:
00220 maxLogY = -1
00221 minY, maxY = findRange(hists, -1, maxLogY)
00222 else:
00223 minY, maxY = findRange(hists, minY_, maxY_)
00224
00225 if pad.GetLogy():
00226 if minY == 0:
00227 minY = 0.001
00228 else:
00229 if minY < 0.7:
00230 minY = minY
00231 if maxY <= 1.1 and maxY > 0.7:
00232 maxY = 1.2
00233 hists[0].SetAxisRange(minY, maxY, "Y")
00234
00235 if pad.GetLogx():
00236 if maxLogX_ > 0:
00237 maxLogX = maxLogX_
00238 else:
00239 maxLogX = -1
00240 minX, maxX = findRange(hists, -1, maxLogX)
00241 else:
00242 minX, maxX = findRange(hists, minX_, maxX_)
00243
00244 if pad.GetLogx():
00245 if minX == 0:
00246 minX = 0.001
00247 else:
00248 if minX < 0.7:
00249 minX = minX
00250 if maxX <= 1.1 and maxX > 0.7:
00251 maxX = 1.2
00252 hists[0].SetAxisRange(minX, maxX, "X")
00253
00254 def optimizeRangeSubPad(argv, pad, hists, maxLogX_, minX_, maxX_, minYRatio_, maxYRatio_):
00255 pad.Update()
00256 if pad.GetLogx():
00257 if maxLogX_ > 0:
00258 maxLogX = maxLogX_
00259 else:
00260 maxLogX = -1
00261 minX, maxX = findRange(hists, -1, maxLogX)
00262 else:
00263 minX, maxX = findRange(hists, minX_, maxX_)
00264 if pad.GetLogx():
00265 if minX == 0:
00266 minX = 0.001
00267 else:
00268 if minX < 0.7:
00269 minX = minX
00270 if maxX <= 1.1 and maxX > 0.7:
00271 maxX = 1.2
00272 hists[0].SetAxisRange(minX, maxX, "X")
00273
00274 min = -1
00275 max = -1
00276 if minYRatio_ > 0:
00277 min = minYRatio_
00278 if maxYRatio_ > 0:
00279 max = maxYRatio_
00280 min, max = findRange(hists, min, max)
00281 if max > 2:
00282 max = 2
00283 hists[0].SetAxisRange(min, max, "Y")
00284
00285 def getMaximumIncludingErrors(hist):
00286
00287 distance = 1.
00288 max = -1
00289 pos = 0
00290 for i in range(1, hist.GetNbinsX()):
00291 if hist.GetBinContent(i) > max:
00292 max = hist.GetBinContent(i)
00293 pos = i
00294 return max + distance*hist.GetBinError(pos)
00295
00296 def getMinimumIncludingErrors(hist):
00297
00298
00299 distance = 1.
00300 min = -1
00301 pos = 0
00302 for i in range(1, hist.GetNbinsX()):
00303 if hist.GetBinContent(i)<=0.:
00304 continue
00305 if hist.GetBinContent(i) < min or min==-1:
00306 min = hist.GetBinContent(i)
00307 pos = i
00308 if min < 0:
00309 min = 0
00310 return min - distance*hist.GetBinError(pos)
00311
00312
00313 def main(argv=None):
00314 if argv is None:
00315 argv = sys.argv
00316
00317 options, toPlot = LoadCommandlineOptions(argv)
00318
00319 gROOT.SetStyle('Plain')
00320 gROOT.SetBatch()
00321 gStyle.SetPalette(1)
00322 gStyle.SetOptStat(0)
00323 gStyle.SetPadGridX(True)
00324 gStyle.SetPadGridY(True)
00325 gStyle.SetOptTitle(0)
00326 gStyle.SetPadTopMargin(0.1)
00327 gStyle.SetPadBottomMargin(0.1)
00328 gStyle.SetPadLeftMargin(0.13)
00329 gStyle.SetPadRightMargin(0.07)
00330
00331
00332 testFile = TFile(options.test)
00333 refFile = None
00334 if options.ref != None:
00335 refFile = TFile(options.ref)
00336
00337
00338 plotList = []
00339 MapDirStructure( testFile,'',plotList)
00340
00341 histoList = []
00342 for plot in toPlot:
00343 for path in plotList:
00344 if Match(plot.lower(),path.lower()):
00345 histoList.append(path)
00346
00347
00348
00349 print histoList
00350
00351 if len(histoList)<1:
00352 print '\tError: Please specify at least one histogram.'
00353 if len(toPlot)>0:
00354 print 'Check your plot list:', toPlot
00355 sys.exit()
00356
00357
00358
00359 histType, label, prefix = DetermineHistType(histoList[0])
00360
00361
00362 scaleToIntegral = False
00363 if options.normalize:
00364 scaleToIntegral = True
00365
00366 ylabel = 'Efficiency'
00367
00368 if options.fakeRate:
00369 ylabel = 'Fake rate'
00370
00371 drawStats = False
00372 if histType=='pTRatio' and len(histoList)<3:
00373 drawStats = True
00374
00375
00376 x1 = 0.33
00377 x2 = 1-gStyle.GetPadRightMargin()
00378 y2 = 1-gStyle.GetPadTopMargin()
00379 lineHeight = .055
00380 if len(histoList) == 1:
00381 lineHeight = .05
00382 y1 = y2 - lineHeight*len(histoList)
00383 legend = TLegend(x1,y1,x2,y2)
00384 legend.SetHeader(label)
00385 legend.SetFillColor(0)
00386 legend.SetTextSize(0.032)
00387 if drawStats:
00388 y2 = y1
00389 y1 = y2 - .07*len(histoList)
00390 statsBox = TPaveText(x1,y1,x2,y2,"NDC")
00391 statsBox.SetFillColor(0)
00392 statsBox.SetTextAlign(12)
00393 statsBox.SetMargin(0.05)
00394 statsBox.SetBorderSize(1)
00395
00396
00397 canvas = TCanvas('MultiPlot','MultiPlot',validation.standardDrawingStuff.canvasSizeX.value(),832)
00398 effPad = TPad('effPad','effPad',0.01,0.35,0.99,0.99)
00399 effPad.SetBottomMargin(0.0)
00400
00401
00402
00403 effPad.Draw()
00404 header = ''
00405 if options.sampleLabel != None:
00406 header += 'Sample: '+options.sampleLabel
00407 if options.testLabel != None:
00408 header += ' Dots: '+options.testLabel
00409 if options.refLabel != None:
00410 header += ' Line: '+options.refLabel
00411 DrawTitle(header)
00412 DrawBranding(options)
00413 diffPad = TPad('diffPad','diffPad',0.01,0.01,0.99,0.32)
00414 diffPad.SetTopMargin(0.00);
00415 diffPad.SetBottomMargin(0.30);
00416 diffPad.Draw()
00417 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]
00418 first = True
00419 divHistos = []
00420 statTemplate = '%s Mean: %.3f RMS: %.3f'
00421 testHs = []
00422 refHs = []
00423 for histoPath,color in zip(histoList,colors):
00424 if(options.rebin == -1):
00425 testH = testFile.Get(histoPath)
00426 else:
00427 testH = Rebin(testFile,histoPath,options.rebin)
00428 if type(testH) != TH1F:
00429 print 'Looking for '+histoPath
00430 print 'Test plot now found! What the hell are you doing? Exiting...'
00431 sys.exit()
00432 testHs.append(testH)
00433 xAx = histoPath[histoPath.find('Eff')+len('Eff'):]
00434 effPad.cd()
00435 if not testH.GetXaxis().GetTitle():
00436 if hasattr(validation.standardDrawingStuff.xAxes,xAx):
00437 testH.GetXaxis().SetTitle( getattr(validation.standardDrawingStuff.xAxes,xAx).xAxisTitle.value())
00438 if not testH.GetYaxis().GetTitle():
00439 testH.GetYaxis().SetTitle(ylabel)
00440 if label!='':
00441 testH.GetXaxis().SetTitle(label+': '+testH.GetXaxis().GetTitle())
00442 testH.GetXaxis().SetTitleOffset(1.1)
00443 testH.GetXaxis().SetRangeUser(options.minXaxis,options.maxXaxis)
00444 testH.GetYaxis().SetTitleOffset(1.1)
00445
00446
00447 testH.SetMarkerSize(1)
00448 testH.SetMarkerStyle(20)
00449 testH.SetMarkerColor(color)
00450 if histType == 'Eff':
00451 legend.AddEntry(testH,histoPath[histoPath.rfind('/')+1:histoPath.find(histType)],'p')
00452 else:
00453 legend.AddEntry(testH,DetermineHistType(histoPath)[2],'p')
00454 if drawStats:
00455 text = statsBox.AddText(statTemplate % ('Dots',testH.GetMean(), testH.GetRMS()) )
00456 text.SetTextColor(color)
00457 if first:
00458 first = False
00459 if options.logScaleY:
00460 effPad.SetLogy()
00461 if options.logScaleX:
00462 effPad.SetLogx()
00463 diffPad.SetLogx()
00464 if scaleToIntegral:
00465 if testH.GetEntries() > 0:
00466 if not testH.GetSumw2N():
00467 testH.Sumw2()
00468 testH.DrawNormalized('ex0 P')
00469 else:
00470 print "--> Warning! You tried to normalize a histogram which seems to be already scaled properly. Draw it unscaled."
00471 scaleToIntegral = False
00472 testH.Draw('ex0')
00473 else:
00474 testH.Draw('ex0')
00475 else:
00476 if scaleToIntegral:
00477 if testH.GetEntries() > 0:
00478 testH.DrawNormalized('same p')
00479 else:
00480 testH.Draw('same ex0 l')
00481 if refFile == None:
00482 continue
00483 if(options.rebin == -1):
00484 refH = refFile.Get(histoPath)
00485 else:
00486 refH = Rebin(refFile,histoPath,options.rebin)
00487 if type(refH) != TH1F:
00488 continue
00489 refHs.append(refH)
00490 refH.SetLineColor(color)
00491 refH.SetLineWidth(1)
00492 if scaleToIntegral:
00493 if testH.GetEntries() > 0:
00494 refH.DrawNormalized('same hist')
00495 else:
00496 refH.DrawCopy('same hist')
00497 if drawStats:
00498 text = statsBox.AddText(statTemplate % ('Line',refH.GetMean(), refH.GetRMS()) )
00499 text.SetTextColor(color)
00500
00501
00502 if scaleToIntegral:
00503 entries = testH.GetEntries()
00504 if entries > 0:
00505 testH.Scale(1./entries)
00506 entries = refH.GetEntries()
00507 refH.Sumw2()
00508 if entries > 0:
00509 refH.Scale(1./entries)
00510 refH.Draw('same hist')
00511 divHistos.append(Divide(testH,refH))
00512
00513 if options.maxLogY > 0:
00514 maxlY=options.maxLogY
00515 if options.maxLogX > 0:
00516 maxlX=options.maxLogX
00517
00518 tmpHists = []
00519 tmpHists.extend(testHs)
00520 tmpHists.extend(refHs)
00521 optimizeRangeMainPad(argv, effPad, tmpHists, maxlX, options.minXaxis, options.maxXaxis, maxlY, options.minYaxis, options.maxYaxis)
00522
00523 firstD = True
00524 if refFile != None:
00525 for histo,color in zip(divHistos,colors):
00526 diffPad.cd()
00527 histo.SetMarkerSize(1)
00528 histo.SetMarkerStyle(20)
00529 histo.SetMarkerColor(color)
00530 histo.GetYaxis().SetLabelSize(0.07)
00531 histo.GetYaxis().SetTitleOffset(0.75)
00532 histo.GetYaxis().SetTitleSize(0.08)
00533 histo.GetXaxis().SetLabelSize(0.08)
00534 histo.GetXaxis().SetTitleSize(0.08)
00535
00536
00537
00538 if firstD:
00539 histo.Draw('ex0')
00540 firstD = False
00541 else:
00542 histo.Draw('same ex0')
00543 diffPad.Update()
00544
00545 if options.maxLogX > 0:
00546 maxlX=options.maxLogX
00547 optimizeRangeSubPad(argv, diffPad, divHistos, maxlX, options.minXaxis, options.maxXaxis, options.minYR, options.maxYR)
00548
00549 effPad.cd()
00550 legend.Draw()
00551
00552 if drawStats:
00553 statsBox.Draw()
00554
00555 canvas.Print(options.out)
00556
00557
00558 if __name__ == '__main__':
00559 sys.exit(main())