19.2 ROOT Objects in Python
Table of Contents
Histograms
In PyROOT, histogram classes such as TH1F, TH1D, TH2F, and others are exactly the same C++ classes you use in the ROOT C++ interface. The difference is only in how you create and manipulate them from Python syntax.
You first need to import ROOT in Python, usually as:
import ROOTTo create a simple one dimensional float histogram with 100 bins between 0 and 10, you write:
h = ROOT.TH1F("h", "Example histogram;X axis;Entries", 100, 0.0, 10.0)The arguments are the same as in C++: internal name, title, number of bins, lower edge, upper edge. Axis titles are separated by semicolons in the string.
Filling a histogram uses the same Fill method:
h.Fill(3.2)
h.Fill(7.5, 2.0) # with weight 2.0You can also fill histograms inside Python loops. For example, if you want to fill a histogram with random Gaussian numbers using ROOT’s random generator:
rand = ROOT.TRandom3(0) # seed
for _ in range(100000):
value = rand.Gaus(0.0, 1.0)
h.Fill(value)You can access basic properties with the familiar methods:
entries = h.GetEntries()
mean = h.GetMean()
rms = h.GetRMS()These methods return Python float or int values through the automatic C++ to Python conversion.
To draw the histogram you can call:
c = ROOT.TCanvas("c", "c", 800, 600)
h.Draw()
c.Update()
If you work in a Python shell that understands GUI event loops (for example python -i with ROOT graphics enabled), the canvas window will appear as in C++. In a Jupyter notebook, you often need to use the built in display integration:
%jsroot on
h.Draw()and the current canvas is shown inline.
You can change histogram style from Python using the same methods: for instance
h.SetLineColor(ROOT.kRed)
h.SetLineWidth(2)
h.SetFillColor(ROOT.kBlue - 10)
The constant color names, such as kRed, are available in the ROOT module.
Saving histograms to ROOT files is also identical to C++:
f = ROOT.TFile("histos.root", "RECREATE")
h.Write()
f.Close()
When you read them back, you use TFile.Get which returns a ROOT object that behaves as a histogram in Python:
f = ROOT.TFile("histos.root")
h2 = f.Get("h")
h2.Draw()One important practical detail in PyROOT is object lifetime. If you create a histogram in a function and do not keep a Python reference to it, it may be destroyed by Python’s garbage collector, which can lead to confusion when drawing. A simple pattern is to keep histograms in a Python list or dictionary that lives long enough:
histos = []
def make_histo(name):
h = ROOT.TH1F(name, name, 50, 0.0, 5.0)
histos.append(h)
return hThis way, the histogram objects remain valid while you use them.
Always keep a Python reference to histograms that you plan to draw or save. If the last Python reference disappears, the object can be deleted even if a canvas still shows it.
Graphs
TGraph and its variants can also be used from Python without special wrapping code. All the C++ constructors and methods are available through the ROOT module.
The simplest way to create a TGraph from Python lists or arrays is to use ROOT’s constructor that accepts the number of points and C-style arrays. PyROOT automatically converts many Python containers into the required form. For example:
import array
import ROOT
x_vals = array.array("d", [0.0, 1.0, 2.0, 3.0])
y_vals = array.array("d", [0.1, 0.9, 2.2, 3.1])
g = ROOT.TGraph(len(x_vals), x_vals, y_vals)
g.SetTitle("Simple graph;X;Y")
Here the array module from the Python standard library provides a C-style array of doubles ("d"). For TGraph and TGraphErrors this is a reliable way to pass data.
If you already use NumPy in your Python analysis, you can also pass NumPy arrays in many cases, because PyROOT can interpret them as the required pointer types:
import numpy as np
x_vals = np.linspace(0, 3, 4, dtype="float64")
y_vals = np.array([0.1, 0.9, 2.2, 3.1], dtype="float64")
g = ROOT.TGraph(len(x_vals), x_vals, y_vals)Once the graph exists, you style it and draw it:
g.SetMarkerStyle(20)
g.SetMarkerColor(ROOT.kBlue)
g.SetLineColor(ROOT.kBlue)
g.Draw("AP") # Axes + Points
For graphs with errors, you can use TGraphErrors or TGraphAsymmErrors. The pattern is similar, but you provide error arrays in addition to x and y:
ex = array.array("d", [0.0, 0.0, 0.0, 0.0])
ey = array.array("d", [0.05, 0.08, 0.10, 0.07])
ge = ROOT.TGraphErrors(len(x_vals), x_vals, y_vals, ex, ey)
ge.SetTitle("Measurements with errors;X;Y")
ge.SetMarkerStyle(21)
ge.Draw("AP")When you read graphs from ROOT files in Python, the returned objects behave exactly as in C++:
f = ROOT.TFile("graphs.root")
g2 = f.Get("mygraph")
g2.Draw("AL")
You can combine several graphs into a TMultiGraph in Python, just as in C++:
mg = ROOT.TMultiGraph()
mg.Add(g)
mg.Add(ge)
mg.Draw("AP")
mg.GetXaxis().SetTitle("X")
mg.GetYaxis().SetTitle("Y")
As with histograms, keep Python references to graphs you want to keep. If you construct small arrays inside a function and pass them into TGraph, they should outlive the graph construction, but you usually do not need to keep explicit references to the arrays after that, because the graph copies the data internally. However, you still need to keep a reference to the graph object itself.
Keep a Python reference to graphs you need to draw or modify later. A graph without a live Python variable can be deleted by the garbage collector, which may lead to missing plots or crashes.
Canvases
TCanvas is the main drawing surface in ROOT and you use it from Python in the same way as in C++. Creating a canvas is straightforward:
import ROOT
c1 = ROOT.TCanvas("c1", "My canvas", 800, 600)You can then draw histograms and graphs on this canvas:
h.Draw()
c1.Update()In an interactive Python session, the canvas window appears and behaves exactly like in the C++ ROOT session. You can divide the canvas into pads from Python:
c1.Divide(2, 2)
c1.cd(1)
h.Draw()
c1.cd(2)
g.Draw("AP")
Each pad is selected with cd before drawing. This works identically in PyROOT.
You can also set canvas properties from Python. For example:
c1.SetGrid()
c1.SetLogy()Saving canvases to files is especially convenient in Python scripts:
c1.SaveAs("plot.png")
c1.SaveAs("plot.pdf")
If you work in Jupyter notebooks, ROOT integrates with the notebook display. With %jsroot on activated, each call to Draw shows the current canvas inline. Sometimes you will want to explicitly show the current canvas:
from ROOT import gPad
gPad.Update()In batch scripts that run without a graphical display, you can tell ROOT not to open any windows:
ROOT.gROOT.SetBatch(True)
c1 = ROOT.TCanvas("c1", "c1", 800, 600)
h.Draw()
c1.SaveAs("hist.png")In this mode, the canvas still exists and can be saved to files, but no window is shown.
Management of canvas lifetimes is again important in PyROOT. If the last Python variable that refers to a canvas is removed, the canvas can be destroyed, which may close the window unexpectedly. Usually you keep a canvas variable at module scope or inside a controlling object so that it survives as long as needed.
Finally, you can connect Python logic with GUI actions by using ROOT’s event loop functions. For quick analysis it is more common to use PyROOT in a script-like style, but the full graphical capabilities remain available.
When scripting with PyROOT, set ROOT.gROOT.SetBatch(True) if you run on a server or in a non graphical environment. Otherwise ROOT may try to open windows and your job can hang or crash.
Views: 14
KAHIBARO