ROOT   Reference Guide
df001_introduction.C
Go to the documentation of this file.
1/// \file
2/// \ingroup tutorial_dataframe
3/// \notebook -nodraw
4/// Basic RDataFrame usage.
5///
6/// This tutorial illustrates the basic features of the RDataFrame class,
7/// a utility which allows to interact with data stored in TTrees following
8/// a functional-chain like approach.
9///
10/// \macro_code
11/// \macro_output
12///
13/// \date December 2016
14/// \author Enrico Guiraud (CERN)
15
16// ## Preparation
17
18// A simple helper function to fill a test tree: this makes the example
19// stand-alone.
20void fill_tree(const char *treeName, const char *fileName)
21{
23 int i(0);
24 d.Define("b1", [&i]() { return (double)i; })
25 .Define("b2",
26 [&i]() {
27 auto j = i * i;
28 ++i;
29 return j;
30 })
31 .Snapshot(treeName, fileName);
32}
33
35{
36
37 // We prepare an input tree to run on
38 auto fileName = "df001_introduction.root";
39 auto treeName = "myTree";
40 fill_tree(treeName, fileName);
41
42 // We read the tree from the file and create a RDataFrame, a class that
43 // allows us to interact with the data contained in the tree.
44 // We select a default column, a *branch* to adopt ROOT jargon, which will
45 // be looked at if none is specified by the user when dealing with filters
46 // and actions.
47 ROOT::RDataFrame d(treeName, fileName, {"b1"});
48
49 // ## Operations on the dataframe
50 // We now review some *actions* which can be performed on the data frame.
51 // All actions but ForEach return a TActionResultPtr<T>. The series of
52 // operations on the data frame is not executed until one of those pointers
53 // is accessed. If the Foreach action is invoked, the execution is immediate.
54 // But first of all, let us we define now our cut-flow with two lambda
55 // functions. We can use free functions too.
56 auto cutb1 = [](double b1) { return b1 < 5.; };
57 auto cutb1b2 = [](int b2, double b1) { return b2 % 2 && b1 < 4.; };
58
59 // ### Count action
60 // The Count allows to retrieve the number of the entries that passed the
61 // filters. Here we show how the automatic selection of the column kicks
62 // in in case the user specifies none.
63 auto entries1 = d.Filter(cutb1) // <- no column name specified here!
64 .Filter(cutb1b2, {"b2", "b1"})
65 .Count();
66
67 std::cout << *entries1 << " entries passed all filters" << std::endl;
68
69 // Filters can be expressed as strings. The content must be C++ code. The
70 // name of the variables must be the name of the branches. The code is
71 // just in time compiled.
72 auto entries2 = d.Filter("b1 < 5.").Count();
73 std::cout << *entries2 << " entries passed the string filter" << std::endl;
74
75 // ### Min, Max and Mean actions
76 // These actions allow to retrieve statistical information about the entries
77 // passing the cuts, if any.
78 auto b1b2_cut = d.Filter(cutb1b2, {"b2", "b1"});
79 auto minVal = b1b2_cut.Min();
80 auto maxVal = b1b2_cut.Max();
81 auto meanVal = b1b2_cut.Mean();
82 auto nonDefmeanVal = b1b2_cut.Mean("b2"); // <- Column is not the default
83 std::cout << "The mean is always included between the min and the max: " << *minVal << " <= " << *meanVal
84 << " <= " << *maxVal << std::endl;
85
86 // ### Take action
87 // The Take action allows to retrieve all values of the variable stored in a
88 // particular column that passed filters we specified. The values are stored
89 // in a list by default, but other collections can be chosen.
90 auto b1_cut = d.Filter(cutb1);
91 auto b1Vec = b1_cut.Take<double>();
92 auto b1List = b1_cut.Take<double, std::list<double>>();
93
94 std::cout << "Selected b1 entries" << std::endl;
95 for (auto b1_entry : *b1List)
96 std::cout << b1_entry << " ";
97 std::cout << std::endl;
98 auto b1VecCl = ROOT::GetClass(b1Vec.GetPtr());
99 std::cout << "The type of b1Vec is " << b1VecCl->GetName() << std::endl;
100
101 // ### Histo1D action
102 // The Histo1D action allows to fill an histogram. It returns a TH1D filled
103 // with values of the column that passed the filters. For the most common
104 // types, the type of the values stored in the column is automatically
105 // guessed.
106 auto hist = d.Filter(cutb1).Histo1D();
107 std::cout << "Filled h " << hist->GetEntries() << " times, mean: " << hist->GetMean() << std::endl;
108
109 // ### Foreach action
110 // The most generic action of all: an operation is applied to all entries.
111 // In this case we fill a histogram. In some sense this is a violation of a
112 // purely functional paradigm - C++ allows to do that.
113 TH1F h("h", "h", 12, -1, 11);
114 d.Filter([](int b2) { return b2 % 2 == 0; }, {"b2"}).Foreach([&h](double b1) { h.Fill(b1); });
115
116 std::cout << "Filled h with " << h.GetEntries() << " entries" << std::endl;
117
118 // ## Express your chain of operations with clarity!
119 // We are discussing an example here but it is not hard to imagine much more
120 // complex pipelines of actions acting on data. Those might require code
121 // which is well organised, for example allowing to conditionally add filters
122 // or again to clearly separate filters and actions without the need of
123 // writing the entire pipeline on one line. This can be easily achieved.
124 // We'll show this re-working the Count example:
125 auto cutb1_result = d.Filter(cutb1);
126 auto cutb1b2_result = d.Filter(cutb1b2, {"b2", "b1"});
127 auto cutb1_cutb1b2_result = cutb1_result.Filter(cutb1b2, {"b2", "b1"});
128 // Now we want to count:
129 auto evts_cutb1_result = cutb1_result.Count();
130 auto evts_cutb1b2_result = cutb1b2_result.Count();
131 auto evts_cutb1_cutb1b2_result = cutb1_cutb1b2_result.Count();
132
133 std::cout << "Events passing cutb1: " << *evts_cutb1_result << std::endl
134 << "Events passing cutb1b2: " << *evts_cutb1b2_result << std::endl
135 << "Events passing both: " << *evts_cutb1_cutb1b2_result << std::endl;
136
137 // ## Calculating quantities starting from existing columns
138 // Often, operations need to be carried out on quantities calculated starting
139 // from the ones present in the columns. We'll create in this example a third
140 // column the values of which are the sum of the *b1* and *b2* ones, entry by
141 // entry. The way in which the new quantity is defined is via a callable.
142 // It is important to note two aspects at this point:
143 // - The value is created on the fly only if the entry passed the existing
144 // filters.
145 // - The newly created column behaves as the one present on the file on disk.
146 // - The operation creates a new value, without modifying anything. De facto,
147 // this is like having a general container at disposal able to accommodate
148 // any value of any type.
149 // Let's dive in an example:
150 auto entries_sum = d.Define("sum", [](double b1, int b2) { return b2 + b1; }, {"b1", "b2"})
151 .Filter([](double sum) { return sum > 4.2; }, {"sum"})
152 .Count();
153 std::cout << *entries_sum << std::endl;
154
155 // Additional columns can be expressed as strings. The content must be C++
156 // code. The name of the variables must be the name of the branches. The code
157 // is just in time compiled.
158 auto entries_sum2 = d.Define("sum2", "b1 + b2").Filter("sum2 > 4.2").Count();
159 std::cout << *entries_sum2 << std::endl;
160
161 // It is possible at any moment to read the entry number and the processing
162 // slot number. The latter may change when implicit multithreading is active.
163 // The special columns which provide the entry number and the slot index are
164 // called "rdfentry_" and "rdfslot_" respectively. Their types are an unsigned
165 // 64 bit integer and an unsigned integer.
166 auto printEntrySlot = [](ULong64_t iEntry, unsigned int slot) {
167 std::cout << "Entry: " << iEntry << " Slot: " << slot << std::endl;
168 };
169 d.Foreach(printEntrySlot, {"rdfentry_", "rdfslot_"});
170
171 return 0;
172}
double
Definition: Converters.cxx:921
#define d(i)
Definition: RSha256.hxx:102
#define h(i)
Definition: RSha256.hxx:106
unsigned long long ULong64_t
Definition: RtypesCore.h:74
ROOT's RDataFrame offers a high level interface for analyses of data stored in TTrees,...
Definition: RDataFrame.hxx:42
1-D histogram with a float per channel (see TH1 documentation)}
Definition: TH1.h:575
RVec< T > Filter(const RVec< T > &v, F &&f)
Create a new collection with the elements passing the filter expressed by the predicate.
Definition: RVec.hxx:971
TClass * GetClass(T *)
Definition: TClass.h:658
static uint64_t sum(uint64_t i)
Definition: Factory.cxx:2345