Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
LikelihoodIntervalPlot.cxx
Go to the documentation of this file.
1 // @(#)root/roostats:$Id$
2 
3 /*************************************************************************
4  * Project: RooStats *
5  * Package: RooFit/RooStats *
6  * Authors: *
7  * Kyle Cranmer, Lorenzo Moneta, Gregory Schott, Wouter Verkerke *
8  *************************************************************************
9  * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. *
10  * All rights reserved. *
11  * *
12  * For the licensing terms see $ROOTSYS/LICENSE. *
13  * For the list of contributors see $ROOTSYS/README/CREDITS. *
14  *************************************************************************/
15 
16 
17 /** \class RooStats::LikelihoodIntervalPlot
18  \ingroup Roostats
19 
20  This class provides simple and straightforward utilities to plot a LikelihoodInterval
21  object.
22 
23 */
24 
26 
27 #include <algorithm>
28 #include <iostream>
29 #include <cmath>
30 
31 #include "TROOT.h"
32 #include "TMath.h"
33 #include "TLine.h"
34 #include "TObjArray.h"
35 #include "TList.h"
36 #include "TGraph.h"
37 #include "TPad.h"
38 #include "TCanvas.h"
39 #include "Math/DistFunc.h"
40 
41 
42 #include "RooRealVar.h"
43 #include "RooPlot.h"
44 #include "RooMsgService.h"
45 #include "RooProfileLL.h"
46 #include "TF1.h"
47 
48 /// ClassImp for building the THtml documentation of the class
49 using namespace std;
50 
51 ClassImp(RooStats::LikelihoodIntervalPlot);
52 
53 using namespace RooStats;
54 
55 ////////////////////////////////////////////////////////////////////////////////
56 /// LikelihoodIntervalPlot default constructor
57 /// with default parameters
58 
59 LikelihoodIntervalPlot::LikelihoodIntervalPlot()
60 {
61  fInterval = 0;
62  fNdimPlot = 0;
63  fParamsPlot = 0;
64  fColor = 0;
65  fFillStyle = 4050; // half transparent
66  fLineColor = 0;
67  fMaximum = -1;
68  fNPoints = 0; // default depends if 1D or 2D
69  // default is variable range
70  fXmin = 0;
71  fXmax = -1;
72  fYmin = 0;
73  fYmax = -1;
74  fPrecision = -1; // use default
75  fPlotObject = 0;
76 }
77 
78 ////////////////////////////////////////////////////////////////////////////////
79 /// LikelihoodIntervalPlot copy constructor
80 
81 LikelihoodIntervalPlot::LikelihoodIntervalPlot(LikelihoodInterval* theInterval)
82 {
83  fInterval = theInterval;
84  fParamsPlot = fInterval->GetParameters();
85  fNdimPlot = fParamsPlot->getSize();
86  fColor = 0;
87  fLineColor = 0;
88  fFillStyle = 4050; // half transparent
89  fMaximum = -1;
90  fNPoints = 0; // default depends if 1D or 2D
91  // default is variable range
92  fXmin = 0;
93  fXmax = -1;
94  fYmin = 0;
95  fYmax = -1;
96  fPrecision = -1; // use default
97  fPlotObject = 0;
98 }
99 
100 ////////////////////////////////////////////////////////////////////////////////
101 /// LikelihoodIntervalPlot destructor
102 
103 LikelihoodIntervalPlot::~LikelihoodIntervalPlot()
104 {
105 }
106 
107 ////////////////////////////////////////////////////////////////////////////////
108 
109 void LikelihoodIntervalPlot::SetLikelihoodInterval(LikelihoodInterval* theInterval)
110 {
111  fInterval = theInterval;
112  fParamsPlot = fInterval->GetParameters();
113  fNdimPlot = fParamsPlot->getSize();
114 
115  return;
116 }
117 
118 ////////////////////////////////////////////////////////////////////////////////
119 
120 void LikelihoodIntervalPlot::SetPlotParameters(const RooArgSet *params)
121 {
122  fNdimPlot = params->getSize();
123  fParamsPlot = (RooArgSet*) params->clone((std::string(params->GetName())+"_clone").c_str());
124 
125  return;
126 }
127 
128 
129 ////////////////////////////////////////////////////////////////////////////////
130 /// draw the log of the profiled likelihood function in 1D with the interval or
131 /// as a 2D plot with the contours.
132 /// Higher dimensional intervals cannot be drawn. One needs to call
133 /// SetPlotParameters to project interval in 1 or 2dim
134 ///
135 /// ### Options for drawing 1D intervals
136 ///
137 /// For 1D problem the log of the profiled likelihood function is drawn by default in a RooPlot as a
138 /// RooCurve
139 /// The plotting range (default is the full parameter range) and the precision of the RooCurve
140 /// can be specified by using SetRange(x1,x2) and SetPrecision(eps).
141 /// SetNPoints(npoints) can also be used (default is npoints=100)
142 /// Optionally the function can be drawn as a TF1 (option="tf1") obtained by sampling the given npoints
143 /// in the given range
144 ///
145 /// ### Options for drawing 2D intervals
146 ///
147 /// For 2D case, a contour and optionally the profiled likelihood function is drawn by sampling npoints in
148 /// the given range. A 2d histogram of nbinsX=nbinsY = sqrt(npoints) is used for sampling the profiled likelihood.
149 /// The contour can be obtained by using Minuit or by the sampled histogram,
150 /// If using Minuit, the number of points specifies the number of contour points. If using an histogram the number of
151 /// points is approximately the total number of bins of the histogram.
152 /// Possible options:
153 /// - minuit/nominuit: use minuit for computing the contour
154 /// - hist/nohist : sample in an histogram the profiled likelihood
155 ///
156 /// Note that one can have both a drawing of the sampled likelihood and of the contour using minuit.
157 /// The default options is "minuit nohist"
158 /// The sampled histogram is drawn first by default using the option "colz" and then 8 probability contours at
159 /// these CL are drawn: { 0.1,0.3,0.5,0.683,0.95,0.9973,0.9999366575,0.9999994267} re-drawing the histogram with the
160 /// option "cont3"
161 ///
162 /// The drawn object (RooPlot or sampled histogram) is saved in the class and can be retrieved using GetPlottedObject()
163 /// In this way the user can eventually customize further the plot.
164 /// Note that the class does not delete the plotted object. It needs, if needed, to be deleted by the user
165 
166 void LikelihoodIntervalPlot::Draw(const Option_t *options)
167 {
168  TIter it = fParamsPlot->createIterator();
169  // we need to check if parameters to plot is different than parameters of interval
170  RooArgSet* intervalParams = fInterval->GetParameters();
171  RooAbsArg * arg = 0;
172  RooArgSet extraParams;
173  while((arg=(RooAbsArg*)it.Next())) {
174  if (!intervalParams->contains(*arg) ) {
175  ccoutE(InputArguments) << "Parameter " << arg->GetName() << "is not in the list of LikelihoodInterval parameters "
176  << " - do not use for plotting " << std::endl;
177  fNdimPlot--;
178  extraParams.add(*arg);
179  }
180  }
181  if (extraParams.getSize() > 0)
182  fParamsPlot->remove(extraParams,true,true);
183 
184  if(fNdimPlot > 2){
185  ccoutE(InputArguments) << "LikelihoodIntervalPlot::Draw(" << GetName()
186  << ") ERROR: contours for more than 2 dimensions not implemented!" << std::endl;
187  return;
188  }
189 
190  // if the number of parameters to plot is less to the number of parameters of the LikelihoodInterval
191  // we need to re-do the profile likelihood function, otherwise those parameters will not be profiled
192  // when plotting
193  RooAbsReal* newProfile = 0;
194  RooAbsReal* oldProfile = fInterval->GetLikelihoodRatio();
195  if (fNdimPlot != intervalParams->getSize() ) {
196  RooProfileLL * profilell = dynamic_cast<RooProfileLL*>(oldProfile);
197  if (!profilell) return;
198  RooAbsReal & nll = profilell->nll();
199  newProfile = nll.createProfile(*fParamsPlot);
200  }
201  else {
202  newProfile = oldProfile;
203  }
204 
205  it.Reset();
206  RooRealVar *myparam = (RooRealVar*) it.Next();
207 
208  // do a dummy evaluation around minimum to be sure profile has right minimum
209  if (fInterval->GetBestFitParameters() ) {
210  *fParamsPlot = *fInterval->GetBestFitParameters();
211  newProfile->getVal();
212  }
213 
214  // analyze options
215  TString opt = options;
216  opt.ToLower();
217 
218  TString title = GetTitle();
219  int nPoints = fNPoints;
220 
221  if(fNdimPlot == 1) {
222 
223  // 1D drawing options
224  // use RooPLot for drawing the 1D PL
225  // if option is TF1 use TF1 for drawing
226  bool useRooPlot = opt.Contains("rooplot") || ! (opt.Contains("tf1"));
227  opt.ReplaceAll("rooplot","");
228  opt.ReplaceAll("tf1","");
229 
230 
231  // if (title.Length() == 0)
232  // title = "- log profile likelihood ratio";
233 
234  if (nPoints <=0) nPoints = 100; // default in 1D
235 
236  const Double_t xcont_min = fInterval->LowerLimit(*myparam);
237  const Double_t xcont_max = fInterval->UpperLimit(*myparam);
238 
239  RooRealVar* myarg = (RooRealVar *) newProfile->getVariables()->find(myparam->GetName());
240  double x1 = myarg->getMin();
241  double x2 = myarg->getMax();
242 
243  // default color values
244  if (fColor == 0) fColor = kBlue;
245  if (fLineColor == 0) fLineColor = kGreen;
246 
247  RooPlot * frame = 0;
248 
249  // use TF1 for drawing the function
250  if (!useRooPlot) {
251 
252  // set a first estimate of range including 2 times upper and lower limit
253  double xmin = std::max( x1, 2*xcont_min - xcont_max);
254  double xmax = std::min( x2, 2*xcont_max - xcont_min);
255  if (fXmin < fXmax) { xmin = fXmin; xmax = fXmax; }
256 
257  TF1 * tmp = newProfile->asTF(*myarg);
258  assert(tmp != 0);
259  tmp->SetRange(xmin, xmax);
260  tmp->SetNpx(nPoints);
261 
262  // clone the function to avoid later to sample it
263  TF1 * f1 = (TF1*) tmp->Clone();
264  delete tmp;
265 
266  f1->SetTitle(title);
267  TString name = TString(GetName()) + TString("_PLL_") + TString(myarg->GetName());
268  f1->SetName(name);
269 
270  // set range for displaying x values where function <= fMaximum
271  // if no range is set amd
272  // if no reasonable value found maintain first estimate
273  x1 = xmin; x2 = xmax;
274  if (fMaximum > 0 && fXmin >= fXmax ) {
275  double x0 = f1->GetX(0, xmin, xmax);
276  // check that minimum is between xmin and xmax
277  if ( x0 > x1 && x0 < x2) {
278  x1 = f1->GetX(fMaximum, xmin, x0);
279  x2 = f1->GetX(fMaximum, x0, xmax);
280  f1->SetMaximum(fMaximum);
281  //std::cout << "setting range to " << x1 << " , " << x2 << " x0 = " << x0 << std::endl;
282  }
283  }
284 
285  f1->SetRange(x1,x2);
286 
287 
288  f1->SetLineColor(kBlue);
289  f1->GetXaxis()->SetTitle(myarg->GetName());
290  f1->GetYaxis()->SetTitle(Form("- log #lambda(%s)",myparam->GetName()));
291  f1->Draw(opt);
292  fPlotObject = f1->GetHistogram();
293 
294  }
295  else {
296  // use a RooPlot for drawing the PL function
297  double xmin = myparam->getMin(); double xmax = myparam->getMax();
298  if (fXmin < fXmax) { xmin = fXmin; xmax = fXmax; }
299 
300  // set nbins (must be used in combination with precision )
301  // the curve will evaluate 2 * nbins if precision is > 1
302  int prevBins = myarg->getBins();
303  myarg->setBins(fNPoints);
304 
305  // want to set range on frame not function
306  frame = myarg->frame(xmin,xmax,nPoints);
307  // for ycutoff line
308  x1= xmin;
309  x2=xmax;
310  frame->SetTitle(title);
311  frame->GetYaxis()->SetTitle(Form("- log #lambda(%s)",myparam->GetName()));
312  // frame->GetYaxis()->SetTitle("- log profile likelihood ratio");
313 
314 
315  // plot
316  RooCmdArg cmd;
317  if (fPrecision > 0) cmd = RooFit::Precision(fPrecision);
318  newProfile->plotOn(frame,cmd,RooFit::LineColor(fColor));
319 
320  frame->SetMaximum(fMaximum);
321  frame->SetMinimum(0.);
322 
323  myarg->setBins(prevBins);
324  fPlotObject = frame;
325  }
326 
327 
328  //myarg->setVal(xcont_max);
329  //const Double_t Yat_Xmax = newProfile->getVal();
330  Double_t Yat_Xmax = 0.5*ROOT::Math::chisquared_quantile(fInterval->ConfidenceLevel(),1);
331 
332  TLine *Yline_cutoff = new TLine(x1,Yat_Xmax,x2,Yat_Xmax);
333  TLine *Yline_min = new TLine(xcont_min,0.,xcont_min,Yat_Xmax);
334  TLine *Yline_max = new TLine(xcont_max,0.,xcont_max,Yat_Xmax);
335 
336  Yline_cutoff->SetLineColor(fLineColor);
337  Yline_min->SetLineColor(fLineColor);
338  Yline_max->SetLineColor(fLineColor);
339 
340  if (!useRooPlot) {
341  // need to draw the line
342  Yline_cutoff->Draw();
343  Yline_min->Draw();
344  Yline_max->Draw();
345  }
346  else {
347  // add line in the RooPlot
348  frame->addObject(Yline_min);
349  frame->addObject(Yline_max);
350  frame->addObject(Yline_cutoff);
351  frame->Draw(opt);
352  }
353 
354 
355  return;
356  }
357 
358  // case of 2 dimensions
359 
360  else if(fNdimPlot == 2){
361 
362  //2D drawing options
363 
364  // use Minuit for drawing the contours of the PL (default case)
365  bool useMinuit = !opt.Contains("nominuit");
366  // plot histogram in 2D
367  bool plotHist = !opt.Contains("nohist");
368  opt.ReplaceAll("nominuit","");
369  opt.ReplaceAll("nohist","");
370  if (opt.Contains("minuit") ) useMinuit= true;
371  if (useMinuit) plotHist = false; // switch off hist by default in case of Minuit
372  if (opt.Contains("hist") ) plotHist= true;
373  opt.ReplaceAll("minuit","");
374  opt.ReplaceAll("hist","");
375 
376  RooRealVar *myparamY = (RooRealVar*)it.Next();
377 
378  Double_t cont_level = ROOT::Math::chisquared_quantile(fInterval->ConfidenceLevel(),fNdimPlot); // level for -2log LR
379  cont_level = cont_level/2; // since we are plotting -log LR
380 
381  RooArgList params(*newProfile->getVariables());
382  // set values and error for the POI to the best fit values
383  for (int i = 0; i < params.getSize(); ++i) {
384  RooRealVar & par = (RooRealVar &) params[i];
385  RooRealVar * fitPar = (RooRealVar *) (fInterval->GetBestFitParameters()->find(par.GetName() ) );
386  if (fitPar) {
387  par.setVal( fitPar->getVal() );
388  }
389  }
390  // do a profile evaluation to start from the best fit values of parameters
391  newProfile->getVal();
392 
393  if (title.Length() == 0)
394  title = TString("Contour of ") + TString(myparamY->GetName() ) + TString(" vs ") + TString(myparam->GetName() );
395  // add also labels
396  title = TString::Format("%s;%s;%s",title.Data(),myparam->GetName(),myparamY->GetName());
397 
398  if (nPoints <=0) nPoints = 40; // default in 2D
399 
400  double xmin = myparam->getMin(); double xmax = myparam->getMax();
401  double ymin = myparamY->getMin(); double ymax = myparamY->getMax();
402  if (fXmin < fXmax) { xmin = fXmin; xmax = fXmax; }
403  if (fYmin < fYmax) { ymin = fYmin; ymax = fYmax; }
404 
405 
406  if (!useMinuit || plotHist) {
407 
408  // find contour from a scanned histogram of points
409 
410  // draw directly the TH2 from the profile LL
411  TString histName = TString::Format("_hist2D__%s_%s",myparam->GetName(),myparamY->GetName() );
412  int nBins = int( std::sqrt(double(nPoints)) + 0.5 );
413  TH2* hist2D = new TH2D(histName, title, nBins, xmin, xmax, nBins, ymin, ymax );
414  newProfile->fillHistogram(hist2D, RooArgList(*myparam,*myparamY), 1, 0, false, 0, false);
415 
416  hist2D->SetTitle(title);
417  hist2D->SetStats(kFALSE);
418 
419  //need many color levels for drawing with option colz
420  if (plotHist) {
421 
422  const int nLevels = 51;
423  double contLevels[nLevels];
424  contLevels[0] = 0.01;
425  double maxVal = (fMaximum > 0) ? fMaximum : hist2D->GetMaximum();
426  for (int k = 1; k < nLevels; ++k) {
427  contLevels[k] = k*maxVal/double(nLevels-1);
428  }
429  hist2D->SetContour(nLevels,contLevels);
430 
431  if (fMaximum>0) hist2D->SetMaximum(fMaximum);
432 
433  hist2D->DrawClone("COLZ");
434  }
435 
436 
437  //need now less contours for drawing with option cont
438 
439  const int nLevels = 8;
440  double contLevels[nLevels];
441  // last 3 are the 3,4,5 sigma levels
442  double confLevels[nLevels] = { 0.1,0.3,0.5,0.683,0.95,0.9973,0.9999366575,0.9999994267};
443  for (int k = 0; k < nLevels; ++k) {
444  //contLevels[k] = 0.5*ROOT::Math::chisquared_quantile(1.-2.*ROOT::Math::normal_cdf_c(nSigmaLevels[k],1),2);
445  contLevels[k] = 0.5*ROOT::Math::chisquared_quantile(confLevels[k],2);
446  }
447  hist2D->SetContour(nLevels,contLevels);
448  if (fLineColor) hist2D->SetLineColor(fLineColor);
449 
450  // default options for drawing a second histogram
451  TString tmpOpt = opt;
452  tmpOpt.ReplaceAll("same","");
453  if (tmpOpt.Length() < 3) opt += "cont3";
454  // if histo is plotted draw on top
455  if (plotHist) opt += TString(" same");
456  hist2D->Draw(opt.Data());
457  gPad->Update();
458 
459  // case of plotting contours without minuit
460  if (!useMinuit) {
461 
462  // set levels of contours if make contours without minuit
463  TH2 * h = (TH2*) hist2D->Clone();
464  h->SetContour(1,&cont_level);
465 
466  TVirtualPad * currentPad = gPad;
467  // o a temporary draw to get the contour graph
468  TCanvas * tmpCanvas = new TCanvas("tmpCanvas","tmpCanvas");
469  h->Draw("CONT LIST");
470  gPad->Update();
471 
472  // get graphs from the contours
473  TObjArray *contoursOrig = (TObjArray*) gROOT->GetListOfSpecials()->FindObject("contours");
474  // CLONE THE LIST IN CASE IT GETS DELETED
475  TObjArray *contours = 0;
476  if (contoursOrig) contours = (TObjArray*) contoursOrig->Clone();
477 
478  delete tmpCanvas;
479  delete h;
480  gPad = currentPad;
481 
482 
483  // in case of option CONT4 I need to re-make the Pad
484  if (tmpOpt.Contains("cont4")) {
485  Double_t bm = gPad->GetBottomMargin();
486  Double_t lm = gPad->GetLeftMargin();
487  Double_t rm = gPad->GetRightMargin();
488  Double_t tm = gPad->GetTopMargin();
489  Double_t x1 = hist2D->GetXaxis()->GetXmin();
490  Double_t y1 = hist2D->GetYaxis()->GetXmin();
491  Double_t x2 = hist2D->GetXaxis()->GetXmax();
492  Double_t y2 = hist2D->GetYaxis()->GetXmax();
493 
494  TPad *null=new TPad("null","null",0,0,1,1);
495  null->SetFillStyle(0);
496  null->SetFrameFillStyle(0);
497  null->Draw();
498  null->cd();
499  null->Range(x1-(x2-x1)*(lm/(1-rm-lm)),
500  y1-(y2-y1)*(bm/(1-tm-lm)),
501  x2+(x2-x1)*(rm/(1-rm-lm)),
502  y2+(y2-y1)*(tm/(1-tm-lm)));
503 
504  gPad->Update();
505  }
506 
507 
508  if (contours) {
509  int ncontours = contours->GetSize();
510  for (int icont = 0; icont < ncontours; ++icont) {
511  TList * contourList = (TList*)contours->At(icont);
512  if (contourList && contourList->GetSize() > 0) {
513  TIterator * itgr = contourList->MakeIterator();
514  TGraph * gr = 0;
515  while( (gr = dynamic_cast<TGraph*>(itgr->Next()) ) ){
516  if (fLineColor) gr->SetLineColor(fLineColor);
517  gr->SetLineStyle(kDashed);
518  gr->SetLineWidth(3);
519  if (fColor) {
520  gr->SetFillColor(fColor);
521  gr->Draw("FL");
522  }
523  else
524  gr->Draw("L");
525  }
526  delete itgr;
527  }
528  }
529  }
530  else {
531  ccoutE(InputArguments) << "LikelihoodIntervalPlot::Draw(" << GetName()
532  << ") ERROR: no contours found in ListOfSpecial" << std::endl;
533  }
534 
535  fPlotObject = hist2D;
536 
537  }
538  }
539  if (useMinuit) {
540 
541  // find contours using Minuit
542  TGraph * gr = new TGraph(nPoints+1);
543 
544  int ncp = fInterval->GetContourPoints(*myparam, *myparamY, gr->GetX(), gr->GetY(),nPoints);
545 
546  if (int(ncp) < nPoints) {
547  std::cout << "Warning - Less points calculated in contours np = " << ncp << " / " << nPoints << std::endl;
548  for (int i = ncp; i < nPoints; ++i) gr->RemovePoint(i);
549  }
550  // add last point to same as first one to close the contour
551  gr->SetPoint(ncp, gr->GetX()[0], gr->GetY()[0] );
552  if (!opt.Contains("c")) opt.Append("L"); // use by default option L if C is not specified
553  // draw first a dummy 2d histogram gfor the axis
554  if (!opt.Contains("same") && !plotHist) {
555 
556  TH2F* hist2D = new TH2F("_hist2D",title, nPoints, xmin, xmax, nPoints, ymin, ymax );
557  hist2D->GetXaxis()->SetTitle(myparam->GetName());
558  hist2D->GetYaxis()->SetTitle(myparamY->GetName());
559  hist2D->SetBit(TH1::kNoStats); // do not draw statistics
560  hist2D->SetFillStyle(fFillStyle);
561  hist2D->SetMaximum(1); // to avoid problem with subsequents draws
562  hist2D->Draw("AXIS");
563  }
564  if (fLineColor) gr->SetLineColor(fLineColor);
565  if (fColor) {
566  // draw contour as filled area (add option "F")
567  gr->SetFillColor(fColor);
568  opt.Append("F");
569  }
570  gr->SetLineWidth(3);
571  if (opt.Contains("same")) gr->SetFillStyle(fFillStyle); // put transparent
572  gr->Draw(opt);
573  TString name = TString("Graph_of_") + TString(fInterval->GetName());
574  gr->SetName(name);
575 
576  if (!fPlotObject) fPlotObject = gr;
577  else if (fPlotObject->IsA() != TH2D::Class() ) fPlotObject = gr;
578 
579  }
580 
581  // draw also the minimum
582  const RooArgSet * bestFitParams = fInterval->GetBestFitParameters();
583  if (bestFitParams) {
584  TGraph * gr0 = new TGraph(1);
585  double x0 = bestFitParams->getRealValue(myparam->GetName());
586  double y0 = bestFitParams->getRealValue(myparamY->GetName());
587  gr0->SetPoint(0,x0,y0);
588  gr0->SetMarkerStyle(33);
589  if (fColor) {
590  if (fColor != kBlack) gr0->SetMarkerColor(fColor+4);
591  else gr0->SetMarkerColor(kGray);
592  }
593  gr0->Draw("P");
594  delete bestFitParams;
595  }
596 
597 
598 
599  }
600 
601  // need to delete if a new profileLL was made
602  if (newProfile != oldProfile) delete newProfile;
603 
604  return;
605 }