23.1. Separating Analysis and Plotting
Table of Contents
Why Separate Analysis and Plotting?
In ROOT analyses it is tempting to write one macro that opens a file, loops over a tree, fills histograms, draws them, styles them, and saves plots. This quickly leads to code that is hard to change, debug, or reuse. Separating analysis and plotting is a simple structuring principle that keeps your analysis flexible and maintainable.
At a high level, analysis code should read data, apply selections, compute derived quantities, and fill output objects such as histograms, TTrees, or summary numbers. Plotting code should take those already produced objects and turn them into visualizations with specific styling. Once you adopt this split, you can change your plots without ever touching the analysis logic, and you can rerun the analysis without regenerating all the figures.
A useful rule: Analysis code produces data products. Plotting code consumes data products.
This chapter focuses on how to implement this separation in ROOT and what practical patterns help you keep the two concerns apart.
Analysis as a Producer of Results
The central idea is that the analysis part of your code is responsible only for producing results in a machine readable form. It should not care about how those results will look in a paper or a presentation. The analysis step should typically do the following tasks.
First, it should read input. This usually means opening ROOT files and reading TTrees or RDataFrame sources, or importing data from text or CSV files. The analysis code should know where the input data are and how to interpret them, for example which branches correspond to which physical quantities.
Second, it should perform selections and compute derived variables. This includes applying cuts, computing new observables from existing branches, classifying events into categories, and generally turning raw detector or simulation output into physically meaningful quantities. This logic is usually the most domain specific part of your analysis and should be clearly separated so it can be reviewed and tested.
Third, it should fill output objects. In ROOT this typically means filling TH1, TH2, TProfile, TGraph, or possibly new TTrees with intermediate or final results. The key is that these objects should be filled without any concern for colors, marker styles, axis fonts, or final canvas layout. The analysis code should focus on what information is needed and in what binning or structure.
Finally, it should save results. A well separated analysis usually writes its outputs to one or more ROOT files. Each file can contain histograms, graphs, summary trees, and possibly small helper objects like TF1 fits for later use. Once saved, these files become an interface between analysis and plotting. Plotting scripts can be re-run many times using only these outputs, without repeating the costly analysis steps.
Important pattern: Analysis macros should end by writing well named ROOT objects to files, not by drawing plots on canvases.
Treat these output ROOT files as the products of your analysis pipeline. They should be stable, structured, and versioned so that any plotting step can rely on their content.
Plotting as a Separate Step
The plotting step should be a consumer of analysis results. It reads existing ROOT files and focuses purely on visual representation. This step should avoid any heavy computation and should not reimplement cuts or physics logic. Instead, it assumes that the input objects are already prepared correctly.
Typically the plotting code will open the output ROOT file from your analysis and retrieve the needed histograms, graphs, or other objects by name. It will then create canvases, arrange pads, set logarithmic axes, and apply styles to lines, markers, and fills. The plotting step is where you add axis titles, labels, legends, annotations, and adjust layout to produce publication quality figures.
A good plotting script also handles exporting plots in different formats such as PNG, PDF, or SVG, and possibly ROOT canvases. The script might loop over a list of histogram names and apply a common styling configuration, but it should never decide which events are signal or background or which selection should be applied. All that belongs to analysis.
If you later need to change line colors or marker types, you only modify plotting code. If you realize that a selection cut must be updated, you modify and rerun analysis code, then rerun the plotting step without touching its styling logic. This clear division keeps each kind of change localized.
Designing Analysis Macros
To support a clean separation you should design analysis macros with clear inputs, outputs, and responsibilities. An analysis macro typically has the following structure: it sets configuration options such as input file paths, tree names, and selection definitions, prepares output objects such as histograms or new trees, executes event loops or RDataFrame operations, and writes results to one or more ROOT files.
The analysis macro should keep a strict focus on numerical and logical tasks. When you create histograms, you choose their binning and ranges based on physics considerations, not on how the plot should look aesthetically. You can add descriptive titles and axis labels, but these can also be modified later in plotting scripts if needed.
If your analysis requires multiple steps, for example skimming from a large raw tree to a smaller tree and then producing histograms from the skim, each step can be implemented as its own macro. Each step reads data from previous outputs and writes new, reduced, or derived outputs. Plotting then attaches only to the final or near final results.
It is helpful to adopt a naming convention for the ROOT objects produced by analysis. Names should encode what the object represents, such as variables, selection region, and possibly dataset or systematic variation. Consistent naming makes later plotting scripts easier to write and read.
Keep in mind: Analysis macros may open many input files, but they should typically write a small number of well organized output files that summarize the results.
By designing analysis macros this way you create a stable interface for all later visualization and sharing of results.
Designing Plotting Macros
Plotting macros should be shorter, more focused, and easier to modify than analysis macros. Their main job is to transform existing ROOT objects into clear, consistent graphics. A plotting macro typically begins by opening one or more ROOT files that contain the results from analysis. It retrieves specific histograms, graphs, or profiles by name, checks that they exist, and then prepares canvases and pads.
Next the macro configures styles. This may involve setting global ROOT style options with TStyle, or applying object specific settings to each histogram or graph, such as line colors, marker styles, fill styles, and axis fonts. It will also create and position legends, add text labels and annotations, and set logarithmic scales on axes when needed.
Plotting macros often focus on comparing multiple related objects, for example signal and background histograms, or several datasets on one TMultiGraph. They need to manage overlaying plots correctly, choosing distinct styles, and ensuring that all elements are visible and interpretable.
Finally the macro will save each canvas to one or more output image formats. It can create directories for different groups of plots and use systematic naming for output files. Because it only works on precomputed objects, a plotting macro should run quickly, which makes it convenient to iterate on style and layout until you are satisfied.
Avoid adding physics decisions to plotting macros. They should not apply selection cuts on raw trees or recalculate weights. If you find yourself reapplying logic that belongs to analysis, it is better to extend the analysis macro to produce more tailored outputs.
Sharing and Reusing Results
A strong benefit of separating analysis and plotting is that it becomes straightforward to share your results with collaborators, and to reuse outputs in different contexts. If your analysis step produces self contained ROOT files with histograms and other objects, other people can write their own plotting scripts that load those files and create entirely different visualizations without repeating the analysis.
For example, one collaborator might produce detailed comparison plots, while another generates compact summary plots for presentations. Both rely on the same analysis outputs. As long as the object names and contents are stable, plotting scripts remain compatible.
This separation also helps when you need to combine results from multiple analyses. You can write a higher level plotting macro that reads histograms from several analysis outputs and combines or compares them. Because the heavy work is already done, you are free to experiment with many different visual combinations.
From a reproducibility standpoint, storing the analysis outputs as ROOT files creates a snapshot of the state of your results at a given time. Even if the underlying raw data or software environment changes, as long as you keep these intermediate products and your plotting code, you can regenerate the exact figures you used in a publication.
A practical rule: Treat analysis outputs in ROOT files as the reference data that any number of independent plotting scripts can use.
By designing your workflow this way you decouple numerical processing from visualization and give yourself and your collaborators more freedom in how to present the results.
Testing and Debugging with Separation
Separating analysis and plotting also makes testing and debugging easier. When an issue appears in a plot, you can often decide whether it originates from analysis or from plotting by inspecting the saved objects directly. You can open the output ROOT file, use Print, Draw, or other inspection tools, and verify whether the histogram contents match expectations before styling is applied.
If a histogram has unexpected bin contents, missing peaks, or wrong normalization, the bug is likely in the analysis step, such as in the event selection or weight application. You then focus your debugging on the event loop logic, branch reading, or RDataFrame filters. The plotting code is probably correct if it simply reads and displays what is already there.
In contrast, if the bin contents look correct but the plot is unreadable because of colors, axis ranges, or overlapping markers, then the issue lies in the plotting macro. You can fix styling or axis configuration without risking changes to the core physics analysis.
Because plotting macros run quickly, you can frequently test changes to line styles, legends, and annotations. Similarly, because analysis macros are isolated from plotting, you can introduce new histograms or branches and validate them with simple Draw calls before integrating them into more complex plotting scripts.
This modular debugging approach often saves time. Instead of searching a single large macro that does everything, you can narrow down the problem to either the data production side or the visualization side.
Automation and Batch Workflows
Once analysis and plotting are separated, automating your workflow becomes more straightforward. You can create simple shell scripts, Python wrappers, or ROOT macros that run the analysis step to produce updated ROOT output files, then run one or multiple plotting scripts that read these outputs and regenerate all figures.
This two step pipeline is particularly effective in batch environments or when using computing clusters. You can submit only the analysis part to the batch system, since it is the part that needs CPU time and access to large datasets. The plotting step can run locally later, using only the compact output files that the batch job created.
You can also schedule regular analysis runs, such as nightly jobs that process new data, and keep plotting scripts separate so you can iterate on the visual style at your own pace. Since plotting does not touch the raw data, it is safer to run many times on shared machines or laptops.
By structuring your ROOT project with clear analysis macros that output ROOT files and separate plotting macros that use them, you prepare your analysis for automation and make it easier to manage complex workflows over time.
Views: 11
KAHIBARO