19.4 PyROOT and NumPy
Table of Contents
Converting ROOT data
When you use ROOT from Python through PyROOT, NumPy is usually the most convenient way to handle numerical data. The typical workflow is: read data from ROOT objects, convert the numbers to NumPy arrays, then use Python tools such as NumPy, SciPy, or pandas for further analysis.
The details of reading ROOT files and TTrees are explained elsewhere in this course. Here, the focus is on how to convert the data you already have in ROOT objects into NumPy arrays.
The exact tools you use depend on the kind of ROOT object and on the ROOT version you have.
Histograms to NumPy
ROOT histograms store bin contents and bin edges internally. In PyROOT you can access this information and construct NumPy arrays that represent the same histogram.
For 1D histograms, there are two useful views: bin contents and bin edges. Bin contents are usually what you want for numerical processing. Bin edges are useful if you plan to re-bin, integrate, or plot with Matplotlib.
A simple way to extract bin contents into a NumPy array is to loop over bins and fill a Python list, then convert the list to a NumPy array. You also need to remember that ROOT bin numbering starts at 1, with bin 0 as the underflow and bin $N\!+\!1$ as the overflow for a histogram with $N$ bins.
Important rule: For a TH1 histogram with $N$ bins, the valid "normal" bins are $1, \dots, N$. Bin 0 is underflow and bin $N+1$ is overflow. Never assume that bin 0 or bin $N+1$ is part of the main histogram range.
You can also obtain the bin edges. For a histogram with $N$ bins there are $N+1$ edges. Once you convert both contents and edges, you can use them with NumPy or Matplotlib. The exact code style can vary, but the pattern is always: loop over ROOT bins, read values, store to NumPy.
TTrees to NumPy
Converting a TTree branch to a NumPy array is very common. Conceptually, each branch is a column of data and each entry in the TTree is a row. When all entries of a single branch are converted to a NumPy array, you obtain a one dimensional array of length equal to the number of entries.
There are several approaches. A straightforward method is to loop over entries in PyROOT, read the branch value, and append it to a Python list, then convert to a NumPy array. This always works, regardless of ROOT version, but it is not the fastest option.
More efficient methods use modern interfaces such as RDataFrame with PyROOT or specific helpers available in your ROOT version. These can fill a NumPy array directly from a branch without an explicit Python loop. The details of RDataFrame are covered in another chapter. From the NumPy point of view, the important idea is that you end with arrays that contain the branch values, one element per entry.
If a TTree branch stores C++ vectors, the conversion is slightly different. Instead of a simple 1D array, you obtain an array of variable length arrays or lists. In such cases you usually have a Python list of per event arrays, or you flatten the data if it makes sense for your analysis.
When you convert several branches at once you can think of your result as a table of NumPy arrays, one array for each column. This is a convenient starting point for numerical analysis, plotting, or exporting data to other Python packages.
Graphs and other objects
You can also convert other ROOT objects to NumPy, such as TGraph or TGraphErrors. There, you usually extract separate arrays for the x coordinates, the y coordinates, and possibly the uncertainties. The pattern is the same: loop or use built in methods to access the ROOT data, then create NumPy arrays.
Once the data is in NumPy, you stop thinking in terms of ROOT objects and instead use Python and NumPy operations to process the arrays.
Working with NumPy arrays
Once your data is in NumPy arrays, you can use NumPy and the rest of the Python ecosystem as you would in any other scientific Python project. The ROOT part only provides the input, and NumPy handles the computation.
Basic array operations
A NumPy array is an efficient container for numbers. Arrays can be one dimensional, such as a list of measured energies, or multidimensional, such as a table of variables per event. Common operations include sums, means, standard deviations, and elementwise arithmetic.
Suppose you have an array $x$ of measured values. NumPy provides functions like $x.\text{mean}()$ and $x.\text{std}()$ for the mean and standard deviation. When you calculate simple statistics from ROOT data, it is often more convenient to convert to NumPy and use these functions.
Important rule: NumPy operations typically act on the entire array at once. You do not need explicit Python loops to add, multiply, or compare elements. Use vectorized operations such as $x + y$ or $x[x > 0]$ instead of manual loops.
You can combine several arrays, for instance, to compute a derived quantity from ROOT branches. If $E$ is an array of energies and $p$ is an array of momenta, you can compute a quotient element by element with a single operation.
Logical selections and masks
NumPy is particularly powerful for applying selection cuts. From ROOT, you may be used to expressions in TTree::Draw. The equivalent in NumPy uses boolean masks.
If you have a NumPy array representing some variable and you want to select only entries that satisfy a condition, you build a boolean array where each element is True or False according to the condition. You then index the original array with this boolean array to select only the desired elements.
For example, if $x$ contains a variable and you want values inside a given range, you create a mask like $(x > x_{\min}) \land (x < x_{\max})$ and use it to filter. You can create more complex combinations using logical operators. This technique parallels selection cuts in ROOT but happens entirely inside NumPy.
These boolean masks can be built from several arrays at once, which makes multi variable cuts straightforward. The logic is close to the "simple cuts" discussed in the TTree chapters, but expressed with NumPy syntax.
Histograms and statistics with NumPy
For quick checks, you can compute histograms directly from NumPy arrays, for instance with functions that return bin counts and edges. This can be useful if you want to compare ROOT histogram binning with a pure NumPy or Matplotlib version.
You can also compute statistics and uncertainties from NumPy directly, then compare them with the values stored by ROOT histograms, such as the mean and RMS. This offers an independent check of your ROOT based analysis.
Keep in mind that ROOT and NumPy may use slightly different definitions or default options for some operations. For example, there may be differences in how normalization is handled or in the definition of standard deviation for small samples. When comparing results, confirm that both sides use the same conventions.
Interoperability with other Python tools
Once your ROOT data is in NumPy arrays, it is easy to pass it to other libraries. You can use SciPy for advanced statistics or fitting, Matplotlib for plotting, pandas for table like analysis, or machine learning libraries such as scikit learn or TensorFlow.
From the perspective of NumPy, ROOT is simply one more data source. You read the data with PyROOT, convert to NumPy arrays, then hand those arrays to any Python tool you like.
This is particularly effective if you keep a clear boundary between I/O and computation. One practical pattern is to use PyROOT only in a small part of your script to read TTrees and produce NumPy arrays, then do the rest of the analysis in standard Python. This simplifies the code and makes it easier to reuse in non ROOT contexts.
Going back from NumPy to ROOT
Sometimes you want to convert NumPy arrays back into ROOT histograms or TTrees. For instance, after manipulating or filtering the data in NumPy, you might want to store the result as a ROOT file or plot it with ROOT plotting features.
The typical pattern is to create a ROOT histogram with some number of bins and range, then loop over the NumPy array and call Fill for each element. For TTrees, you can create branches that hold simple types or vector types and fill them entry by entry from your arrays.
Although this conversion is more mechanical than conceptually difficult, it emphasizes an important idea: PyROOT and NumPy together let you move data freely between the ROOT world and the Python scientific stack.
Views: 11
KAHIBARO