Logo ROOT   6.14/05
Reference Guide
df001_introduction.py
Go to the documentation of this file.
1 ## \file
2 ## \ingroup tutorial_dataframe
3 ## \notebook -nodraw
4 ## This tutorial illustrates the basic features of the RDataFrame class,
5 ## a utility which allows to interact with data stored in TTrees following
6 ## a functional-chain like approach.
7 ##
8 ## \macro_code
9 ##
10 ## \date May 2017
11 ## \author Danilo Piparo
12 
13 import ROOT
14 
15 # A simple helper function to fill a test tree: this makes the example stand-alone.
16 def fill_tree(treeName, fileName):
17  tdf = ROOT.ROOT.RDataFrame(10)
18  tdf.Define("b1", "(double) tdfentry_")\
19  .Define("b2", "(int) tdfentry_ * tdfentry_").Snapshot(treeName, fileName)
20 
21 # We prepare an input tree to run on
22 fileName = "df001_introduction_py.root"
23 treeName = "myTree"
24 fill_tree(treeName, fileName)
25 
26 
27 # We read the tree from the file and create a RDataFrame, a class that
28 # allows us to interact with the data contained in the tree.
29 RDF = ROOT.ROOT.RDataFrame
30 d = RDF(treeName, fileName)
31 
32 # Operations on the dataframe
33 # We now review some *actions* which can be performed on the data frame.
34 # All actions but ForEach return a TActionResultPtr<T>. The series of
35 # operations on the data frame is not executed until one of those pointers
36 # is accessed.
37 # But first of all, let us we define now our cut-flow with two strings.
38 # Filters can be expressed as strings. The content must be C++ code. The
39 # name of the variables must be the name of the branches. The code is
40 # just in time compiled.
41 cutb1 = 'b1 < 5.'
42 cutb1b2 = 'b2 % 2 && b1 < 4.'
43 
44 # `Count` action
45 # The `Count` allows to retrieve the number of the entries that passed the
46 # filters. Here we show how the automatic selection of the column kicks
47 # in in case the user specifies none.
48 entries1 = d.Filter(cutb1) \
49  .Filter(cutb1b2) \
50  .Count();
51 
52 print("%s entries passed all filters" %entries1.GetValue())
53 
54 entries2 = d.Filter("b1 < 5.").Count();
55 print("%s entries passed all filters" %entries2.GetValue())
56 
57 # `Min`, `Max` and `Mean` actions
58 # These actions allow to retrieve statistical information about the entries
59 # passing the cuts, if any.
60 b1b2_cut = d.Filter(cutb1b2)
61 minVal = b1b2_cut.Min('b1')
62 maxVal = b1b2_cut.Max('b1')
63 meanVal = b1b2_cut.Mean('b1')
64 nonDefmeanVal = b1b2_cut.Mean("b2")
65 print("The mean is always included between the min and the max: %s <= %s <= %s" %(minVal.GetValue(), meanVal.GetValue(), maxVal.GetValue()))
66 
67 # `Histo1D` action
68 # The `Histo1D` action allows to fill an histogram. It returns a TH1F filled
69 # with values of the column that passed the filters. For the most common
70 # types, the type of the values stored in the column is automatically
71 # guessed.
72 hist = d.Filter(cutb1).Histo1D('b1')
73 print("Filled h %s times, mean: %s" %(hist.GetEntries(), hist.GetMean()))
74 
75 # Express your chain of operations with clarity!
76 # We are discussing an example here but it is not hard to imagine much more
77 # complex pipelines of actions acting on data. Those might require code
78 # which is well organised, for example allowing to conditionally add filters
79 # or again to clearly separate filters and actions without the need of
80 # writing the entire pipeline on one line. This can be easily achieved.
81 # We'll show this re-working the `Count` example:
82 cutb1_result = d.Filter(cutb1);
83 cutb1b2_result = d.Filter(cutb1b2);
84 cutb1_cutb1b2_result = cutb1_result.Filter(cutb1b2)
85 
86 # Now we want to count:
87 evts_cutb1_result = cutb1_result.Count()
88 evts_cutb1b2_result = cutb1b2_result.Count()
89 evts_cutb1_cutb1b2_result = cutb1_cutb1b2_result.Count()
90 
91 print("Events passing cutb1: %s" %evts_cutb1_result.GetValue())
92 print("Events passing cutb1b2: %s" %evts_cutb1b2_result.GetValue())
93 print("Events passing both: %s" %evts_cutb1_cutb1b2_result.GetValue())
94 
95 # Calculating quantities starting from existing columns
96 # Often, operations need to be carried out on quantities calculated starting
97 # from the ones present in the columns. We'll create in this example a third
98 # column the values of which are the sum of the *b1* and *b2* ones, entry by
99 # entry. The way in which the new quantity is defined is via a runable.
100 # It is important to note two aspects at this point:
101 # - The value is created on the fly only if the entry passed the existing
102 # filters.
103 # - The newly created column behaves as the one present on the file on disk.
104 # - The operation creates a new value, without modifying anything. De facto,
105 # this is like having a general container at disposal able to accommodate
106 # any value of any type.
107 # Let's dive in an example:
108 entries_sum = d.Define('sum', 'b2 + b1') \
109  .Filter('sum > 4.2') \
110  .Count()
111 print(entries_sum.GetValue())
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:636