Create a 1D histogram:
TH1F hpx("hpx","random gaussian",30,-3,3);
Create a new canvas:
TCanvas c1("c1","Filling Example",600,600);
Fill histograms with random numbers following a Gaussian shape with mean 0 and width 1:
TRandom3 r;
int stats = 10000;
Float_t px;
for (Int_t i = 0; i < stats; i++) {
double value = r.Gaus(0.0,1.0);
hpx.Fill(value);
}
Always use TRandom3, which has the highest periodicity and is fast!
Draw histogram and canvas:
hpx.Draw();
c1.Draw();
How random is this? Do we get the same distribution if we run again?
TH1F hpx2("hpx2","random gaussian",30,-3,3);
for (Int_t i = 0; i < stats; i++) {
double value = r.Gaus(0.0,1.0);
hpx2.Fill(value);
}
And now draw again...
Note that you need to use the SAME
option in order to have it in the same canvas.
hpx2.Draw("SAME");
c1.Draw();
Apparently, it is random and the distributions look different, why is that?
Any random number generator needs a seed, by default
r.SetSeed(0);
is used which means, the system time of the computer is used.
If you want to get the exact same random distributions you need to set the same seed for both generations:
//Reset histograms
hpx.Reset();
hpx2.Reset();
//Set a seed=1234
r.SetSeed(1234);
//Loop
for (Int_t i = 0; i < stats; i++) {
double value = r.Gaus(0.0,1.0);
hpx.Fill(value);
}
//Set a seed=1234
r.SetSeed(1234);
//Loop
for (Int_t i = 0; i < stats; i++) {
double value = r.Gaus(0.0,1.0);
hpx2.Fill(value);
}
//Draw histogram
hpx.Draw("SAME");
hpx2.Draw("SAME");
c1.Draw();
Ok, now we don't see any difference in the distributions. It is often very useful to reproduce results!
Create a 2D histogram:
TH2F hpxpy("hpxpy","py vs px",30,-3,3,30,-3,3);
Use a 2-dimensional Gaussian with mean 0, width 1 (Rannor), fill the 2D histogram and draw:
Float_t py;
for (Int_t i = 0; i < stats; i++) {
r.Rannor(px,py);
hpxpy.Fill(px,py);
}
hpxpy.Draw();
c1.Draw();
Now we just see the hit distribution but it is hard to tell how many events are in each bin. We can use the COLZ
draw option instead:
hpxpy.Draw("COLZ");
c1.Draw();
Now we can see from the colour code the number of entries.
Or we can even draw it in a 3D-style:
hpxpy.Draw("SURF1");
c1.Draw();
Two-dimensional histograms are sometimes difficult to read, so it often makes sense to look at the one-dimensional profile.
TProfile *hprof = new TProfile("hprof","Profile along px",30,-3,3,0,20);
hprof = hpxpy.ProfileX();
This is now a pointer to an object! We need to use ->
instead of .
hprof->Draw();
c1.Draw();
Well, that makes sense, the mean should be zero!
Assume that we measure a variable y vs x for a certain number of points n and we have an uncertainty on the measurement of y:
const int n=10;
double x[n] = {1,2,3,4,5,6,7,8,9,10};
double y[n] = {6,12,14,20,22,24,35,45,44,53};
double y_err[n]= {5,5,4.7,4.5,4.2,5.1,2.9,4.1,4.8,5.43};
We can then display our measurement using TGraphErrors, which allows for symmetric uncerstainties.
By using the APE
draw option the graph is shown with a new axis (A) markers (P) and errors (E).
TGraphErrors graph(n, x, y, nullptr, y_err);
graph.Draw("APE");
c1.Draw();
Define a linear function using TF1 and fit the Graph
TF1 *f = new TF1("linear","[0]+x*[1]",.5,10.5);
graph.Fit(f);
f->Draw("SAME");
c1.Draw();
Open ROOT file with option RECREATE
, save all objects we have created in it and close it:
TFile hfile("test.root","RECREATE","Test ROOT file");
hpx.Write();
hpx2.Write();
hpxpy.Write();
hprof->Write();
graph.Write();
hfile.Write();
hfile.Close();
Now we can have a look at the content:
.! rootls -l test.root
Similarly, we can open an exisiting root file with option READ
:
TFile *fSig = new TFile("Signal_1fb.root", "READ");
Information is often stored in so called Trees
which can contain Branches
and Leaves
representing the content.
We can retrieve a tree using its name "tree":
TTree *tSig = (TTree*)fSig->Get("tree");
To get the content of the tree we need to link a branch variable invariantMass
to a local variable mass
:
double mass;
tSig->SetBranchAddress("invariantMass", &mass);
We now create a new histogram in order to plot the content and get the number of entries of the tree.
TH1F *hSig = new TH1F("signal", "", 50, 100, 160);
int nEntries_Sig = tSig -> GetEntries();
One entry of a tree can often be understood as one event with many different properties stored in the branches. To fill the histogram with all events, we need to loop over the tree:
for(int i = 0; i < nEntries_Sig; ++i){
tSig->GetEntry(i);
hSig->Fill(mass);
}
Finally, we can draw the histogram on the canvas:
hSig->Draw();
c1.Draw();
Last, but not least we want to create a stacked histgram, which are often used to show the different process contributions in simulations.
THStack hstack("stack","Stacked histograms");
We can now simply add the created signal histogram to the THStack
object:
hstack.Add(hSig);
Ok, nice but to have a stacked histogram we need another histogram, let's read in the background and fill a histogram:
TFile *fBkg = new TFile("Background_1fb.root", "READ");
TTree *tBkg = (TTree*) fBkg -> Get("tree");
TH1F *hBkg = new TH1F("bkg","", 50, 100, 160);
double mass_bkg;
tBkg->SetBranchAddress("invariantMass", &mass_bkg);
int nEntries_Bkg = tBkg -> GetEntries();
for(int i = 0; i < nEntries_Bkg; ++i){
tBkg->GetEntry(i);
hBkg->Fill(mass_bkg);
}
Now we can also add it to the stack, fill the histograms with color and draw it:
hstack.RecursiveRemove(hSig);
hstack.Add(hBkg);
hstack.Add(hSig);
hBkg->SetFillColor(kBlue);
hBkg->SetLineColor(kBlack);
hSig->SetFillColor(kRed);
hSig->SetLineColor(kBlack);
hstack.Draw();
c1.Draw();