Logo ROOT   6.19/01
Reference Guide
RDataFrame.cxx
Go to the documentation of this file.
1 // Author: Enrico Guiraud, Danilo Piparo CERN 12/2016
2 
3 /*************************************************************************
4  * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
5  * All rights reserved. *
6  * *
7  * For the licensing terms see $ROOTSYS/LICENSE. *
8  * For the list of contributors see $ROOTSYS/README/CREDITS. *
9  *************************************************************************/
10 
11 #include <algorithm>
12 #include <stdexcept>
13 
14 #include "ROOT/RDataFrame.hxx"
15 #include "ROOT/RDataSource.hxx"
16 #include "TChain.h"
17 #include "TDirectory.h"
18 
19 // clang-format off
20 /**
21 * \class ROOT::RDataFrame
22 * \ingroup dataframe
23 * \brief ROOT's RDataFrame offers a high level interface for analyses of data stored in `TTree`s, CSV's and other data formats.
24 
25 In addition, multi-threading and other low-level optimisations allow users to exploit all the resources available
26 on their machines completely transparently.<br>
27 Skip to the [class reference](#reference) or keep reading for the user guide.
28 
29 In a nutshell:
30 ~~~{.cpp}
31 ROOT::EnableImplicitMT(); // Tell ROOT you want to go parallel
32 ROOT::RDataFrame d("myTree", "file_*.root"); // Interface to TTree and TChain
33 auto myHisto = d.Histo1D("Branch_A"); // This happens in parallel!
34 myHisto->Draw();
35 ~~~
36 
37 Calculations are expressed in terms of a type-safe *functional chain of actions and transformations*, `RDataFrame` takes
38 care of their execution. The implementation automatically puts in place several low level optimisations such as
39 multi-thread parallelisation and caching.
40 
41 \htmlonly
42 <a href="https://doi.org/10.5281/zenodo.260230"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.260230.svg"
43 alt="DOI"></a>
44 \endhtmlonly
45 
46 ## For the impatient user
47 You can directly see RDataFrame in action through its [code examples](https://root.cern.ch/doc/master/group__tutorial__dataframe.html), both in C++ and Python.
48 
49 ## Table of Contents
50 - [Cheat sheet](#cheatsheet)
51 - [Introduction](#introduction)
52 - [Crash course](#crash-course)
53 - [More features](#more-features)
54 - [Transformations](#transformations) -- manipulating data
55 - [Actions](#actions) -- getting results
56 - [Parallel execution](#parallel-execution) -- how to use it and common pitfalls
57 - [Class reference](#reference) -- most methods are implemented in the [RInterface](https://root.cern/doc/master/classROOT_1_1RDF_1_1RInterface.html) base class
58 
59 ## <a name="cheatsheet"></a>Cheat sheet
60 These are the operations which can be performed with RDataFrame
61 
62 ### Transformations
63 Transformations are a way to manipulate the data.
64 
65 | **Transformation** | **Description** |
66 |------------------|--------------------|
67 | [Define](classROOT_1_1RDF_1_1RInterface.html#a7d48eb23b4378e99ebccb35e94ad025a) | Creates a new column in the dataset. |
68 | [DefineSlot](classROOT_1_1RDF_1_1RInterface.html#acaacf727b8a41d27c6bb4513348ac892) | Same as `Define`, but the user-defined function must take an extra `unsigned int slot` as its first parameter. `slot` will take a different value, `0` to `nThreads - 1`, for each thread of execution. This is meant as a helper in writing thread-safe `Define` transformation when using `RDataFrame` after `ROOT::EnableImplicitMT()`. `DefineSlot` works just as well with single-thread execution: in that case `slot` will always be `0`. |
69 | [DefineSlotEntry](classROOT_1_1RDF_1_1RInterface.html#a4f17074d5771916e3df18f8458186de7) | Same as `DefineSlot`, but the entry number is passed in addition to the slot number. This is meant as a helper in case some dependency on the entry number needs to be honoured. |
70 | [Filter](classROOT_1_1RDF_1_1RInterface.html#a70284a3bedc72b19610aaa91b5007ebd) | Filter the rows of the dataset. |
71 | [Range](classROOT_1_1RDF_1_1RInterface.html#a1b36b7868831de2375e061bb06cfc225) | Creates a node that filters entries based on range of entries |
72 
73 ### Actions
74 Actions are a way to produce a result out of the data. Each one is described in more detail in the reference guide.
75 
76 In the following, whenever we say an action "returns" something, we always mean it returns a smart pointer to it. Also
77 note that all actions are only executed for events that pass all preceding filters.
78 
79 Lazy actions only trigger the event loop when one of the results is accessed for the first time, making it easy to
80 produce several different results in one event loop. Instant actions trigger the event loop instantly.
81 
82 
83 | **Lazy action** | **Description** |
84 |------------------|-----------------|
85 | [Aggregate](classROOT_1_1RDF_1_1RInterface.html#ae540b00addc441f9b504cbae0ef0a24d) | Execute a user-defined accumulation operation on the processed column values. |
86 | [Book](classROOT_1_1RDF_1_1RInterface.html#a9b2f61f3333d1669e57055b9ae8be9d9) | Book execution of a custom action using a user-defined helper object. |
87 | [Cache](classROOT_1_1RDF_1_1RInterface.html#aaaa0a7bb8eb21315d8daa08c3e25f6c9) | Caches in contiguous memory columns' entries. Custom columns can be cached as well, filtered entries are not cached. Users can specify which columns to save (default is all). |
88 | [Count](classROOT_1_1RDF_1_1RInterface.html#a37f9e00c2ece7f53fae50b740adc1456) | Return the number of events processed. |
89 | [Display](classROOT_1_1RDF_1_1RInterface.html#aee68f4411f16f00a1d46eccb6d296f01) | Obtains the events in the dataset for the requested columns. The method returns a [RDisplay](classROOT_1_1RDF_1_1RDisplay.html) instance which can be queried to get a compressed tabular representation on the standard output or a complete representation as a string. |
90 | [Fill](classROOT_1_1RDF_1_1RInterface.html#a0cac4d08297c23d16de81ff25545440a) | Fill a user-defined object with the values of the specified branches, as if by calling `Obj.Fill(branch1, branch2, ...). |
91 | [Graph](classROOT_1_1RDF_1_1RInterface.html#a804b466ebdbddef5c7e3400cc6b89301) | Fills a TGraph with the two columns provided. If Multithread is enabled, the order of the points may not be the one expected, it is therefore suggested to sort if before drawing. |
92 | [Histo{1D,2D,3D}](classROOT_1_1RDF_1_1RInterface.html#a247ca3aeb7ce5b95015b7fae72983055) | Fill a {one,two,three}-dimensional histogram with the processed branch values. |
93 | [Max](classROOT_1_1RDF_1_1RInterface.html#a057179b1e77599466a0b02200d5cd8c3) | Return the maximum of processed branch values. If the type of the column is inferred, the return type is `double`, the type of the column otherwise.|
94 | [Mean](classROOT_1_1RDF_1_1RInterface.html#ade6b020284f2f4fe9d3b09246b5f376a) | Return the mean of processed branch values.|
95 | [Min](classROOT_1_1RDF_1_1RInterface.html#a7005702189e601972b6d19ecebcdc80c) | Return the minimum of processed branch values. If the type of the column is inferred, the return type is `double`, the type of the column otherwise.|
96 | [Profile{1D,2D}](classROOT_1_1RDF_1_1RInterface.html#a8ef7dc16b0e9f7bc9cfbe2d9e5de0cef) | Fill a {one,two}-dimensional profile with the branch values that passed all filters. |
97 | [Reduce](classROOT_1_1RDF_1_1RInterface.html#a118e723ae29834df8f2a992ded347354) | Reduce (e.g. sum, merge) entries using the function (lambda, functor...) passed as argument. The function must have signature `T(T,T)` where `T` is the type of the branch. Return the final result of the reduction operation. An optional parameter allows initialization of the result object to non-default values. |
98 | [Report](classROOT_1_1RDF_1_1RInterface.html#a94f322531dcb25beb8f53a602e5d6332) | Obtains statistics on how many entries have been accepted and rejected by the filters. See the section on [named filters](#named-filters-and-cutflow-reports) for a more detailed explanation. The method returns a RCutFlowReport instance which can be queried programmatically to get information about the effects of the individual cuts. |
99 | [StdDev](classROOT_1_1RDF_1_1RInterface.html#a482c4e4f81fe1e421c016f89cd281572) | Return the unbiased standard deviation of the processed branch values. |
100 | [Sum](classROOT_1_1RDF_1_1RInterface.html#a61d03407459120df6749af43ed506891) | Return the sum of the values in the column. If the type of the column is inferred, the return type is `double`, the type of the column otherwise. |
101 | [Take](classROOT_1_1RDF_1_1RInterface.html#a4fd694773a2931b6b07737ddcd1e73b4) | Extract a column from the dataset as a collection of values. If the type of the column is a C-style array, the type stored in the return container is a `ROOT::VecOps::RVec<T>` to guarantee the lifetime of the data involved. |
102 
103 | **Instant action** | **Description** |
104 |---------------------|-----------------|
105 | [Foreach](classROOT_1_1RDF_1_1RInterface.html#ad2822a7ccb8a9afdf3e5b2ea321886ca) | Execute a user-defined function on each entry. Users are responsible for the thread-safety of this lambda when executing with implicit multi-threading enabled. |
106 | [ForeachSlot](classROOT_1_1RDF_1_1RInterface.html#a3650ca30aae1ccd0d92bf3d680314129) | Same as `Foreach`, but the user-defined function must take an extra `unsigned int slot` as its first parameter. `slot` will take a different value, `0` to `nThreads - 1`, for each thread of execution. This is meant as a helper in writing thread-safe `Foreach` actions when using `RDataFrame` after `ROOT::EnableImplicitMT()`. `ForeachSlot` works just as well with single-thread execution: in that case `slot` will always be `0`. |
107 | [Snapshot](classROOT_1_1RDF_1_1RInterface.html#a233b7723e498967f4340705d2c4db7f8) | Writes processed data-set to disk, in a new `TTree` and `TFile`. Custom columns can be saved as well, filtered entries are not saved. Users can specify which columns to save (default is all). Snapshot, by default, overwrites the output file if it already exists. `Snapshot` can be made *lazy* setting the appropriate flage in the snapshot options.|
108 
109 
110 ### Other Operations
111 
112 | **Operation** | **Description** |
113 |---------------------|-----------------|
114 | [Alias](classROOT_1_1RDF_1_1RInterface.html#a31ca327e4a192dcc05a4aac240e1a725) | Introduce an alias for a particular column name. |
115 | [GetColumnNames](classROOT_1_1RDF_1_1RInterface.html#a951fe60b74d3a9fda37df59fd1dac186) | Get the names of all the available columns of the dataset. |
116 | [GetDefinedColumnNames](classROOT_1_1RDF_1_1RInterface.html#ad5c3fab8155aae8f614735df68430c58) | Get the names of all the defined columns |
117 | [GetColumnType](classROOT_1_1RDF_1_1RInterface.html#ad3ccd813d9fed014ae6a080411c5b5a8) | Return the type of a given column as a string. |
118 | [GetColumnTypeNamesList](classROOT_1_1RDF_1_1RInterface.html#a951fe60b74d3a9fda37df59fd1dac186) | Return the list of type names of columns in the dataset. |
119 | [GetFilterNames](classROOT_1_1RDF_1_1RInterface.html#a25026681111897058299161a70ad9bb2) | Get all the filters defined. If called on a root node, all filters will be returned. For any other node, only the filters upstream of that node. |
120 | [Display](classROOT_1_1RDF_1_1RInterface.html#a652f9ab3e8d2da9335b347b540a9a941) | Provides an ASCII representation of the columns types and contents of the dataset printable by the user. |
121 | [SaveGraph](namespaceROOT_1_1RDF.html#adc17882b283c3d3ba85b1a236197c533) | Store the computation graph of an RDataFrame in graphviz format for easy inspection. |
122 
123 
124 ## <a name="introduction"></a>Introduction
125 Users define their analysis as a sequence of operations to be performed on the data-frame object; the framework
126 takes care of the management of the loop over entries as well as low-level details such as I/O and parallelisation.
127 `RDataFrame` provides methods to perform most common operations required by ROOT analyses;
128 at the same time, users can just as easily specify custom code that will be executed in the event loop.
129 
130 `RDataFrame` is built with a *modular* and *flexible* workflow in mind, summarised as follows:
131 
132 1. **build a data-frame** object by specifying your data-set
133 2. **apply a series of transformations** to your data
134  1. **filter** (e.g. apply some cuts) or
135  2. **define** a new column (e.g. the result of an expensive computation on branches)
136 3. **apply actions** to the transformed data to produce results (e.g. fill a histogram)
137 
138 The following table shows how analyses based on `TTreeReader` and `TTree::Draw` translate to `RDataFrame`. Follow the
139 [crash course](#crash-course) to discover more idiomatic and flexible ways to express analyses with `RDataFrame`.
140 <table>
141 <tr>
142  <td>
143  <b>TTreeReader</b>
144  </td>
145  <td>
146  <b>ROOT::RDataFrame</b>
147  </td>
148 </tr>
149 <tr>
150  <td>
151 ~~~{.cpp}
152 TTreeReader reader("myTree", file);
153 TTreeReaderValue<A_t> a(reader, "A");
154 TTreeReaderValue<B_t> b(reader, "B");
155 TTreeReaderValue<C_t> c(reader, "C");
156 while(reader.Next()) {
157  if(IsGoodEvent(*a, *b, *c))
158  DoStuff(*a, *b, *c);
159 }
160 ~~~
161  </td>
162  <td>
163 ~~~{.cpp}
164 ROOT::RDataFrame d("myTree", file, {"A", "B", "C"});
165 d.Filter(IsGoodEvent).Foreach(DoStuff);
166 ~~~
167  </td>
168 </tr>
169 <tr>
170  <td>
171  <b>TTree::Draw</b>
172  </td>
173  <td>
174  <b>ROOT::RDataFrame</b>
175  </td>
176 </tr>
177 <tr>
178  <td>
179 ~~~{.cpp}
180 auto t = file->Get<TTree>("myTree");
181 t->Draw("x", "y > 2");
182 ~~~
183  </td>
184  <td>
185 ~~~{.cpp}
186 ROOT::RDataFrame d("myTree", file);
187 auto h = d.Filter("y > 2").Histo1D("x");
188 ~~~
189  </td>
190 </tr>
191 </table>
192 
193 ## <a name="crash-course"></a> Crash course
194 All snippets of code presented in the crash course can be executed in the ROOT interpreter. Simply precede them with
195 ~~~{.cpp}
196 using namespace ROOT; // RDataFrame's namespace
197 ~~~
198 which is omitted for brevity. The terms "column" and "branch" are used interchangeably.
199 
200 ### Creating a RDataFrame
201 RDataFrame's constructor is where the user specifies the dataset and, optionally, a default set of columns that
202 operations should work with. Here are the most common methods to construct a RDataFrame object:
203 ~~~{.cpp}
204 // single file -- all ctors are equivalent
205 TFile *f = TFile::Open("file.root");
206 auto t = f.Get<TTree>("treeName");
207 
208 RDataFrame d1("treeName", "file.root");
209 RDataFrame d2("treeName", f); // same as TTreeReader
210 RDataFrame d3(*t); // TTreeReader takes a pointer, RDF takes a reference
211 
212 // multiple files -- all ctors are equivalent
213 std::vector<std::string> files = {"file1.root", "file2.root"};
214 TChain chain("myTree");
215 chain.Add("file1.root");
216 chain.Add("file2.root");
217 
218 RDataFrame d4("myTree", {"file1.root", "file2.root"});
219 RDataFrame d5("myTree", files);
220 RDataFrame d6("myTree", "file*.root"); // see TRegexp's documentation for a list of valid regexes
221 RDataFrame d7(chain);
222 ~~~
223 Additionally, users can construct a RDataFrame specifying just an integer number. This is the number of "events" that
224 will be generated by this RDataFrame.
225 ~~~{.cpp}
226 RDataFrame d(10); // a RDF with 10 entries (and no columns/branches, for now)
227 d.Foreach([] { static int i = 0; std::cout << i++ << std::endl; }); // silly example usage: count to ten
228 ~~~
229 This is useful to generate simple data-sets on the fly: the contents of each event can be specified via the `Define`
230 transformation (explained below). For example, we have used this method to generate Pythia events (with a `Define`
231 transformation) and write them to disk in parallel (with the `Snapshot` action).
232 
233 ### Filling a histogram
234 Let's now tackle a very common task, filling a histogram:
235 ~~~{.cpp}
236 // Fill a TH1D with the "MET" branch
237 RDataFrame d("myTree", "file.root");
238 auto h = d.Histo1D("MET");
239 h->Draw();
240 ~~~
241 The first line creates a `RDataFrame` associated to the `TTree` "myTree". This tree has a branch named "MET".
242 
243 `Histo1D` is an *action*; it returns a smart pointer (a `RResultPtr` to be precise) to a `TH1D` histogram filled
244 with the `MET` of all events. If the quantity stored in the branch is a collection (e.g. a vector or an array), the
245 histogram is filled with its elements.
246 
247 You can use the objects returned by actions as if they were pointers to the desired results. There are many other
248 possible [actions](#overview), and all their results are wrapped in smart pointers; we'll see why in a minute.
249 
250 ### Applying a filter
251 Let's say we want to cut over the value of branch "MET" and count how many events pass this cut. This is one way to do it:
252 ~~~{.cpp}
253 RDataFrame d("myTree", "file.root");
254 auto c = d.Filter("MET > 4.").Count();
255 std::cout << *c << std::endl;
256 ~~~
257 The filter string (which must contain a valid c++ expression) is applied to the specified branches for each event;
258 the name and types of the columns are inferred automatically. The string expression is required to return a `bool`
259 which signals whether the event passes the filter (`true`) or not (`false`).
260 
261 You can think of your data as "flowing" through the chain of calls, being transformed, filtered and finally used to
262 perform actions. Multiple `Filter` calls can be chained one after another.
263 
264 Using string filters is nice for simple things, but they are limited to specifying the equivalent of a single return
265 statement or the body of a lambda, so it's cumbersome to use strings with more complex filters. They also add a small
266 runtime overhead, as ROOT needs to just-in-time compile the string into C++ code. When more freedom is required or
267 runtime performance is very important, a C++ callable can be specified instead (a lambda in the following snippet,
268 but it can be any kind of function or even a functor class), together with a list of branch names.
269 This snippet is analogous to the one above:
270 ~~~{.cpp}
271 RDataFrame d("myTree", "file.root");
272 auto metCut = [](double x) { return x > 4.; }; // a c++11 lambda function checking "x > 4"
273 auto c = d.Filter(metCut, {"MET"}).Count();
274 std::cout << *c << std::endl;
275 ~~~
276 
277 An example of a more complex filter expressed as a string containing C++ code is shown below
278 
279 ~~~{.cpp}
280 RDataFrame d("myTree", "file.root");
281 auto df = d.Define("p", "std::array<double, 4> p{px, py, pz}; return p;")
282  .Filter("double p2 = 0.0; for (auto&& x : p) p2 += x*x; return sqrt(p2) < 10.0;");
283 ~~~
284 
285 The code snippet above defines a column `p` that is a fixed-size array using the component column names and then
286 filters on its magnitude by looping over its elements. It must be noted that the usage of strings to define columns
287 like the one above is a major advantage when using PyROOT. However, only constants and data coming from other columns
288 in the dataset can be involved in the code passed as a string. Local variables and functions cannot be used, since
289 the interpreter will not know how to find them. When capturing local state is necessary, a C++ callable can be used.
290 
291 More information on filters and how to use them to automatically generate cutflow reports can be found [below](#Filters).
292 
293 ### Defining custom columns
294 Let's now consider the case in which "myTree" contains two quantities "x" and "y", but our analysis relies on a derived
295 quantity `z = sqrt(x*x + y*y)`. Using the `Define` transformation, we can create a new column in the data-set containing
296 the variable "z":
297 ~~~{.cpp}
298 RDataFrame d("myTree", "file.root");
299 auto sqrtSum = [](double x, double y) { return sqrt(x*x + y*y); };
300 auto zMean = d.Define("z", sqrtSum, {"x","y"}).Mean("z");
301 std::cout << *zMean << std::endl;
302 ~~~
303 `Define` creates the variable "z" by applying `sqrtSum` to "x" and "y". Later in the chain of calls we refer to
304 variables created with `Define` as if they were actual tree branches/columns, but they are evaluated on demand, at most
305 once per event. As with filters, `Define` calls can be chained with other transformations to create multiple custom
306 columns. `Define` and `Filter` transformations can be concatenated and intermixed at will.
307 
308 As with filters, it is possible to specify new columns as string expressions. This snippet is analogous to the one above:
309 ~~~{.cpp}
310 RDataFrame d("myTree", "file.root");
311 auto zMean = d.Define("z", "sqrt(x*x + y*y)").Mean("z");
312 std::cout << *zMean << std::endl;
313 ~~~
314 Again the names of the branches used in the expression and their types are inferred automatically. The string must be
315 valid c++ and is just-in-time compiled by the ROOT interpreter, cling -- the process has a small runtime overhead.
316 
317 Previously, when showing the different ways a RDataFrame can be created, we showed a constructor that only takes a
318 number of entries a parameter. In the following example we show how to combine such an "empty" `RDataFrame` with `Define`
319 transformations to create a data-set on the fly. We then save the generated data on disk using the `Snapshot` action.
320 ~~~{.cpp}
321 RDataFrame d(100); // a RDF that will generate 100 entries (currently empty)
322 int x = -1;
323 auto d_with_columns = d.Define("x", [&x] { return ++x; })
324  .Define("xx", [&x] { return x*x; });
325 d_with_columns.Snapshot("myNewTree", "newfile.root");
326 ~~~
327 This example is slightly more advanced than what we have seen so far: for starters, it makes use of lambda captures (a
328 simple way to make external variables available inside the body of c++ lambdas) to act on the same variable `x` from
329 both `Define` transformations. Secondly we have *stored* the transformed data-frame in a variable. This is always
330 possible: at each point of the transformation chain, users can store the status of the data-frame for further use (more
331 on this [below](#callgraphs)).
332 
333 You can read more about defining new columns [here](#custom-columns).
334 
335 \image html RDF_Graph.png "A graph composed of two branches, one starting with a filter and one with a define. The end point of a branch is always an action."
336 
337 ### Running on a range of entries
338 It is sometimes necessary to limit the processing of the dataset to a range of entries. For this reason, the RDataFrame
339 offers the concept of ranges as a node of the RDataFrame chain of transformations; this means that filters, columns and
340 actions can be concatenated to and intermixed with `Range`s. If a range is specified after a filter, the range will act
341 exclusively on the entries passing the filter -- it will not even count the other entries! The same goes for a `Range`
342 hanging from another `Range`. Here are some commented examples:
343 ~~~{.cpp}
344 RDataFrame d("myTree", "file.root");
345 // Here we store a data-frame that loops over only the first 30 entries in a variable
346 auto d30 = d.Range(30);
347 // This is how you pick all entries from 15 onwards
348 auto d15on = d.Range(15, 0);
349 // We can specify a stride too, in this case we pick an event every 3
350 auto d15each3 = d.Range(0, 15, 3);
351 ~~~
352 Note that ranges are not available when multi-threading is enabled. More information on ranges is available
353 [here](#ranges).
354 
355 ### Executing multiple actions in the same event loop
356 As a final example let us apply two different cuts on branch "MET" and fill two different histograms with the "pt\_v" of
357 the filtered events.
358 By now, you should be able to easily understand what's happening:
359 ~~~{.cpp}
360 RDataFrame d("treeName", "file.root");
361 auto h1 = d.Filter("MET > 10").Histo1D("pt_v");
362 auto h2 = d.Histo1D("pt_v");
363 h1->Draw(); // event loop is run once here
364 h2->Draw("SAME"); // no need to run the event loop again
365 ~~~
366 `RDataFrame` executes all above actions by **running the event-loop only once**. The trick is that actions are not
367 executed at the moment they are called, but they are **lazy**, i.e. delayed until the moment one of their results is
368 accessed through the smart pointer. At that time, the event loop is triggered and *all* results are produced
369 simultaneously.
370 
371 It is therefore good practice to declare all your transformations and actions *before* accessing their results, allowing
372 `RDataFrame` to run the loop once and produce all results in one go.
373 
374 ### Going parallel
375 Let's say we would like to run the previous examples in parallel on several cores, dividing events fairly between cores.
376 The only modification required to the snippets would be the addition of this line *before* constructing the main
377 data-frame object:
378 ~~~{.cpp}
379 ROOT::EnableImplicitMT();
380 ~~~
381 Simple as that. More details are given [below](#parallel-execution).
382 
383 ## <a name="more-features"></a>More features
384 Here is a list of the most important features that have been omitted in the "Crash course" for brevity.
385 You don't need to read all these to start using `RDataFrame`, but they are useful to save typing time and runtime.
386 
387 ### Programmatically get the list of column names
388 The `GetColumnsNames()` method returns the list of valid column names for the dataset:
389 ~~~{.cpp}
390 RDataFrame d("myTree", "file.root");
391 std::vector<std::string> colNames = d.GetColumnNames();
392 ~~~
393 
394 ### Reading and manipulating collections
395 When using RDataFrame to read data from a ROOT file, users can specify that the type of a branch is `RVec<T>`
396 to indicate the branch is a c-style array, a `std::vector` or any other collection type associated to a
397 contiguous storage in memory.
398 
399 Column values of type `RVec<T>` perform no copy of the underlying array data
400 and offer a rich interface to operate on the array elements in a vectorised fashion.
401 
402 The `RVec<T>` type signals to RDataFrame that a special behaviour needs to be adopted when snapshotting
403 a dataset on disk. Indeed, if columns which are variable size C arrays are treated via the `RVec<T>`,
404 RDataFrame will correctly persistify them - if anything else is adopted, for example `std::span`, only
405 the first element of the array will be written.
406 
407 Learn more on [RVec](https://root.cern/doc/master/classROOT_1_1VecOps_1_1RVec.html).
408 
409 ### Callbacks
410 It's possible to schedule execution of arbitrary functions (callbacks) during the event loop.
411 Callbacks can be used e.g. to inspect partial results of the analysis while the event loop is running,
412 drawing a partially-filled histogram every time a certain number of new entries is processed, or event
413 displaying a progress bar while the event loop runs.
414 
415 For example one can draw an up-to-date version of a result histogram every 100 entries like this:
416 ~~~{.cpp}
417 auto h = tdf.Histo1D("x");
418 TCanvas c("c","x hist");
419 h.OnPartialResult(100, [&c](TH1D &h_) { c.cd(); h_.Draw(); c.Update(); });
420 h->Draw(); // event loop runs here, this `Draw` is executed after the event loop is finished
421 ~~~
422 
423 Callbacks are registered to a RResultPtr and must be callables that takes a reference to the result type as argument
424 and return nothing. RDataFrame will invoke registered callbacks passing partial action results as arguments to them
425 (e.g. a histogram filled with a part of the selected events).
426 
427 Read more on RResultPtr::OnPartialResult().
428 
429 ### Default branch lists
430 When constructing a `RDataFrame` object, it is possible to specify a **default column list** for your analysis, in the
431 usual form of a list of strings representing branch/column names. The default column list will be used as a fallback
432 whenever a list specific to the transformation/action is not present. RDataFrame will take as many of these columns as
433 needed, ignoring trailing extra names if present.
434 ~~~{.cpp}
435 // use "b1" and "b2" as default branches
436 RDataFrame d1("myTree", "file.root", {"b1","b2"});
437 auto h = d1.Filter([](int b1, int b2) { return b1 > b2; }) // will act on "b1" and "b2"
438  .Histo1D(); // will act on "b1"
439 
440 // just one default branch this time
441 RDataFrame d2("myTree", "file.root", {"b1"});
442 auto min = d2.Filter([](double b2) { return b2 > 0; }, {"b2"}) // we can still specify non-default branch lists
443  .Min(); // returns the minimum value of "b1" for the filtered entries
444 ~~~
445 
446 ### <a name="ImplicitColumns"></a> Implicit Columns
447 Every instance of `RDataFrame` is created with two special columns called `rdfentry_` and `rdfslot_`. The `rdfentry_`
448 column is an unsigned 64-bit integer holding the current entry number while `rdfslot_` is an unsigned 32-bit integer
449 holding the index of the current data processing slot.
450 For backwards compatibility reasons, the names `tdfentry_` and `tdfslot_` are also accepted.
451 These columns are not considered by operations such as [Cache](classROOT_1_1RDF_1_1RInterface.html#aaaa0a7bb8eb21315d8daa08c3e25f6c9)
452 or [Snapshot](classROOT_1_1RDF_1_1RInterface.html#a233b7723e498967f4340705d2c4db7f8). The _cached_ or _snapshot_ data frame
453 provides "its own" values for these columns which do not necessarily correspond to the ones of the mother data frame. This is
454 most notably the case where filters are used before deriving a cached/persistified dataframe.
455 
456 Note that in multi-thread event loops the values of `rdfentry_` _do not_ correspond to what would be the entry numbers
457 of a TChain constructed over the same set of ROOT files, as the entries are processed in an unspecified order.
458 
459 ### Branch type guessing and explicit declaration of branch types
460 C++ is a statically typed language: all types must be known at compile-time. This includes the types of the `TTree`
461 branches we want to work on. For filters, temporary columns and some of the actions, **branch types are deduced from the
462 signature** of the relevant filter function/temporary column expression/action function:
463 ~~~{.cpp}
464 // here b1 is deduced to be `int` and b2 to be `double`
465 dataFrame.Filter([](int x, double y) { return x > 0 && y < 0.; }, {"b1", "b2"});
466 ~~~
467 If we specify an incorrect type for one of the branches, an exception with an informative message will be thrown at
468 runtime, when the branch value is actually read from the `TTree`: `RDataFrame` detects type mismatches. The same would
469 happen if we swapped the order of "b1" and "b2" in the branch list passed to `Filter`.
470 
471 Certain actions, on the other hand, do not take a function as argument (e.g. `Histo1D`), so we cannot deduce the type of
472 the branch at compile-time. In this case **`RDataFrame` infers the type of the branch** from the `TTree` itself. This
473 is why we never needed to specify the branch types for all actions in the above snippets.
474 
475 When the branch type is not a common one such as `int`, `double`, `char` or `float` it is nonetheless good practice to
476 specify it as a template parameter to the action itself, like this:
477 ~~~{.cpp}
478 dataFrame.Histo1D("b1"); // OK, the type of "b1" is deduced at runtime
479 dataFrame.Min<MyNumber_t>("myObject"); // OK, "myObject" is deduced to be of type `MyNumber_t`
480 ~~~
481 
482 Deducing types at runtime requires the just-in-time compilation of the relevant actions, which has a small runtime
483 overhead, so specifying the type of the columns as template parameters to the action is good practice when performance is a goal.
484 
485 ### Generic actions
486 `RDataFrame` strives to offer a comprehensive set of standard actions that can be performed on each event. At the same
487 time, it **allows users to execute arbitrary code (i.e. a generic action) inside the event loop** through the `Foreach`
488 and `ForeachSlot` actions.
489 
490 `Foreach(f, columnList)` takes a function `f` (lambda expression, free function, functor...) and a list of columns, and
491 executes `f` on those columns for each event. The function passed must return nothing (i.e. `void`). It can be used to
492 perform actions that are not already available in the interface. For example, the following snippet evaluates the root
493 mean square of column "b":
494 ~~~{.cpp}
495 // Single-thread evaluation of RMS of column "b" using Foreach
496 double sumSq = 0.;
497 unsigned int n = 0;
498 RDataFrame d("bTree", bFilePtr);
499 d.Foreach([&sumSq, &n](double b) { ++n; sumSq += b*b; }, {"b"});
500 std::cout << "rms of b: " << std::sqrt(sumSq / n) << std::endl;
501 ~~~
502 When executing on multiple threads, users are responsible for the thread-safety of the expression passed to `Foreach`:
503 each thread will execute the expression multiple times (once per entry) in an unspecified order.
504 The code above would need to employ some resource protection mechanism to ensure non-concurrent writing of `rms`; but
505 this is probably too much head-scratch for such a simple operation.
506 
507 `ForeachSlot` can help in this situation. It is an alternative version of `Foreach` for which the function takes an
508 additional parameter besides the columns it should be applied to: an `unsigned int slot` parameter, where `slot` is a
509 number indicating which thread (0, 1, 2 , ..., poolSize - 1) the function is being run in. More specifically, RDataFrame
510 guarantees that `ForeachSlot` will invoke the user expression with different `slot` parameters for different concurrent
511 executions (there is no guarantee that a certain slot number will always correspond to a given thread id, though).
512 We can take advantage of `ForeachSlot` to evaluate a thread-safe root mean square of branch "b":
513 ~~~{.cpp}
514 // Thread-safe evaluation of RMS of branch "b" using ForeachSlot
515 ROOT::EnableImplicitMT();
516 const unsigned int nSlots = ROOT::GetImplicitMTPoolSize();
517 std::vector<double> sumSqs(nSlots, 0.);
518 std::vector<unsigned int> ns(nSlots, 0);
519 
520 RDataFrame d("bTree", bFilePtr);
521 d.ForeachSlot([&sumSqs, &ns](unsigned int slot, double b) { sumSqs[slot] += b*b; ns[slot] += 1; }, {"b"});
522 double sumSq = std::accumulate(sumSqs.begin(), sumSqs.end(), 0.); // sum all squares
523 unsigned int n = std::accumulate(ns.begin(), ns.end(), 0); // sum all counts
524 std::cout << "rms of b: " << std::sqrt(sumSq / n) << std::endl;
525 ~~~
526 You see how we created one `double` variable for each thread in the pool, and later merged their results via
527 `std::accumulate`.
528 
529 ### Friend trees
530 Friend trees are supported by RDataFrame.
531 In order to deal with friend trees with RDataFrame, the user is required to build
532 the tree and its friends and instantiate a RDataFrame with it.
533 ~~~{.cpp}
534 TTree t([...]);
535 TTree ft([...]);
536 t.AddFriend(ft, "myFriend");
537 
538 RDataFrame d(t);
539 auto f = d.Filter("myFriend.MyCol == 42");
540 ~~~
541 
542 ### Reading file formats different from ROOT's
543 RDataFrame can be interfaced with RDataSources. The RDataSource interface defines an API that RDataFrame can use to read arbitrary data formats.
544 
545 A concrete RDataSource implementation (i.e. a class that inherits from RDataSource and implements all of its pure
546 methods) provides an adaptor that RDataFrame can leverage to read any kind of tabular data formats.
547 RDataFrame calls into RDataSource to retrieve information about the data, retrieve (thread-local) readers or "cursors" for selected columns
548 and to advance the readers to the desired data entry.
549 Some predefined RDataSources are natively provided by ROOT such as the `RCsvDS` which allows to read comma separated files:
550 ~~~{.cpp}
551 auto tdf = ROOT::RDF::MakeCsvDataFrame("MuRun2010B.csv");
552 auto filteredEvents =
553  tdf.Filter("Q1 * Q2 == -1")
554  .Define("m", "sqrt(pow(E1 + E2, 2) - (pow(px1 + px2, 2) + pow(py1 + py2, 2) + pow(pz1 + pz2, 2)))");
555 auto h = filteredEvents.Histo1D("m");
556 h->Draw();
557 ~~~
558 
559 ### <a name="callgraphs"></a>Call graphs (storing and reusing sets of transformations)
560 **Sets of transformations can be stored as variables** and reused multiple times to create **call graphs** in which
561 several paths of filtering/creation of columns are executed simultaneously; we often refer to this as "storing the
562 state of the chain".
563 
564 This feature can be used, for example, to create a temporary column once and use it in several subsequent filters or
565 actions, or to apply a strict filter to the data-set *before* executing several other transformations and actions,
566 effectively reducing the amount of events processed.
567 
568 Let's try to make this clearer with a commented example:
569 ~~~{.cpp}
570 // build the data-frame and specify a default column list
571 RDataFrame d(treeName, filePtr, {"var1", "var2", "var3"});
572 
573 // apply a cut and save the state of the chain
574 auto filtered = d.Filter(myBigCut);
575 
576 // plot branch "var1" at this point of the chain
577 auto h1 = filtered.Histo1D("var1");
578 
579 // create a new branch "vec" with a vector extracted from a complex object (only for filtered entries)
580 // and save the state of the chain
581 auto newBranchFiltered = filtered.Define("vec", [](const Obj& o) { return o.getVector(); }, {"obj"});
582 
583 // apply a cut and fill a histogram with "vec"
584 auto h2 = newBranchFiltered.Filter(cut1).Histo1D("vec");
585 
586 // apply a different cut and fill a new histogram
587 auto h3 = newBranchFiltered.Filter(cut2).Histo1D("vec");
588 
589 // Inspect results
590 h2->Draw(); // first access to an action result: run event-loop!
591 h3->Draw("SAME"); // event loop does not need to be run again here..
592 std::cout << "Entries in h1: " << h1->GetEntries() << std::endl; // ..or here
593 ~~~
594 `RDataFrame` detects when several actions use the same filter or the same temporary column, and **only evaluates each
595 filter or temporary column once per event**, regardless of how many times that result is used down the call graph.
596 Objects read from each column are **built once and never copied**, for maximum efficiency.
597 When "upstream" filters are not passed, subsequent filters, temporary column expressions and actions are not evaluated,
598 so it might be advisable to put the strictest filters first in the chain.
599 
600 ### <a name="representgraph"></a>Printing the computation graph
601 It is possible to print the computation graph from any node to obtain a dot representation either on the standard output
602 or in a file.
603 
604 Invoking the function ROOT::RDF::SaveGraph() on any node that is not the head node, the computation graph of the branch
605 the node belongs to is printed. By using the head node, the entire computation graph is printed.
606 
607 Following there is an example of usage:
608 ~~~{.cpp}
609 // First, a sample computational graph is built
610 ROOT::RDataFrame df("tree", "f.root");
611 
612 auto df2 = df.Define("x", []() { return 1; })
613  .Filter("col0 % 1 == col0")
614  .Filter([](int b1) { return b1 <2; }, {"cut1"})
615  .Define("y", []() { return 1; });
616 
617 auto count = df2.Count();
618 
619 // Prints the graph to the rd1.dot file in the current directory
620 ROOT::RDF::SaveGraph(rd1, "./mydot.dot");
621 // Prints the graph to standard output
622 ROOT::RDF::SaveGraph(rd1);
623 ~~~
624 
625 ### RDataFrame variables as function arguments and return values
626 RDataFrame variables/nodes are relatively cheap to copy and it's possible to both pass them to (or move them into)
627 functions and to return them from functions. However, in general each dataframe node will have a different C++ type,
628 which includes all available compile-time information about what that node does. One way to cope with this complication
629 is to use template functions and/or C++14 auto return types:
630 ~~~{.cpp}
631 template <typename RDF>
632 auto ApplySomeFilters(RDF df)
633 {
634  return df.Filter("x > 0").Filter([](int y) { return y < 0; }, {"y"});
635 }
636 ~~~
637 
638 A possibly simpler, C++11-compatible alternative is to take advantage of the fact that any dataframe node can be
639 converted to the common type ROOT::RDF::RNode:
640 ~~~{.cpp}
641 // a function that conditionally adds a Range to a RDataFrame node.
642 RNode MaybeAddRange(RNode df, bool mustAddRange)
643 {
644  return mustAddRange ? df.Range(1) : df;
645 }
646 // use as :
647 ROOT::RDataFrame df(10);
648 auto maybeRangedDF = MaybeAddRange(df, true);
649 ~~~
650 
651 The conversion to ROOT::RDF::RNode is cheap, but it will introduce an extra virtual call during the RDataFrame event
652 loop (in most cases, the resulting performance impact should be negligible).
653 
654 As a final note, remember that RDataFrame actions do not return another dataframe, but a RResultPtr<T>, where T is the
655 type of the result of the action.
656 
657 Read more on this topic [here](https://root.cern.ch/doc/master/classROOT_1_1RDF_1_1RInterface.html#a6909f04c05723de79f97a14b092318b1).
658 
659 ## <a name="transformations"></a>Transformations
660 ### <a name="Filters"></a> Filters
661 A filter is defined through a call to `Filter(f, columnList)`. `f` can be a function, a lambda expression, a functor
662 class, or any other callable object. It must return a `bool` signalling whether the event has passed the selection
663 (`true`) or not (`false`). It must perform "read-only" actions on the columns, and should not have side-effects (e.g.
664 modification of an external or static variable) to ensure correct results when implicit multi-threading is active.
665 
666 `RDataFrame` only evaluates filters when necessary: if multiple filters are chained one after another, they are executed
667 in order and the first one returning `false` causes the event to be discarded and triggers the processing of the next
668 entry. If multiple actions or transformations depend on the same filter, that filter is not executed multiple times for
669 each entry: after the first access it simply serves a cached result.
670 
671 #### <a name="named-filters-and-cutflow-reports"></a>Named filters and cutflow reports
672 An optional string parameter `name` can be passed to the `Filter` method to create a **named filter**. Named filters
673 work as usual, but also keep track of how many entries they accept and reject.
674 
675 Statistics are retrieved through a call to the `Report` method:
676 
677 - when `Report` is called on the main `RDataFrame` object, it returns a RResultPtr<RCutFlowReport> relative to all
678 named filters declared up to that point
679 - when called on a specific node (e.g. the result of a `Define` or `Filter`), it returns a RResultPtr<RCutFlowReport>
680 relative all named filters in the section of the chain between the main `RDataFrame` and that node (included).
681 
682 Stats are stored in the same order as named filters have been added to the graph, and *refer to the latest event-loop*
683 that has been run using the relevant `RDataFrame`.
684 
685 ### <a name="ranges"></a>Ranges
686 When `RDataFrame` is not being used in a multi-thread environment (i.e. no call to `EnableImplicitMT` was made),
687 `Range` transformations are available. These act very much like filters but instead of basing their decision on
688 a filter expression, they rely on `begin`,`end` and `stride` parameters.
689 
690 - `begin`: initial entry number considered for this range.
691 - `end`: final entry number (excluded) considered for this range. 0 means that the range goes until the end of the dataset.
692 - `stride`: process one entry of the [begin, end) range every `stride` entries. Must be strictly greater than 0.
693 
694 The actual number of entries processed downstream of a `Range` node will be `(end - begin)/stride` (or less if less
695 entries than that are available).
696 
697 Note that ranges act "locally", not based on the global entry count: `Range(10,50)` means "skip the first 10 entries
698 *that reach this node*, let the next 40 entries pass, then stop processing". If a range node hangs from a filter node,
699 and the range has a `begin` parameter of 10, that means the range will skip the first 10 entries *that pass the
700 preceding filter*.
701 
702 Ranges allow "early quitting": if all branches of execution of a functional graph reached their `end` value of
703 processed entries, the event-loop is immediately interrupted. This is useful for debugging and quick data explorations.
704 
705 ### <a name="custom-columns"></a> Custom columns
706 Custom columns are created by invoking `Define(name, f, columnList)`. As usual, `f` can be any callable object
707 (function, lambda expression, functor class...); it takes the values of the columns listed in `columnList` (a list of
708 strings) as parameters, in the same order as they are listed in `columnList`. `f` must return the value that will be
709 assigned to the temporary column.
710 
711 A new variable is created called `name`, accessible as if it was contained in the dataset from subsequent
712 transformations/actions.
713 
714 Use cases include:
715 - caching the results of complex calculations for easy and efficient multiple access
716 - extraction of quantities of interest from complex objects
717 - branch aliasing, i.e. changing the name of a branch
718 
719 An exception is thrown if the `name` of the new column/branch is already in use for another branch in the `TTree`.
720 
721 It is also possible to specify the quantity to be stored in the new temporary column as a C++ expression with the method
722 `Define(name, expression)`. For example this invocation
723 
724 ~~~{.cpp}
725 tdf.Define("pt", "sqrt(px*px + py*py)");
726 ~~~
727 
728 will create a new column called "pt" the value of which is calculated starting from the columns px and py. The system
729 builds a just-in-time compiled function starting from the expression after having deduced the list of necessary branches
730 from the names of the variables specified by the user.
731 
732 #### Custom columns as function of slot and entry number
733 
734 It is possible to create custom columns also as a function of the processing slot and entry numbers. The methods that can
735 be invoked are:
736 - `DefineSlot(name, f, columnList)`. In this case the callable f has this signature `R(unsigned int, T1, T2, ...)`: the
737 first parameter is the slot number which ranges from 0 to ROOT::GetImplicitMTPoolSize() - 1.
738 - `DefineSlotEntry(name, f, columnList)`. In this case the callable f has this signature `R(unsigned int, ULong64_t,
739 T1, T2, ...)`: the first parameter is the slot number while the second one the number of the entry being processed.
740 
741 ## <a name="actions"></a>Actions
742 ### Instant and lazy actions
743 Actions can be **instant** or **lazy**. Instant actions are executed as soon as they are called, while lazy actions are
744 executed whenever the object they return is accessed for the first time. As a rule of thumb, actions with a return value
745 are lazy, the others are instant.
746 
747 ## <a name="parallel-execution"></a>Parallel execution
748 As pointed out before in this document, `RDataFrame` can transparently perform multi-threaded event loops to speed up
749 the execution of its actions. Users have to call `ROOT::EnableImplicitMT()` *before* constructing the `RDataFrame`
750 object to indicate that it should take advantage of a pool of worker threads. **Each worker thread processes a distinct
751 subset of entries**, and their partial results are merged before returning the final values to the user.
752 More specifically, the dataset will be divided in batches of entries, and threads will divide among themselves the
753 processing of these batches. There are no guarantees on the order the batches are processed, i.e. no guarantees in the
754 order entries of the dataset are processed. Note that this in turn means that, for multi-thread event loops, there is no
755 guarantee on the order in which `Snapshot` will _write_ entries: they could be scrambled with respect to the input dataset.
756 
757 ### Thread-safety of user-defined expressions
758 RDataFrame operations such as `Histo1D` or `Snapshot` are guaranteed to work correctly in multi-thread event loops.
759 User-defined expressions, such as strings or lambdas passed to `Filter`, `Define`, `Foreach`, `Reduce` or `Aggregate`
760 will have to be thread-safe, i.e. it should be possible to call them concurrently from different threads.
761 
762 Note that simple `Filter` and `Define` transformations will inherently satisfy this requirement: `Filter`/`Define`
763 expressions will often be *pure* in the functional programming sense (no side-effects, no dependency on external state),
764 which eliminates all risks of race conditions.
765 
766 In order to facilitate writing of thread-safe operations, some RDataFrame features such as `Foreach`, `Define` or `OnPartialResult`
767 offer thread-aware counterparts (`ForeachSlot`, `DefineSlot`, `OnPartialResultSlot`): their only difference is that they
768 will pass an extra `slot` argument (an unsigned integer) to the user-defined expression. When calling user-defined code
769 concurrently, `RDataFrame` guarantees that different threads will employ different values of the `slot` parameter,
770 where `slot` will be a number between 0 and `ROOT::GetImplicitMTPoolSize() - 1`.
771 In other words, within a slot, computation runs sequentially and events are processed sequentially.
772 Note that the same slot might be associated to different threads over the course of a single event loop, but two threads
773 will never receive the same slot at the same time.
774 This extra parameter might facilitate writing safe parallel code by having each thread write/modify a different
775 *processing slot*, e.g. a different element of a list. See [here](#generic-actions) for an example usage of `ForeachSlot`.
776 
777 <a name="reference"></a>
778 */
779 // clang-format on
780 
781 namespace ROOT {
782 namespace Detail {
783 namespace RDF {
784 class RCustomColumnBase;
785 }
786 } // namespace Detail
787 
789 using ColumnNamesPtr_t = std::shared_ptr<const ColumnNames_t>;
790 
791 namespace RDFInternal = ROOT::Internal::RDF;
792 
793 ////////////////////////////////////////////////////////////////////////////
794 /// \brief Build the dataframe
795 /// \param[in] treeName Name of the tree contained in the directory
796 /// \param[in] dirPtr TDirectory where the tree is stored, e.g. a TFile.
797 /// \param[in] defaultBranches Collection of default branches.
798 ///
799 /// The default branches are looked at in case no branch is specified in the
800 /// booking of actions or transformations.
801 /// See RInterface for the documentation of the methods available.
802 RDataFrame::RDataFrame(std::string_view treeName, TDirectory *dirPtr, const ColumnNames_t &defaultBranches)
803  : RInterface(std::make_shared<RDFDetail::RLoopManager>(nullptr, defaultBranches))
804 {
805  if (!dirPtr) {
806  auto msg = "Invalid TDirectory!";
807  throw std::runtime_error(msg);
808  }
809  const std::string treeNameInt(treeName);
810  auto tree = static_cast<TTree *>(dirPtr->Get(treeNameInt.c_str()));
811  if (!tree) {
812  auto msg = "Tree \"" + treeNameInt + "\" cannot be found!";
813  throw std::runtime_error(msg);
814  }
815  GetProxiedPtr()->SetTree(std::shared_ptr<TTree>(tree, [](TTree *) {}));
816 }
817 
818 ////////////////////////////////////////////////////////////////////////////
819 /// \brief Build the dataframe
820 /// \param[in] treeName Name of the tree contained in the directory
821 /// \param[in] filenameglob TDirectory where the tree is stored, e.g. a TFile.
822 /// \param[in] defaultBranches Collection of default branches.
823 ///
824 /// The filename globbing supports the same type of expressions as TChain::Add().
825 /// The default branches are looked at in case no branch is specified in the
826 /// booking of actions or transformations.
827 /// See RInterface for the documentation of the methods available.
828 RDataFrame::RDataFrame(std::string_view treeName, std::string_view filenameglob, const ColumnNames_t &defaultBranches)
829  : RInterface(std::make_shared<RDFDetail::RLoopManager>(nullptr, defaultBranches))
830 {
831  const std::string treeNameInt(treeName);
832  const std::string filenameglobInt(filenameglob);
833  auto chain = std::make_shared<TChain>(treeNameInt.c_str());
834  chain->Add(filenameglobInt.c_str());
835  GetProxiedPtr()->SetTree(chain);
836 }
837 
838 ////////////////////////////////////////////////////////////////////////////
839 /// \brief Build the dataframe
840 /// \param[in] treeName Name of the tree contained in the directory
841 /// \param[in] fileglobs Collection of file names of filename globs
842 /// \param[in] defaultBranches Collection of default branches.
843 ///
844 /// The filename globbing supports the same type of expressions as TChain::Add().
845 /// The default branches are looked at in case no branch is specified in the booking of actions or transformations.
846 /// See RInterface for the documentation of the methods available.
847 RDataFrame::RDataFrame(std::string_view treeName, const std::vector<std::string> &fileglobs,
848  const ColumnNames_t &defaultBranches)
849  : RInterface(std::make_shared<RDFDetail::RLoopManager>(nullptr, defaultBranches))
850 {
851  std::string treeNameInt(treeName);
852  auto chain = std::make_shared<TChain>(treeNameInt.c_str());
853  for (auto &f : fileglobs)
854  chain->Add(f.c_str());
855  GetProxiedPtr()->SetTree(chain);
856 }
857 
858 ////////////////////////////////////////////////////////////////////////////
859 /// \brief Build the dataframe
860 /// \param[in] tree The tree or chain to be studied.
861 /// \param[in] defaultBranches Collection of default column names to fall back to when none is specified.
862 ///
863 /// The default branches are looked at in case no branch is specified in the
864 /// booking of actions or transformations.
865 /// See RInterface for the documentation of the methods available.
867  : RInterface(std::make_shared<RDFDetail::RLoopManager>(&tree, defaultBranches))
868 {
869 }
870 
871 //////////////////////////////////////////////////////////////////////////
872 /// \brief Build a dataframe that generates numEntries entries.
873 /// \param[in] numEntries The number of entries to generate.
874 ///
875 /// An empty-source dataframe constructed with a number of entries will
876 /// generate those entries on the fly when some action is triggered,
877 /// and it will do so for all the previously-defined temporary branches.
878 /// See RInterface for the documentation of the methods available.
880  : RInterface(std::make_shared<RDFDetail::RLoopManager>(numEntries))
881 
882 {
883 }
884 
885 //////////////////////////////////////////////////////////////////////////
886 /// \brief Build dataframe associated to datasource.
887 /// \param[in] ds The data-source object.
888 /// \param[in] defaultBranches Collection of default column names to fall back to when none is specified.
889 ///
890 /// A dataframe associated to a datasource will query it to access column values.
891 /// See RInterface for the documentation of the methods available.
892 RDataFrame::RDataFrame(std::unique_ptr<ROOT::RDF::RDataSource> ds, const ColumnNames_t &defaultBranches)
893  : RInterface(std::make_shared<RDFDetail::RLoopManager>(std::move(ds), defaultBranches))
894 {
895 }
896 
897 } // namespace ROOT
898 
899 namespace cling {
900 //////////////////////////////////////////////////////////////////////////
901 /// Print a RDataFrame at the prompt
902 std::string printValue(ROOT::RDataFrame *tdf)
903 {
904  auto &df = *tdf->GetLoopManager();
905  auto *tree = df.GetTree();
906  auto defBranches = df.GetDefaultColumnNames();
907 
908  std::ostringstream ret;
909  if (tree) {
910  ret << "A data frame built on top of the " << tree->GetName() << " dataset.";
911  if (!defBranches.empty()) {
912  if (defBranches.size() == 1)
913  ret << "\nDefault branch: " << defBranches[0];
914  else {
915  ret << "\nDefault branches:\n";
916  for (auto &&branch : defBranches) {
917  ret << " - " << branch << "\n";
918  }
919  }
920  }
921  } else if (auto ds = tdf->fDataSource) {
922  ret << "A data frame associated to the data source \"" << cling::printValue(ds) << "\"";
923  } else {
924  ret << "An empty data frame that will create " << df.GetNEmptyEntries() << " entries\n";
925  }
926 
927  return ret.str();
928 }
929 } // namespace cling
The head node of a RDF computation graph.
VSD Structures.
Definition: StringConv.hxx:21
virtual TObject * Get(const char *namecycle)
Return pointer to object identified by namecycle.
Definition: TDirectory.cxx:805
#define f(i)
Definition: RSha256.hxx:104
STL namespace.
ROOT&#39;s RDataFrame offers a high level interface for analyses of data stored in TTrees, CSV&#39;s and other data formats.
Definition: RDataFrame.hxx:42
const std::shared_ptr< RDFDetail::RLoopManager > & GetProxiedPtr() const
Describe directory structure in memory.
Definition: TDirectory.h:34
unsigned long long ULong64_t
Definition: RtypesCore.h:70
Print a TSeq at the prompt:
Definition: TDatime.h:115
RDataFrame(std::string_view treeName, std::string_view filenameglob, const ColumnNames_t &defaultBranches={})
Build the dataframe.
Definition: RDataFrame.cxx:828
RLoopManager * GetLoopManager() const
std::shared_ptr< const ColumnNames_t > ColumnNamesPtr_t
Definition: RDataFrame.cxx:789
Definition: tree.py:1
A TTree represents a columnar dataset.
Definition: TTree.h:72
RDFDetail::ColumnNames_t ColumnNames_t
Definition: RDataFrame.hxx:44
ROOT::Detail::RDF::ColumnNames_t ColumnNames_t
Definition: RDataFrame.cxx:788