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.
- Note
- ROOT::VecOps::RVec can also be spelled simply ROOT::RVec. Shorthand aliases such as ROOT::RVecI or ROOT::RVecD are also available as template instantiations of RVec of fundamental types. The full list of available aliases:
- RVecB (
bool
)
- RVecC (
char
)
- RVecD (
double
)
- RVecF (
float
)
- RVecI (
int
)
- RVecL (
long
)
- RVecLL (
long long
)
- RVecU (
unsigned
)
- RVecUL (
unsigned long
)
- RVecULL (
unsigned long long
)
-
RVec does not attempt to be exception safe. Exceptions thrown by element constructors during insertions, swaps or other operations will be propagated potentially leaving the RVec object in an invalid state.
-
RVec methods (e.g.
at
or size
) follow the STL naming convention instead of the ROOT naming convention in order to make RVec a drop-in replacement for std::vector
.
Table of Contents
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]);
}
}
RVec< PromoteType< T > > abs(const RVec< T > &v)
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.
Arithmetic operations, logical operations and mathematical functions
Arithmetic operations on RVec instances can be performed: for example, they can be added, subtracted, multiplied.
A "std::vector"-like collection of values implementing handy operation to analyse them.
The supported operators are
- +, -, *, /
- +=, -=, *=, /=
- <, >, ==, !=, <=, >=, &&, ||
- ~, !
- &, |, ^
- &=, |=, ^=
- <<=, >>=
The most common mathematical functions are supported. It is possible to invoke them passing RVecs as arguments.
- abs, fdim, fmod, remainder
- floor, ceil, trunc, round, lround, llround
- exp, exp2, expm1
- log, log10, log2, log1p
- pow
- sqrt, cbrt
- sin, cos, tan, asin, acos, atan, atan2, hypot
- sinh, cosh, tanh, asinh, acosh
- erf, erfc
- lgamma, tgamma
If the VDT library is available, the following functions can be invoked. Internally the calculations are vectorized:
- fast_expf, fast_logf, fast_sinf, fast_cosf, fast_tanf, fast_asinf, fast_acosf, fast_atanf
- fast_exp, fast_log, fast_sin, fast_cos, fast_tan, fast_asin, fast_acos, fast_atan
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:
std::sort(
v.begin(),
v.end());
For convenience, 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< 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
and StableArgsort
helper extracts the indices which order the content of a RVec
. For example, this snippet accomplishes 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:
RVecF 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:
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();
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree ,...
And if you'd like to express your selection as a string:
auto hpt =
d.Define(
"pt",
"sqrt(pxs * pxs + pys * pys)[E>200]")
.Histo1D("pt");
hpt->Draw();
PyROOT
The ROOT::RVec class has additional features in Python, which allow to adopt memory from Numpy arrays and vice versa. The purpose of these features is the copyless interfacing of Python and C++ using their most common data containers, Numpy arrays and RVec with a std::vector interface.
Conversion of RVecs to Numpy arrays
RVecs of fundamental types (int, float, ...) have in Python the __array_interface__
attribute attached. This information allows Numpy to adopt the memory of RVecs without copying the content. You can find further documentation regarding the Numpy array interface here. The following code example demonstrates the memory adoption mechanism using numpy.asarray
.
print(rvec)
npy = numpy.asarray(rvec)
print(npy)
rvec[0] = 42
print(npy)
Conversion of Numpy arrays to RVecs
Data owned by Numpy arrays with fundamental types (int, float, ...) can be adopted by RVecs. To create an RVec from a Numpy array, ROOT offers the facility ROOT.VecOps.AsRVec, which performs a similar operation to numpy.asarray
, but vice versa. A code example demonstrating the feature and the adoption of the data owned by the Numpy array is shown below.
npy = numpy.array([1.0, 2.0, 3.0])
print(npy)
rvec = ROOT.VecOps.AsRVec(npy)
print(rvec)
npy[0] = 42
print(rvec)
Definition at line 1529 of file RVec.hxx.
|
| RVec () |
|
| RVec (const RVec &RHS) |
|
template<unsigned N> |
| RVec (const RVecN< T, N > &RHS) |
|
| RVec (const std::vector< T > &RHS) |
|
| RVec (Detail::VecOps::RVecImpl< T > &&RHS) |
|
template<typename ItTy , typename = typename std::enable_if<std::is_convertible< typename std::iterator_traits<ItTy>::iterator_category, std::input_iterator_tag>::value>::type> |
| RVec (ItTy S, ItTy E) |
|
| RVec (RVec &&RHS) |
|
template<unsigned N> |
| RVec (RVecN< T, N > &&RHS) |
|
| RVec (size_t Size) |
|
| RVec (size_t Size, const T &Value) |
|
| RVec (std::initializer_list< T > IL) |
|
| RVec (T *p, size_t n) |
|
template<typename U , typename = std::enable_if<std::is_convertible<T, U>::value>> |
| operator RVec () const |
|
RVec & | operator= (const RVec &RHS) |
|
RVec & | operator= (RVec &&RHS) |
|
template<typename V , typename = std::enable_if<std::is_convertible<V, bool>::value>> |
RVec | operator[] (const RVec< V > &conds) const |
|
| RVecN () |
|
| RVecN (const RVecN &RHS) |
|
| RVecN (const std::vector< T > &RHS) |
|
| RVecN (Detail::VecOps::RVecImpl< T > &&RHS) |
|
| RVecN (ItTy S, ItTy E) |
|
| RVecN (RVecN &&RHS) |
|
| RVecN (size_t Size) |
|
| RVecN (size_t Size, const T &Value) |
|
| RVecN (std::initializer_list< T > IL) |
|
| RVecN (T *p, size_t n) |
|
| ~RVecN () |
|
reference | at (size_type pos) |
|
const_reference | at (size_type pos) const |
|
value_type | at (size_type pos, value_type fallback) |
| No exception thrown. The user specifies the desired value in case the RVecN is shorter than pos .
|
|
value_type | at (size_type pos, value_type fallback) const |
| No exception thrown. The user specifies the desired value in case the RVecN is shorter than pos .
|
|
| operator RVecN< U, M > () const |
|
RVecN & | operator= (const RVecN &RHS) |
|
RVecN & | operator= (Detail::VecOps::RVecImpl< T > &&RHS) |
|
RVecN & | operator= (RVecN &&RHS) |
|
RVecN & | operator= (std::initializer_list< T > IL) |
|
RVecN | operator[] (const RVecN< V, M > &conds) const |
|
reference | operator[] (size_type idx) |
|
const_reference | operator[] (size_type idx) const |
|
| RVecImpl (const RVecImpl &)=delete |
|
| ~RVecImpl () |
|
template<typename in_iter , typename = typename std::enable_if<std::is_convertible< typename std::iterator_traits<in_iter>::iterator_category, std::input_iterator_tag>::value>::type> |
void | append (in_iter in_start, in_iter in_end) |
| Add the specified range to the end of the SmallVector.
|
|
void | append (size_type NumInputs, const T &Elt) |
| Append NumInputs copies of Elt to the end.
|
|
void | append (std::initializer_list< T > IL) |
|
template<typename in_iter , typename = typename std::enable_if<std::is_convertible< typename std::iterator_traits<in_iter>::iterator_category, std::input_iterator_tag>::value>::type> |
void | assign (in_iter in_start, in_iter in_end) |
|
void | assign (size_type NumElts, const T &Elt) |
|
void | assign (std::initializer_list< T > IL) |
|
void | clear () |
|
template<typename... ArgTypes> |
reference | emplace_back (ArgTypes &&...Args) |
|
iterator | erase (const_iterator CI) |
|
iterator | erase (const_iterator CS, const_iterator CE) |
|
iterator | insert (iterator I, const T &Elt) |
|
template<typename ItTy , typename = typename std::enable_if<std::is_convertible< typename std::iterator_traits<ItTy>::iterator_category, std::input_iterator_tag>::value>::type> |
iterator | insert (iterator I, ItTy From, ItTy To) |
|
iterator | insert (iterator I, size_type NumToInsert, const T &Elt) |
|
void | insert (iterator I, std::initializer_list< T > IL) |
|
iterator | insert (iterator I, T &&Elt) |
|
RVecImpl & | operator= (const RVecImpl &RHS) |
|
RVecImpl & | operator= (RVecImpl &&RHS) |
|
void | pop_back_n (size_type NumItems) |
|
T | pop_back_val () |
|
void | reserve (size_type N) |
|
void | resize (size_type N) |
|
void | resize (size_type N, const T &NV) |
|
void | swap (RVecImpl &RHS) |
|
void | pop_back () |
|
void | push_back (const T &Elt) |
|
void | push_back (T &&Elt) |
|
reference | back () |
|
const_reference | back () const |
|
const_iterator | begin () const noexcept |
|
iterator | begin () noexcept |
|
size_t | capacity () const noexcept |
|
size_t | capacity_in_bytes () const |
|
const_iterator | cbegin () const noexcept |
|
const_iterator | cend () const noexcept |
|
const_reverse_iterator | crbegin () const noexcept |
|
const_reverse_iterator | crend () const noexcept |
|
const_pointer | data () const noexcept |
| Return a pointer to the vector's buffer, even if empty().
|
|
pointer | data () noexcept |
| Return a pointer to the vector's buffer, even if empty().
|
|
bool | empty () const |
|
const_iterator | end () const noexcept |
|
iterator | end () noexcept |
|
reference | front () |
|
const_reference | front () const |
|
size_type | max_size () const noexcept |
|
const_reverse_iterator | rbegin () const noexcept |
|
reverse_iterator | rbegin () noexcept |
|
const_reverse_iterator | rend () const noexcept |
|
reverse_iterator | rend () noexcept |
|
size_t | size () const |
|
size_type | size_in_bytes () const |
|
size_t | capacity () const noexcept |
|
bool | empty () const |
|
void | set_size (size_t N) |
| Set the array size to N , which the current array must have enough capacity for.
|
|
size_t | size () const |
|
|
| RVecImpl (unsigned N) |
|
| SmallVectorTemplateBase (size_t Size) |
|
void | grow (size_t MinSize=0) |
| Grow the allocated memory (without initializing new elements), doubling the size of the allocated memory.
|
|
| SmallVectorTemplateCommon (size_t Size) |
|
void | grow_pod (size_t MinSize, size_t TSize) |
|
bool | isSmall () const |
| Return true if this is a smallvector which has not had dynamic memory allocated for it.
|
|
void | resetToSmall () |
| Put this vector in a state of being small.
|
|
| SmallVectorBase ()=delete |
|
| SmallVectorBase (void *FirstEl, size_t TotalCapacity) |
|
void | grow_pod (void *FirstEl, size_t MinSize, size_t TSize) |
| This is an implementation of the grow() method which only works on POD-like data types and is out of line to reduce code duplication.
|
|
bool | Owns () const |
| If false, the RVec is in "memory adoption" mode, i.e. it is acting as a view on a memory buffer it does not own.
|
|
static void | destroy_range (T *S, T *E) |
|
template<typename It1 , typename It2 > |
static void | uninitialized_copy (It1 I, It1 E, It2 Dest) |
| Copy the range [I, E) onto the uninitialized memory starting with "Dest", constructing elements as needed.
|
|
template<typename It1 , typename It2 > |
static void | uninitialized_move (It1 I, It1 E, It2 Dest) |
| Move the range [I, E) into the uninitialized memory starting with "Dest", constructing elements as needed.
|
|
static void | report_at_maximum_capacity () |
| Report that this vector is already at maximum capacity.
|
|
static void | report_size_overflow (size_t MinSize) |
| Report that MinSize doesn't fit into this vector's size type.
|
|
static constexpr size_t | SizeTypeMax () |
| The maximum value of the Size_T used.
|
|
void * | fBeginX |
|
Size_T | fCapacity |
| Always >= -1. fCapacity == -1 indicates the RVec is in "memory adoption" mode.
|
|
Size_T | fSize = 0 |
| Always >= 0.
|
|