template<typename T>
class ROOT::VecOps::RVec< T >
A "std::vector"-like collection of values implementing handy operation to analyse them.
- Template Parameters
-
T | The type of the contained objects |
A RVec is a container designed to make analysis of values' collections fast and easy. Its storage is contiguous in memory and its interface is designed such to resemble to the one of the stl vector. In addition the interface features methods and external functions to ease the manipulation and analysis of the data in the RVec.
Table of Contents
- Example
- Owning and adopting memory
- Sorting and manipulation of indices
- Usage in combination with RDataFrame
- Reference for the RVec class
Also see the reference for RVec helper functions.
Example
Suppose to have an event featuring a collection of muons with a certain pseudorapidity, momentum and charge, e.g.:
std::vector<short> mu_charge {1, 1, -1, -1, -1, 1, 1, -1};
std::vector<float> mu_pt {56, 45, 32, 24, 12, 8, 7, 6.2};
std::vector<float> mu_eta {3.1, -.2, -1.1, 1, 4.1, 1.6, 2.4, -.5};
Suppose you want to extract the transverse momenta of the muons satisfying certain criteria, for example consider only negatively charged muons with a pseudorapidity smaller or equal to 2 and with a transverse momentum greater than 10 GeV. Such a selection would require, among the other things, the management of an explicit loop, for example:
std::vector<float> goodMuons_pt;
const auto size = mu_charge.size();
for (
size_t i=0; i <
size; ++i) {
if (mu_pt[i] > 10 && abs(mu_eta[i]) <= 2. && mu_charge[i] == -1) {
goodMuons_pt.emplace_back(mu_pt[i]);
}
}
size_type size() const noexcept
These operations become straightforward with RVec - we just need to write what we mean:
auto goodMuons_pt = mu_pt[ (mu_pt > 10.f && abs(mu_eta) <= 2.f && mu_charge == -1) ]
Now the clean collection of transverse momenta can be used within the rest of the data analysis, for example to fill a histogram.
Owning and adopting memory
RVec has contiguous memory associated to it. It can own it or simply adopt it. In the latter case, it can be constructed with the address of the memory associated to it and its length. For example:
std::vector<int> myStlVec {1,2,3};
RVec<int> myRVec(myStlVec.data(), myStlVec.size());
In this case, the memory associated to myStlVec and myRVec is the same, myRVec simply "adopted it". If any method which implies a re-allocation is called, e.g. emplace_back or resize, the adopted memory is released and new one is allocated. The previous content is copied in the new memory and preserved.
Sorting and manipulation of indices
Sorting
RVec complies to the STL interfaces when it comes to iterations. As a result, standard algorithms can be used, for example sorting:
RVec<double>
v{6., 4., 5.};
std::sort(
v.begin(),
v.end());
For convinience, helpers are provided too:
RVec< T > Reverse(const RVec< T > &v)
Return copy of reversed vector.
RVec< T > Sort(const RVec< T > &v)
Return copy of RVec with elements sorted in ascending order.
Manipulation of indices
It is also possible to manipulated the RVecs acting on their indices. For example, the following syntax
RVec<double>
v0 {9., 7., 8.};
RVec< T > Take(const RVec< T > &v, const RVec< typename RVec< T >::size_type > &i)
Return elements of a vector at given indices.
will yield a new RVec<double> the content of which is the first, second and zeroth element of v0, i.e. {7., 8., 9.}
.
The Argsort
helper extracts the indices which order the content of a RVec
. For example, this snippet accomplish in a more expressive way what we just achieved:
RVec< typename RVec< T >::size_type > Argsort(const RVec< T > &v)
Return an RVec of indices that sort the input RVec.
The Take
utility allows to extract portions of the RVec
. The content to be taken can be specified with an RVec
of indices or an integer. If the integer is negative, elements will be picked starting from the end of the container:
RVec<float> vf {1.f, 2.f, 3.f, 4.f};
auto vf_1 =
Take(vf, {1, 3});
auto vf_3 =
Take(vf, -3);
Usage in combination with RDataFrame
RDataFrame leverages internally RVecs. Suppose to have a dataset stored in a TTree which holds these columns (here we choose C arrays to represent the collections, they could be as well std::vector instances):
nPart "nPart/I" An integer representing the number of particles
px "px[nPart]/D" The C array of the particles' x component of the momentum
py "py[nPart]/D" The C array of the particles' y component of the momentum
E "E[nPart]/D" The C array of the particles' Energy
Suppose you'd like to plot in a histogram the transverse momenta of all particles for which the energy is greater than 200 MeV. The code required would just be:
RDataFrame
d(
"mytree",
"myfile.root");
using doubles = RVec<double>;
auto cutPt = [](doubles &pxs, doubles &pys, doubles &Es) {
auto all_pts =
sqrt(pxs * pxs + pys * pys);
auto good_pts = all_pts[Es > 200.];
return good_pts;
};
auto hpt =
d.Define(
"pt", cutPt, {
"px",
"py",
"E"})
.Histo1D("pt");
hpt->Draw();
And if you'd like to express your selection as a string:
RDataFrame
d(
"mytree",
"myfile.root");
auto hpt =
d.Define(
"pt",
"sqrt(pxs * pxs + pys * pys)[E>200]")
.Histo1D("pt");
hpt->Draw();
Definition at line 275 of file RVec.hxx.