First Steps: Formulae, Functions and Fitting
Creating and using TFormula objects. Creating and drawing TF1 function
objects. Fitting histograms.
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:-
- The names x, y, and z: These denote the 3 dimensions.
- The terms [0], [1], ... These are elements of a parameter
array.
Initially, when a TFormula object is
instantiated
they are all set to zero but can subsequently be set, individually or
collectively.
- Standard operators such as + - * / && || == != < <= > >= ...
- Standard functions such as sin, asin, sinh, exp, log ...
- Gausians, exponentials and power series that use both the dimensions
and the parameter array.
- Previously defined TFormula objects.
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:-
- They can be 1, 2 or 3 dimensional: TF1, TF2 and TF3 respectively.
- They are bounded in all dimensions.
- The underlying formula can either be a TFormula object or a user
written C function.
- They can draw, integrate and differentiate themselves.
- They can be passed to histogram objects for fitting.
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:-
- Array x - array containing x, (and possibly y and z - for 2D and 3D
functions).
- Array parm - array of fit parameters.
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:-
- Create a file called tf1.cxx containing the above.
- Run ROOT.
- Load the code: .L tf1.cxx
- 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
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:-
- Its not a pointer to TF1 object that is passed, but its name.
ROOT's ability to access objects by name was a subject dealt with in the
Inheritance
lesson. It so happens that in this case the name of the TF1 object (the first
arg in its constructor) is the same as the name of its pointer. That's
a common convention, but nothing more, its the object name that the
Fit() message requires.
- This is a miniature example of a typical OO program, passing one
object to another and then letting them interact to achieve a task. The
TF1 object "knows all about" functions, including how to draw them. The
TH1F object "knows all about histograms" including how to fit them using
the abstraction of a function which TF1 implements.
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:-
- TFormula are objects that encapsulate the concept of an expression
in x, y and z. The expression can involve user assignable parameters,
standard maths functions and operators. For example:-
TFormula *my_formula = new TFormula("my_formula","[0] + [1]*sin(x)");
my_formula->SetParameter(0, 2.);
my_formula->SetParameter(1, 3.);
my_formula->Eval(20.);
evaluates 2 + 3*sin(20.) The full list of supported expressions can be
found in
TFormula
- TF1, TF2 and TF3 represent 1, 2 and 3 dimensional functions that can
be based on TFormula objects or user written C functions. Functions can
draw, differentiate and integrate themselves and are used as the basis for
histogram fitting. For example:-
TF1 *fun1 = new TF1("fun1","abs(sin(x)/x)",0,10);
fun1->Draw()
- TF1, TF2 and TF3 objects whose Tformula, or user C function,
involves parameters can be passed to histogram's Fit() message:-
my_hist->Fit(my_tf1)
After fitting the TF1, TF2 and TF3 object's parameters hold the fit
result.
Go Back to the
The First Steps Top Page
If you have any comments about this page please send them to
Nick West