First Steps: Formulae, Functions and Fitting

Overview

Creating and using TFormula objects. Creating and drawing TF1 function objects. Fitting histograms.

The Lesson

The material in this lesson is based on the ROOT tutorial Simple Formula and Functions

Formulae: TFormula

TFormula encapsulates the concept of a 3 dimensional formula. A formula is specified as character string containing an expression involving:- Suppose we wish to create the formula:- 2 + 3*sin(x) This can be constructed as follows:- TFormula *my_formula = new TFormula("my_formula","2 + 3*sin(x)"); It can be evaluated at say, x = 20, with:- my_formula->Eval(20.); If we want the terms to be variable, this could be done like this:- TFormula *my_formula = new TFormula("my_formula","[0] + [1]*sin(x)"); my_formula->SetParameter(0, 2.); my_formula->SetParameter(1, 3.); which results in the same formula only now the parameters can be changed during the object's lifetime. Parameters can be named using the SetParName() and SetParNames() messages. The complete parameter array can be set with the single message SetParameters().

Polynomials are denoted by:-

  dpoln(i)

where:-
 d = dimension = x(default if omitted), y or z
 n = dimension of the power series
 i = index of first parameter element

so:-
  pol3(5) or xpol3(5) is equivalent to [5]+[6]*x+[7]*x**2+[8]*x**3
In a similar way:-
  xgaus(i) is equivalent to [i]*exp(-0.5*((x-[i+1])/[i+2])**2)
  xexpo(i) is equivalent to exp([i]+[i+1]*x)
Try this in ROOT as a simple example:- TFormula *form1 = new TFormula("form1","sqrt(abs(x))"); cout << "Value of func at 2 is: " << form1->Eval(2) << endl; cout << "Value of func at -45 is: " << form1->Eval(-45) << endl; and then ask the object about itself:- form1->Print(); Here is a trivial example of a 3 dimensional function:- TFormula *my_formula3 = new TFormula("my_formula","x + 2*y + 3*z"); my_formula3->Eval(4.,5.,6); which should yield the value 32. For more details see TFormula

Functions: TF1, TF2, TF3

Functions build on the concept of formulae:- As a demonstration of a TF1 object, create the file:- function.cxx containing:- { gROOT->Reset(); TCanvas *c1 = new TCanvas("c1","Example with Formula",200,10,700,500); c1->SetGridx(); c1->SetGridy(); TF1 *fun1 = new TF1("fun1","abs(sin(x)/x)",0,10); fun1->Draw(); c1->Update(); } The TF1 object is is bounded between 0 and 10. Run ROOT and execute this macro.

As we shall see shortly the TF1, TF2, TF3 classes are used in histogram fitting. They inherit from TFormula and the parameters of the TFormula object are used as the fit parameters. Flexible though TFormula is, it can only describe a simple analytic form and there are times when the form of a fitted function needs to be more complex. To allow for this, TF1, TF2 and TF3 can take a user C defined function. The function definition is of the form:-

Double_t func( Double_t *x, Double_t *parm ); This appears to be a function that takes two pointers to Double_t (double precision) and returns a Double_t. In fact the 2 arguments are arrays. In C++, a single dimensional array is essentially a pointer to the first array element. The two arguments are:- As a simple example, consider the following:- Double_t my_c_func ( Double_t *x, Double_t *parm) { return parm[0] + parm[1]*x[0]; } void test_c_func() { TF1 *my_f1 = new TF1("my_f1", my_c_func, 0., 10., 2); my_f1->SetParameters(2.,3.); my_f1->Draw(); } This first defines a function called my_c_func that describes a straight line with parm[0] as the constant term and parm[1] as the slope:- Double_t my_c_func ( Double_t *x, Double_t *parm) { return parm[0] + parm[1]*x[0]; } Next comes a function to test this. It creates a TF1 object:- void test_c_func() { TF1 *my_f1 = new TF1("my_f1", my_c_func, 0., 10., 2); Note how the TF1 constructor is passed my_c_func as argument. C++ doesn't pass the functions as arguments, but whenever a function identifier appears without the trailing (), then the compiler knows that this is not a function call, and takes the reference to mean a pointer to function. Pointers to functions can get rather complicated, but all we need to know is that the TF1 object now has a pointer to my_c_func, and can call it when it needs to. The TF1 constructor is also passed the function limits 0., 10. and the number of parameters 2. It did not need the number of parameters when using a TFormula, it could determined it from the expression, but with a C function it has no way of knowing.

Now the parameters are loaded and finally the TF1 draws itself for the line 2 + 3x:-

my_f1->SetParameters(2.,3.); my_f1->Draw(); } To run the above:-
  1. Create a file called tf1.cxx containing the above.
  2. Run ROOT.
  3. Load the code: .L tf1.cxx
  4. Call the test function: test_c_func();

Fitting

One of the main uses for TF1, TF2 and TF3 are as fitting objects for histograms. To demonstrate this we will generate a histogram of the form:- exp(-0.5*x) and then fit it. This is how it is done:- { #include <math.h> gROOT->Reset(); my_canvas = new TCanvas("my_canvas","Histogram Example",200,10,600,400); // Generate the histogram TH1F *hist = new TH1F("hist","Exponential distribution",100,0.,5.); hist->SetFillColor(3); hist->Draw(); gRandom->SetSeed(); Float_t data; Int_t dummy; for ( Int_t i=0; i<10000; i++) { data = - log( gRandom->Rndm(dummy) ) / 0.5; hist->Fill(data); if ( i%500 == 0 ) { my_canvas->Modified(); my_canvas->Update(); } } // Fit the histogram. TF1 *my_func = new TF1("my_func","xexpo(0)",0.,5.); my_func->SetParameters(1.,-1.); my_func->Print(); hist->Fit( "my_func" ); my_func->Draw("same"); } The first part of the macro creates a TCanvas and a TH1F which it draws and fills with the required exponential. This is taken almost straight from the Histograms lesson. Next a TF1 object is created using a TFormula expression for an exponential in x:- TF1 *my_func = new TF1("my_func","xexpo(0)",0.,5.); The two parameters are set to some nominal initial value, and the Tf1 prints itself:- my_func->SetParameters(1.,-1.); my_func->Print(); Although the initial values are nominal, it does appear that they cannot be totally arbitrary, some values can cause fitter problems.

The next line is were all the action takes place:-

hist->Fit( "my_func" ); The histogram is asked to fit itself and is passed the TF1 object. Two points are worthy of note:- Finally, having had its parameters fitted, the TF1 object is asked to draw itself:- my_func->Draw("same"); The "same" argument, ensures that the canvas is not cleared so that the fit can be superimposed on the histogram. This is the final result:-

Summary


Go Back to the The First Steps Top Page


If you have any comments about this page please send them to Nick West