You are here

9. Fitting By Coding

TF1

In ROOT you can fit histograms (or TEfficiency objects) using formulas that you provide, and that have free parameters. For an efficiency curve a TMath::Erf() is usually a good fit function. But you can't really use it as is. Let's see what it looks like:

// This lambda defines the values of the function. For now we don't use p:
auto myErf0Lambda = [](double* x, double* p) { return TMath::Erf(x[0]); };
// When drawing, show between -10..10
TF1* myErf0 = new TF1("myErf0", myErf0Lambda, -10, 10);
myErf0->Draw();

This is the same one but moved around to match the concept of efficiencies:

auto myErf1Lambda = [](double* x, double* p) 
      { return (TMath::Erf((x[0]-5)/3) + 1)/2 * 0.8; };
TF1* myErf1 = new TF1("myErf1", myErf1Lambda, -10, 10);
myErf1->Draw();

We can re-write this using parameter notation:

auto myErf2Lambda = [](double* x, double* p) { 
        return (TMath::Erf((x[0]-p[0])/p[1]) + 1)/2 * p[2]; };
TF1* myErf2 = new TF1("myErf2", myErf2Lambda, 
           -10, 10, 3 /*number of parameters*/);
myErf2->SetParameter(0, 5.);
myErf2->SetParameter(1, 3.);
myErf2->SetParameter(2, 0.8);
myErf2->Draw();

Try out what happens when you change the values for 5, 3 and 0.8 (simply by calling myErf2->SetParameter(...);)!

Fitting a TF1

Now that we have a nice TF1 we can fit it to the TEfficiency object by calling eff->Fit(). Here is the complete code you need to add to the end of effX():

...
void effX() {
...
   auto effErf = [](double* x, double* p) {
      return (TMath::Erf((x[0] - p[0]) / p[1]) + 1) / 2. * p[2];
   };

   TF1* myErf = new TF1("myErf", effErf, 0., 10., 3);
   myErf->SetParameter(0, 5.);
   myErf->SetParameter(1, 5.);
   myErf->SetParameter(2, 1.);

   eff->Fit(myErf);
}

Parameter Initialization and Fitting

You must initialize the parameters to sensible values - this is especially important for higher dimensional fits, e.g. with a TF2. Fitting is magic, but only within limits. Try with completely bogus parameters and you'll see how the fit fails!