Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
HypoTestResult.cxx
Go to the documentation of this file.
1 // @(#)root/roostats:$Id$
2 // Author: Kyle Cranmer, Lorenzo Moneta, Gregory Schott, Wouter Verkerke, Sven Kreiss
3 /*************************************************************************
4  * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. *
5  * All rights reserved. *
6  * *
7  * For the licensing terms see $ROOTSYS/LICENSE. *
8  * For the list of contributors see $ROOTSYS/README/CREDITS. *
9  *************************************************************************/
10 
11 /*****************************************************************************
12  * Project: RooStats
13  * Package: RooFit/RooStats
14  * @(#)root/roofit/roostats:$Id$
15  * Authors:
16  * Kyle Cranmer, Lorenzo Moneta, Gregory Schott, Wouter Verkerke, Sven Kreiss
17  *
18  *****************************************************************************/
19 
20 
21 /** \class RooStats::HypoTestResult
22  \ingroup Roostats
23 
24 HypoTestResult is a base class for results from hypothesis tests.
25 Any tool inheriting from HypoTestCalculator can return a HypoTestResult.
26 As such, it stores a p-value for the null-hypothesis (eg. background-only)
27 and an alternate hypothesis (eg. signal+background).
28 The p-values can also be transformed into confidence levels
29 (\f$CL_{b}\f$, \f$CL_{s+b}\f$) in a trivial way.
30 The ratio of the \f$CL_{s+b}\f$ to \f$CL_{b}\f$ is often called
31 \f$CL_{s}\f$, and is considered useful, though it is not a probability.
32 Finally, the p-value of the null can be transformed into a number of
33 equivalent Gaussian sigma using the Significance method.
34 
35 The p-value of the null for a given test statistic is rigorously defined and
36 this is the starting point for the following conventions.
37 
38 ### Conventions used in this class
39 
40 The p-value for the null and alternate are on the **same side** of the
41 observed value of the test statistic. This is the more standard
42 convention and avoids confusion when doing inverted tests.
43 
44 For exclusion, we also want the formula \f$CL_{s} = CL_{s+b} / CL_{b}\f$
45 to hold which therefore defines our conventions for \f$CL_{s+b}\f$ and
46 \f$CL_{b}\f$. \f$CL_{s}\f$ was specifically invented for exclusion
47 and therefore all quantities need be related through the assignments
48 as they are for exclusion: \f$CL_{s+b} = p_{s+b}\f$; \f$CL_{b} = p_{b}\f$. This
49 is derived by considering the scenarios of a powerful and not powerful
50 inverted test, where for the not so powerful test, \f$CL_{s}\f$ must be
51 close to one.
52 
53 For results of Hypothesis tests,
54 \f$CL_{s}\f$ has no similar direct interpretation as for exclusion and can
55 be larger than one.
56 
57 */
58 
61 #include "RooAbsReal.h"
62 
63 #include "RooStats/RooStatsUtils.h"
64 
65 #include <limits>
66 #define NaN numeric_limits<float>::quiet_NaN()
67 #define IsNaN(a) TMath::IsNaN(a)
68 
69 ClassImp(RooStats::HypoTestResult); ;
70 
71 using namespace RooStats;
72 using namespace std;
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 /// Default constructor
76 
77 HypoTestResult::HypoTestResult(const char* name) :
78  TNamed(name,name),
79  fNullPValue(NaN), fAlternatePValue(NaN),
80  fNullPValueError(0), fAlternatePValueError(0),
81  fTestStatisticData(NaN),
82  fAllTestStatisticsData(NULL),
83  fNullDistr(NULL), fAltDistr(NULL),
84  fNullDetailedOutput(NULL), fAltDetailedOutput(NULL), fFitInfo(NULL),
85  fPValueIsRightTail(kTRUE),
86  fBackgroundIsAlt(kFALSE)
87 {
88 }
89 
90 ////////////////////////////////////////////////////////////////////////////////
91 /// Alternate constructor
92 
93 HypoTestResult::HypoTestResult(const char* name, Double_t nullp, Double_t altp) :
94  TNamed(name,name),
95  fNullPValue(nullp), fAlternatePValue(altp),
96  fNullPValueError(0), fAlternatePValueError(0),
97  fTestStatisticData(NaN),
98  fAllTestStatisticsData(NULL),
99  fNullDistr(NULL), fAltDistr(NULL),
100  fNullDetailedOutput(NULL), fAltDetailedOutput(NULL), fFitInfo(NULL),
101  fPValueIsRightTail(kTRUE),
102  fBackgroundIsAlt(kFALSE)
103 {
104 }
105 
106 ////////////////////////////////////////////////////////////////////////////////
107 /// copy constructor
108 
109 HypoTestResult::HypoTestResult(const HypoTestResult& other) :
110  TNamed(other),
111  fNullPValue(NaN), fAlternatePValue(NaN),
112  fNullPValueError(0), fAlternatePValueError(0),
113  fTestStatisticData(NaN),
114  fAllTestStatisticsData(NULL),
115  fNullDistr(NULL), fAltDistr(NULL),
116  fNullDetailedOutput(NULL), fAltDetailedOutput(NULL), fFitInfo(NULL),
117  fPValueIsRightTail( other.GetPValueIsRightTail() ),
118  fBackgroundIsAlt( other.GetBackGroundIsAlt() )
119 {
120  this->Append( &other );
121 }
122 
123 ////////////////////////////////////////////////////////////////////////////////
124 /// Destructor
125 
126 HypoTestResult::~HypoTestResult()
127 {
128  if( fNullDistr ) delete fNullDistr;
129  if( fAltDistr ) delete fAltDistr;
130 
131  if( fNullDetailedOutput ) delete fNullDetailedOutput;
132  if( fAltDetailedOutput ) delete fAltDetailedOutput;
133 
134  if( fAllTestStatisticsData ) delete fAllTestStatisticsData;
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
138 /// assignment operator
139 
140 HypoTestResult & HypoTestResult::operator=(const HypoTestResult& other) {
141  if (this == &other) return *this;
142  SetName(other.GetName());
143  SetTitle(other.GetTitle());
144  fNullPValue = other.fNullPValue;
145  fAlternatePValue = other.fAlternatePValue;
146  fNullPValueError = other.fNullPValueError;
147  fAlternatePValueError = other.fAlternatePValueError;
148  fTestStatisticData = other.fTestStatisticData;
149 
150  if( fAllTestStatisticsData ) delete fAllTestStatisticsData;
151  fAllTestStatisticsData = NULL;
152  if( fNullDistr ) { delete fNullDistr; fNullDistr = NULL; }
153  if( fAltDistr ) { delete fAltDistr; fAltDistr = NULL; }
154  if( fNullDetailedOutput ) { delete fNullDetailedOutput; fNullDetailedOutput = NULL; }
155  if( fAltDetailedOutput ) { delete fAltDetailedOutput; fAltDetailedOutput = NULL; }
156  if (fFitInfo) { delete fFitInfo; fFitInfo = NULL; }
157 
158  fPValueIsRightTail = other.GetPValueIsRightTail();
159  fBackgroundIsAlt = other.GetBackGroundIsAlt();
160 
161  this->Append( &other );
162 
163  return *this;
164 }
165 
166 ////////////////////////////////////////////////////////////////////////////////
167 /// Add additional toy-MC experiments to the current results.
168 /// Use the data test statistics of the added object if it is not already
169 /// set (otherwise, ignore the new one).
170 
171 void HypoTestResult::Append(const HypoTestResult* other) {
172  if(fNullDistr)
173  fNullDistr->Add(other->GetNullDistribution());
174  else
175  if(other->GetNullDistribution()) fNullDistr = new SamplingDistribution( *other->GetNullDistribution() );
176 
177  if(fAltDistr)
178  fAltDistr->Add(other->GetAltDistribution());
179  else
180  if(other->GetAltDistribution()) fAltDistr = new SamplingDistribution( *other->GetAltDistribution() );
181 
182 
183  if( fNullDetailedOutput ) {
184  if( other->GetNullDetailedOutput() ) fNullDetailedOutput->append( *other->GetNullDetailedOutput() );
185  }else{
186  if( other->GetNullDetailedOutput() ) fNullDetailedOutput = new RooDataSet( *other->GetNullDetailedOutput() );
187  }
188 
189  if( fAltDetailedOutput ) {
190  if( other->GetAltDetailedOutput() ) fAltDetailedOutput->append( *other->GetAltDetailedOutput() );
191  }else{
192  if( other->GetAltDetailedOutput() ) fAltDetailedOutput = new RooDataSet( *other->GetAltDetailedOutput() );
193  }
194 
195  if( fFitInfo ) {
196  if( other->GetFitInfo() ) fFitInfo->append( *other->GetFitInfo() );
197  }else{
198  if( other->GetFitInfo() ) fFitInfo = new RooDataSet( *other->GetFitInfo() );
199  }
200 
201  // if no data is present use the other HypoTestResult's data
202  if(IsNaN(fTestStatisticData)) fTestStatisticData = other->GetTestStatisticData();
203 
204  UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, kTRUE);
205  UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, kFALSE);
206 }
207 
208 ////////////////////////////////////////////////////////////////////////////////
209 
210 void HypoTestResult::SetAltDistribution(SamplingDistribution *alt) {
211  fAltDistr = alt;
212  UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, kFALSE);
213 }
214 
215 ////////////////////////////////////////////////////////////////////////////////
216 
217 void HypoTestResult::SetNullDistribution(SamplingDistribution *null) {
218  fNullDistr = null;
219  UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, kTRUE);
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 
224 void HypoTestResult::SetTestStatisticData(const Double_t tsd) {
225  fTestStatisticData = tsd;
226 
227  UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, kTRUE);
228  UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, kFALSE);
229 }
230 
231 ////////////////////////////////////////////////////////////////////////////////
232 
233 void HypoTestResult::SetAllTestStatisticsData(const RooArgList* tsd) {
234  if (fAllTestStatisticsData) {
235  delete fAllTestStatisticsData;
236  fAllTestStatisticsData = 0;
237  }
238  if (tsd) fAllTestStatisticsData = (const RooArgList*)tsd->snapshot();
239 
240  if( fAllTestStatisticsData && fAllTestStatisticsData->getSize() > 0 ) {
241  RooRealVar* firstTS = (RooRealVar*)fAllTestStatisticsData->at(0);
242  if( firstTS ) SetTestStatisticData( firstTS->getVal() );
243  }
244 }
245 
246 ////////////////////////////////////////////////////////////////////////////////
247 
248 void HypoTestResult::SetPValueIsRightTail(Bool_t pr) {
249  fPValueIsRightTail = pr;
250 
251  UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, kTRUE);
252  UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, kFALSE);
253 }
254 
255 ////////////////////////////////////////////////////////////////////////////////
256 
257 Bool_t HypoTestResult::HasTestStatisticData(void) const {
258  return !IsNaN(fTestStatisticData);
259 }
260 
261 ////////////////////////////////////////////////////////////////////////////////
262 
263 Double_t HypoTestResult::NullPValueError() const {
264  // compute error on Null pvalue
265  return fNullPValueError;
266 }
267 
268 ////////////////////////////////////////////////////////////////////////////////
269 /// compute \f$CL_{b}\f$ error
270 /// \f$CL_{b}\f$ = 1 - NullPValue()
271 /// must use opposite condition that routine above
272 
273 Double_t HypoTestResult::CLbError() const {
274  return fBackgroundIsAlt ? fAlternatePValueError : fNullPValueError;
275 }
276 
277 ////////////////////////////////////////////////////////////////////////////////
278 
279 Double_t HypoTestResult::CLsplusbError() const {
280  return fBackgroundIsAlt ? fNullPValueError : fAlternatePValueError;
281 }
282 
283 ////////////////////////////////////////////////////////////////////////////////
284 /// Taylor expansion series approximation for standard deviation (error propagation)
285 
286 Double_t HypoTestResult::SignificanceError() const {
287  return NullPValueError() / ROOT::Math::normal_pdf(Significance());
288 }
289 
290 ////////////////////////////////////////////////////////////////////////////////
291 /// Returns an estimate of the error on \f$CL_{s}\f$ through combination of the
292 /// errors on \f$CL_{b}\f$ and \f$CL_{s+b}\f$:
293 /// \f[
294 /// \sigma_{CL_s} = CL_s
295 /// \sqrt{\left( \frac{\sigma_{CL_{s+b}}}{CL_{s+b}} \right)^2 + \left( \frac{\sigma_{CL_{b}}}{CL_{b}} \right)^2}
296 /// \f]
297 
298 Double_t HypoTestResult::CLsError() const {
299  if(!fAltDistr || !fNullDistr) return 0.0;
300 
301  // unsigned const int n_b = fNullDistr->GetSamplingDistribution().size();
302  // unsigned const int n_sb = fAltDistr->GetSamplingDistribution().size();
303 
304  // if CLb() == 0 CLs = -1 so return a -1 error
305  if (CLb() == 0 ) return -1;
306 
307  double cl_b_err2 = pow(CLbError(),2);
308  double cl_sb_err2 = pow(CLsplusbError(),2);
309 
310  return TMath::Sqrt(cl_sb_err2 + cl_b_err2 * pow(CLs(),2))/CLb();
311 }
312 
313 ////////////////////////////////////////////////////////////////////////////////
314 /// updates the pvalue if sufficient data is available
315 
316 void HypoTestResult::UpdatePValue(const SamplingDistribution* distr, Double_t &pvalue, Double_t &perror, Bool_t /*isNull*/) {
317  if(IsNaN(fTestStatisticData)) return;
318  if(!distr) return;
319 
320  /* Got to be careful for discrete distributions:
321  * To get the right behaviour for limits, the p-value must
322  * include the value of fTestStatistic both for Alt and Null cases
323  */
324  if(fPValueIsRightTail) {
325  pvalue = distr->IntegralAndError(perror, fTestStatisticData, RooNumber::infinity(), kTRUE,
326  kTRUE , kTRUE ); // always closed interval [ fTestStatistic, inf ]
327 
328  }else{
329  pvalue = distr->IntegralAndError(perror, -RooNumber::infinity(), fTestStatisticData, kTRUE,
330  kTRUE, kTRUE ); // // always closed [ -inf, fTestStatistic ]
331  }
332 }
333 
334 ////////////////////////////////////////////////////////////////////////////////
335 /// Print out some information about the results
336 /// Note: use Alt/Null labels for the hypotheses here as the Null
337 /// might be the s+b hypothesis.
338 
339 void HypoTestResult::Print(Option_t * ) const
340 {
341  bool fromToys = (fAltDistr || fNullDistr);
342 
343  std::cout << std::endl << "Results " << GetName() << ": " << endl;
344  std::cout << " - Null p-value = " << NullPValue();
345  if (fromToys) std::cout << " +/- " << NullPValueError();
346  std::cout << std::endl;
347  std::cout << " - Significance = " << Significance();
348  if (fromToys) std::cout << " +/- " << SignificanceError() << " sigma";
349  std::cout << std::endl;
350  if(fAltDistr)
351  std::cout << " - Number of Alt toys: " << fAltDistr->GetSize() << std::endl;
352  if(fNullDistr)
353  std::cout << " - Number of Null toys: " << fNullDistr->GetSize() << std::endl;
354 
355  if (HasTestStatisticData() ) std::cout << " - Test statistic evaluated on data: " << fTestStatisticData << std::endl;
356  std::cout << " - CL_b: " << CLb();
357  if (fromToys) std::cout << " +/- " << CLbError();
358  std::cout << std::endl;
359  std::cout << " - CL_s+b: " << CLsplusb();
360  if (fromToys) std::cout << " +/- " << CLsplusbError();
361  std::cout << std::endl;
362  std::cout << " - CL_s: " << CLs();
363  if (fromToys) std::cout << " +/- " << CLsError();
364  std::cout << std::endl;
365 
366  return;
367 }