Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RDatasetLoader.hxx
Go to the documentation of this file.
1// Author: Martin Føll, University of Oslo (UiO) & CERN 01/2026
2
3/*************************************************************************
4 * Copyright (C) 1995-2026, 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#ifndef ROOT_INTERNAL_ML_RDATASETLOADER
12#define ROOT_INTERNAL_ML_RDATASETLOADER
13
14#include <algorithm>
15#include <memory>
16#include <numeric>
17#include <string>
18#include <type_traits>
19#include <vector>
20
23#include "ROOT/RDataFrame.hxx"
24#include "ROOT/RDF/Utils.hxx"
25
27
28/**
29\class ROOT::Experimental::Internal::ML::RDatasetLoaderFunctor
30
31\brief Loading chunks made in RDatasetLoader into tensors from data from RDataFrame.
32*/
33
34template <typename... ColTypes>
36 std::size_t fOffset{};
37 std::size_t fVecSizeIdx{};
38 float fVecPadding{};
39 std::vector<std::size_t> fMaxVecSizes{};
41
42 std::size_t fNumDatasetCols;
43
44 int fI;
46
47 //////////////////////////////////////////////////////////////////////////
48 /// \brief Copy the content of a column into RFlat2DMatrix when the column consits of vectors
49 template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value, int> = 0>
50 void AssignToTensor(const T &vec, int i, int numColumns)
51 {
52 std::size_t max_vec_size = fMaxVecSizes[fVecSizeIdx++];
53 std::size_t vec_size = vec.size();
54 if (vec_size < max_vec_size) // Padding vector column to max_vec_size with fVecPadding
55 {
56 std::copy(vec.begin(), vec.end(), &fDatasetTensor.GetData()[fOffset + numColumns * i]);
57 std::fill(&fDatasetTensor.GetData()[fOffset + numColumns * i + vec_size],
58 &fDatasetTensor.GetData()[fOffset + numColumns * i + max_vec_size], fVecPadding);
59 } else // Copy only max_vec_size length from vector column
60 {
61 std::copy(vec.begin(), vec.begin() + max_vec_size, &fDatasetTensor.GetData()[fOffset + numColumns * i]);
62 }
63 fOffset += max_vec_size;
64 }
65
66 //////////////////////////////////////////////////////////////////////////
67 /// \brief Copy the content of a column into RFlat2DMatrix when the column consits of single values
68 template <typename T, std::enable_if_t<!ROOT::Internal::RDF::IsDataContainer<T>::value, int> = 0>
69 void AssignToTensor(const T &val, int i, int numColumns)
70 {
71 fDatasetTensor.GetData()[fOffset + numColumns * i] = val;
72 fOffset++;
73 }
74
75public:
76 RDatasetLoaderFunctor(RFlat2DMatrix &datasetTensor, std::size_t numColumns,
77 const std::vector<std::size_t> &maxVecSizes, float vecPadding, int i)
78 : fDatasetTensor(datasetTensor),
79 fMaxVecSizes(maxVecSizes),
80 fVecPadding(vecPadding),
81 fI(i),
82 fNumColumns(numColumns)
83 {
84 }
85
86 void operator()(const ColTypes &...cols)
87 {
88 fVecSizeIdx = 0;
89 (AssignToTensor(cols, fI, fNumColumns), ...);
90 }
91};
92
93/**
94\class ROOT::Experimental::Internal::ML::RDatasetLoader
95
96\brief Load the whole dataset into memory.
97
98In this class the whole dataset is loaded into memory. The dataset is further shuffled and spit into training and
99validation sets with the user-defined validation split fraction.
100*/
101
102template <typename... Args>
104private:
105 std::size_t fNumEntries;
107
108 std::vector<std::size_t> fVecSizes;
109 std::size_t fSumVecSizes;
110 std::size_t fVecPadding;
111 std::size_t fNumDatasetCols;
112
113 std::vector<RFlat2DMatrix> fTrainingDatasets;
114 std::vector<RFlat2DMatrix> fValidationDatasets;
115
118
121 std::unique_ptr<RFlat2DMatrixOperators> fTensorOperators;
122
123 std::vector<ROOT::RDF::RNode> f_rdfs;
124 std::vector<std::string> fCols;
125 std::size_t fNumCols;
126 std::size_t fSetSeed;
127
130
132
133public:
134 RDatasetLoader(const std::vector<ROOT::RDF::RNode> &rdfs, const float validationSplit,
135 const std::vector<std::string> &cols, const std::vector<std::size_t> &vecSizes = {},
136 const float vecPadding = 0.0, bool shuffle = true, const std::size_t setSeed = 0)
137 : f_rdfs(rdfs),
138 fCols(cols),
139 fVecSizes(vecSizes),
140 fVecPadding(vecPadding),
141 fValidationSplit(validationSplit),
142 fShuffle(shuffle),
143 fSetSeed(setSeed)
144 {
145 fTensorOperators = std::make_unique<RFlat2DMatrixOperators>(fShuffle, fSetSeed);
146 fNumCols = fCols.size();
147 fSumVecSizes = std::accumulate(fVecSizes.begin(), fVecSizes.end(), 0);
148
150 }
151
152 //////////////////////////////////////////////////////////////////////////
153 /// \brief Split an individual dataframe into a training and validation dataset
154 /// \param[in] rdf Dataframe that will be split into training and validation
155 /// \param[in] TrainingDataset Tensor for the training dataset
156 /// \param[in] ValidationDataset Tensor for the validation dataset
157 void SplitDataframe(ROOT::RDF::RNode &rdf, RFlat2DMatrix &TrainingDataset, RFlat2DMatrix &ValidationDataset)
158 {
159 const bool NotFiltered = rdf.GetFilterNames().empty();
160
161 // size the buffer from the cluster metadata, Count() is only the fallback for sources without it
163 std::size_t NumEntries = 0;
164 if (NotFiltered) {
165 try {
166 for (const auto &cluster : ROOT::Internal::RDF::GetDatasetGlobalClusterBoundaries(rdf)) {
167 NumEntries += cluster.second - cluster.first;
168 }
169 } catch (const std::runtime_error &) {
170 // GetDatasetGlobalClusterBoundaries() throws when the RDataFrame has no cluster
171 // metadata to query (a source other than TTree/RNTuple or no data source at all).
172 // Those paths fall back to Count() below.
173 }
174 if (NumEntries == 0) {
175 NumEntries = static_cast<std::size_t>(*rdf.Count());
176 }
177 } else {
178 Entries = rdf.Take<ULong64_t>("rdfentry_");
179 NumEntries = Entries->size();
180 // add the last element in entries to not go out of range when filling chunks
181 Entries->push_back((*Entries)[NumEntries - 1] + 1);
182 }
183
184 // number of training and validation entries after the split
185 std::size_t NumValidationEntries = static_cast<std::size_t>(fValidationSplit * NumEntries);
186 std::size_t NumTrainingEntries = NumEntries - NumValidationEntries;
187
188 RFlat2DMatrix Dataset({NumEntries, fNumDatasetCols});
189
190 if (NotFiltered) {
191 RDatasetLoaderFunctor<Args...> func(Dataset, fNumDatasetCols, fVecSizes, fVecPadding, 0);
192 rdf.Foreach(func, fCols);
193 }
194
195 else {
196 std::size_t datasetEntry = 0;
197 for (std::size_t j = 0; j < NumEntries; j++) {
198 RDatasetLoaderFunctor<Args...> func(Dataset, fNumDatasetCols, fVecSizes, fVecPadding, datasetEntry);
199 ROOT::Internal::RDF::ChangeBeginAndEndEntries(rdf, (*Entries)[j], (*Entries)[j + 1]);
200 rdf.Foreach(func, fCols);
201 datasetEntry++;
202 }
203 }
204
205 // reset dataframe, only the filtered path changed the entry range
206 if (!NotFiltered) {
207 ROOT::Internal::RDF::ChangeBeginAndEndEntries(rdf, (*Entries)[0], (*Entries)[NumEntries]);
208 }
209
210 // copy out the validation tail, then shrink the (shuffled) buffer to the training rows and move it
211 RFlat2DMatrix ShuffledDataset;
212 RFlat2DMatrix &Source = fTensorOperators->ShuffleTensor(ShuffledDataset, Dataset);
213 fTensorOperators->SliceTensor(ValidationDataset, Source,
214 {{NumTrainingEntries, NumEntries}, {0, fNumDatasetCols}});
215 Source.Resize(NumTrainingEntries, fNumDatasetCols);
216 TrainingDataset = std::move(Source);
217 }
218
219 //////////////////////////////////////////////////////////////////////////
220 /// \brief Split the dataframes in a training and validation dataset
222 {
223 fNumEntries = 0;
226
227 for (auto &rdf : f_rdfs) {
228 RFlat2DMatrix TrainingDataset;
229 RFlat2DMatrix ValidationDataset;
230
231 SplitDataframe(rdf, TrainingDataset, ValidationDataset);
232
233 fNumTrainingEntries += TrainingDataset.GetRows();
234 fNumValidationEntries += ValidationDataset.GetRows();
235 fNumEntries += TrainingDataset.GetRows() + ValidationDataset.GetRows();
236
237 fTrainingDatasets.push_back(std::move(TrainingDataset));
238 fValidationDatasets.push_back(std::move(ValidationDataset));
239 }
240 }
241
242 //////////////////////////////////////////////////////////////////////////
243 /// \brief Concatenate the datasets to a dataset
245 {
246 if (fTrainingDatasets.size() == 1) {
247 fTrainingDataset = std::move(fTrainingDatasets[0]);
249 } else {
252 }
253 fTrainingDatasets.clear();
254 fValidationDatasets.clear();
255 }
256
257 std::vector<RFlat2DMatrix> ReleaseTrainingDatasets() { return std::move(fTrainingDatasets); }
258 std::vector<RFlat2DMatrix> ReleaseValidationDatasets() { return std::move(fValidationDatasets); }
259
262
265};
266
267} // namespace ROOT::Experimental::Internal::ML
268#endif // ROOT_INTERNAL_ML_RDATASETLOADER
unsigned long long ULong64_t
Portable unsigned long integer 8 bytes.
Definition RtypesCore.h:85
Loading chunks made in RDatasetLoader into tensors from data from RDataFrame.
void AssignToTensor(const T &val, int i, int numColumns)
Copy the content of a column into RFlat2DMatrix when the column consits of single values.
void AssignToTensor(const T &vec, int i, int numColumns)
Copy the content of a column into RFlat2DMatrix when the column consits of vectors.
RDatasetLoaderFunctor(RFlat2DMatrix &datasetTensor, std::size_t numColumns, const std::vector< std::size_t > &maxVecSizes, float vecPadding, int i)
std::vector< RFlat2DMatrix > ReleaseValidationDatasets()
std::vector< RFlat2DMatrix > ReleaseTrainingDatasets()
void SplitDataframe(ROOT::RDF::RNode &rdf, RFlat2DMatrix &TrainingDataset, RFlat2DMatrix &ValidationDataset)
Split an individual dataframe into a training and validation dataset.
std::unique_ptr< RFlat2DMatrixOperators > fTensorOperators
void SplitDatasets()
Split the dataframes in a training and validation dataset.
ROOT::RDF::RResultPtr< std::vector< ULong64_t > > fEntries
RDatasetLoader(const std::vector< ROOT::RDF::RNode > &rdfs, const float validationSplit, const std::vector< std::string > &cols, const std::vector< std::size_t > &vecSizes={}, const float vecPadding=0.0, bool shuffle=true, const std::size_t setSeed=0)
void ConcatenateDatasets()
Concatenate the datasets to a dataset.
The public interface to the RDataFrame federation of classes.
std::vector< std::string > GetFilterNames()
Returns the names of the filters created.
RResultPtr< COLL > Take(std::string_view column="")
Return a collection of values of a column (lazy action, returns a std::vector by default).
void Foreach(F f, const ColumnNames_t &columns={})
Execute a user-defined function on each entry (instant action).
RResultPtr< ULong64_t > Count()
Return the number of entries processed (lazy action).
Smart pointer for the return type of actions.
std::vector< std::pair< std::uint64_t, std::uint64_t > > GetDatasetGlobalClusterBoundaries(const RNode &node)
Retrieve the cluster boundaries for each cluster in the dataset, across files, with a global offset.
void ChangeBeginAndEndEntries(const RNode &node, Long64_t begin, Long64_t end)
Wrapper around ROOT::RVec<float> representing a 2D matrix.
void Resize(std::size_t rows, std::size_t cols)