Walkthrough: Making a histogram with Analyze

(15 minutes)

Edit the file Analyze.C. In the Definitions section, insert the following code:

TH1* chi2Hist = NULL;


This means “define a new histogram pointer and call it chi2Hist.” Why define this as a pointer when plain ol’ variables are easier to use? The short answer is that ROOT uses pointers all the time; for example, if you want to read something from a file, you must always use pointers. The sooner you get used to pointers, the better.1

Don’t forget the semi-colons “;” at the ends of the lines! You can omit them in interactive commands, but not in macros.

In the Initialization section, insert the following code:

chi2Hist = new TH1D("chi2","Histogram of Chi2",100,0,20);


This means “set this pointer to a new histogram object.” We’re doing this here, instead of the Definitions section, because sometimes you want quantities like histogram limits to be variable rather than fixed; e.g., they depend on user input.

In the Loop section, put this in:



The first of these two lines means “get the ‘(entry+1)-th’ row from the TTree”; e.g., if entry is 100, get the 101st row from the n-tuple.2 Note that the variable entry comes from an argument to the Process method, so you don’t have to set it. This line will assign values to variables defined in the n-tuple: *ebeam, *chi2, and so on.3 In code prepared by MakeSelector, the variables extracted from an n-tuple are pointers; they have to be prefixed with “*” to access their values.

The second line means “in the histogram chi2Hist add 1 to a bin that corresponds to the value of *chi2.”

This goes in the Wrap-up section:



You already know what this does; you’ve used it before!

Save the file, quit and restart ROOT, then enter the same commands as before:

[] TFile myFile("experiment.root")
[] tree1->Process("Analyze.C")


Finally, we’ve made our first histogram with a C++ analysis macro. In the Initialization section, we defined a histogram; in the Loop section, we filled the histogram with values; in the Wrap-up section, we drew the histogram.

“What histogram? I don’t see anything!” Don’t forget: if you have the TBrowser open, you may need to click on the Canvas 1 tab.

How did I know which bin limits to use on chi2Hist? Before I wrote the code, I drew a test histogram with the command:

[] tree1->Draw("chi2")

Hmm, the histogram’s axes aren’t labeled. How do I put the labels in the macro? Here’s how I figured it out: I labeled the axes on the test histogram by right-clicking on them and selecting SetTitle. I saved the canvas by selecting File ‣ Save ‣ c1.C. I looked at c1.C and saw these commands in the file:

chi2->GetYaxis()->SetTitle("number of events");

I scrolled up and saw that ROOT had used the variable chi2 for the name of the histogram pointer. I copied the lines into Analyze.C, but used the name of my histogram instead:

chi2Hist->GetYaxis()->SetTitle("number of events");

Try this yourself: add the two lines above to the Initialization section, right after the line that defines the histogram. Test the revised Analyze class.


Why are we defining a pointer then setting it equal to NULL? I’m teaching you to avoid a common problem in programming: uninitialized variables. If we didn’t set chi2Hist to NULL, what would its value be? I don’t know. It would likely be set to zero, which is also the typical value of NULL. But this behavior varies between different C++ compilers. It’s better to be sure.

This is not an issue in the code we’re writing now, but in the future you’ll discover that uninitialized variables cause lots of crashes. Let’s get into good programming habits and avoid them from the start.

Some compilers offer another name for NULL: nullptr. They both have the same value, but for you one may be clearer than the other.


Actually, in the context of MakeSelector it means “get the data from the TTree pointed to by fReader.SetEntry(entry)”.

Note that when entry is 0, GetEntry will fetch the first row in the n-tuple; that’s why when entry is 100, GetEntry will fetch the 101st row in the n-tuple.


It’s mildly annoying that whenever you use MakeSelector to create an analysis skeleton, you must remember to put a GetEntry line. Since MakeSelector is doing everything else for us, why can’t it put in that one line too so we don’t have to remember?

The answer is that there’s more that can be done with the TSelector skeleton than we’re doing in this course; do a web search on “TSelector example” for some ideas. Since there are times when a simple line like GetEntry(entry) is not what you want, or you might create an analysis skeleton for one tree and use it on another, MakeSelector makes you put in the GetEntry line manually.