Skip to content

Commit 417e957

Browse files
committed
[hist] Implement RHist::Slice
Taint the global histogram statistics if dropping bin contents.
1 parent bbf4104 commit 417e957

4 files changed

Lines changed: 304 additions & 37 deletions

File tree

hist/histv7/inc/ROOT/RHist.hxx

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,115 @@ public:
451451
fStats.Scale(factor);
452452
}
453453

454+
/// Slice this histogram with an RSliceSpec per dimension.
455+
///
456+
/// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
457+
/// overflow bins:
458+
/// \code
459+
/// ROOT::Experimental::RHist<int> hist(/* one dimension */);
460+
/// // Fill the histogram with a number of entries...
461+
/// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)});
462+
/// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
463+
/// \endcode
464+
///
465+
/// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
466+
/// the histogram axis, grouping a number of normal bins into a new one:
467+
/// \code
468+
/// ROOT::Experimental::RHist<int> hist(/* one dimension */);
469+
/// // Fill the histogram with a number of entries...
470+
/// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
471+
/// // The returned histogram has groups of two normal bins merged.
472+
/// \endcode
473+
///
474+
/// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
475+
/// histogram:
476+
/// \code
477+
/// ROOT::Experimental::RHist<int> hist({/* two dimensions */});
478+
/// // Fill the histogram with a number of entries...
479+
/// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
480+
/// // The returned histogram has one dimension, with bin contents summed along the second axis.
481+
/// \endcode
482+
/// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
483+
///
484+
/// Ranges and operations can be combined. In that case, the range is applied before the operation.
485+
///
486+
/// \warning Combining a range and the sum operation drops bin contents, which will taint the global histogram
487+
/// statistics. Attempting to access its values, for example calling GetNEntries(), will throw exceptions.
488+
///
489+
/// \param[in] sliceSpecs the slice specifications for each axis
490+
/// \return the sliced histogram
491+
/// \par See also
492+
/// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly
493+
RHist Slice(const std::vector<RSliceSpec> &sliceSpecs) const
494+
{
495+
bool dropped = false;
496+
RHist sliced(fEngine.SliceImpl(sliceSpecs, dropped));
497+
assert(sliced.fStats.GetNDimensions() == sliced.GetNDimensions());
498+
if (dropped || fStats.IsTainted()) {
499+
sliced.fStats.Taint();
500+
} else {
501+
sliced.fStats.fNEntries = fStats.fNEntries;
502+
sliced.fStats.fSumW = fStats.fSumW;
503+
sliced.fStats.fSumW2 = fStats.fSumW2;
504+
std::size_t slicedDim = 0;
505+
for (std::size_t i = 0; i < sliceSpecs.size(); i++) {
506+
// A sum operation makes the dimension disappear.
507+
if (sliceSpecs[i].GetOperationSum() == nullptr) {
508+
sliced.fStats.fDimensionStats[slicedDim] = fStats.fDimensionStats[i];
509+
slicedDim++;
510+
}
511+
}
512+
assert(slicedDim == sliced.GetNDimensions());
513+
}
514+
return sliced;
515+
}
516+
517+
/// Slice this histogram with an RSliceSpec per dimension.
518+
///
519+
/// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
520+
/// overflow bins:
521+
/// \code
522+
/// ROOT::Experimental::RHist<int> hist(/* one dimension */);
523+
/// // Fill the histogram with a number of entries...
524+
/// auto sliced = hist.Slice(hist.GetAxes()[0].GetNormalRange(1, 5));
525+
/// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
526+
/// \endcode
527+
///
528+
/// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
529+
/// the histogram axis, grouping a number of normal bins into a new one:
530+
/// \code
531+
/// ROOT::Experimental::RHist<int> hist(/* one dimension */);
532+
/// // Fill the histogram with a number of entries...
533+
/// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
534+
/// // The returned histogram has groups of two normal bins merged.
535+
/// \endcode
536+
///
537+
/// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
538+
/// histogram:
539+
/// \code
540+
/// ROOT::Experimental::RHist<int> hist({/* two dimensions */});
541+
/// // Fill the histogram with a number of entries...
542+
/// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
543+
/// // The returned histogram has one dimension, with bin contents summed along the second axis.
544+
/// \endcode
545+
/// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
546+
///
547+
/// Ranges and operations can be combined. In that case, the range is applied before the operation.
548+
///
549+
/// \warning Combining a range and the sum operation drops bin contents, which will taint the global histogram
550+
/// statistics. Attempting to access its values, for example calling GetNEntries(), will throw exceptions.
551+
///
552+
/// \param[in] args the arguments for each axis
553+
/// \return the sliced histogram
554+
/// \par See also
555+
/// the \ref Slice(const std::vector<RSliceSpec> &sliceSpecs) const "function overload" accepting `std::vector`
556+
template <typename... A>
557+
RHist Slice(const A &...args) const
558+
{
559+
std::vector<RSliceSpec> sliceSpecs{args...};
560+
return Slice(sliceSpecs);
561+
}
562+
454563
/// %ROOT Streamer function to throw when trying to store an object of this class.
455564
void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHist"); }
456565
};

hist/histv7/inc/ROOT/RHistEngine.hxx

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class TBuffer;
3131
namespace ROOT {
3232
namespace Experimental {
3333

34+
// forward declaration for friend declaration
35+
template <typename T>
36+
class RHist;
37+
3438
/**
3539
A histogram data structure to bin data along multiple dimensions.
3640
@@ -67,6 +71,9 @@ class RHistEngine final {
6771
template <typename U>
6872
friend class RHistEngine;
6973

74+
// For slicing, RHist needs to call SliceImpl.
75+
friend class RHist<BinContentType>;
76+
7077
/// The axis configuration for this histogram. Relevant methods are forwarded from the public interface.
7178
Internal::RAxes fAxes;
7279
/// The bin contents for this histogram
@@ -641,43 +648,8 @@ public:
641648
}
642649
}
643650

644-
/// Slice this histogram with an RSliceSpec per dimension.
645-
///
646-
/// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
647-
/// overflow bins:
648-
/// \code
649-
/// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
650-
/// // Fill the histogram with a number of entries...
651-
/// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)});
652-
/// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
653-
/// \endcode
654-
///
655-
/// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
656-
/// the histogram axis, grouping a number of normal bins into a new one:
657-
/// \code
658-
/// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
659-
/// // Fill the histogram with a number of entries...
660-
/// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
661-
/// // The returned histogram has groups of two normal bins merged.
662-
/// \endcode
663-
///
664-
/// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
665-
/// histogram:
666-
/// \code
667-
/// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
668-
/// // Fill the histogram with a number of entries...
669-
/// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
670-
/// // The returned histogram has one dimension, with bin contents summed along the second axis.
671-
/// \endcode
672-
/// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
673-
///
674-
/// Ranges and operations can be combined. In that case, the range is applied before the operation.
675-
///
676-
/// \param[in] sliceSpecs the slice specifications for each axis
677-
/// \return the sliced histogram
678-
/// \par See also
679-
/// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly
680-
RHistEngine Slice(const std::vector<RSliceSpec> &sliceSpecs) const
651+
private:
652+
RHistEngine SliceImpl(const std::vector<RSliceSpec> &sliceSpecs, bool &dropped) const
681653
{
682654
if (sliceSpecs.size() != GetNDimensions()) {
683655
throw std::invalid_argument("invalid number of specifications passed to Slice");
@@ -719,13 +691,58 @@ public:
719691
RLinearizedIndex mappedIndex = sliced.fAxes.ComputeGlobalIndex(mappedIndices);
720692
assert(mappedIndex.fValid);
721693
sliced.fBinContents[mappedIndex.fIndex] += fBinContents[i];
694+
} else {
695+
dropped = true;
722696
}
723697
++origRangeIt;
724698
}
725699

726700
return sliced;
727701
}
728702

703+
public:
704+
/// Slice this histogram with an RSliceSpec per dimension.
705+
///
706+
/// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
707+
/// overflow bins:
708+
/// \code
709+
/// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
710+
/// // Fill the histogram with a number of entries...
711+
/// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)});
712+
/// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
713+
/// \endcode
714+
///
715+
/// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
716+
/// the histogram axis, grouping a number of normal bins into a new one:
717+
/// \code
718+
/// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
719+
/// // Fill the histogram with a number of entries...
720+
/// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
721+
/// // The returned histogram has groups of two normal bins merged.
722+
/// \endcode
723+
///
724+
/// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
725+
/// histogram:
726+
/// \code
727+
/// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
728+
/// // Fill the histogram with a number of entries...
729+
/// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
730+
/// // The returned histogram has one dimension, with bin contents summed along the second axis.
731+
/// \endcode
732+
/// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
733+
///
734+
/// Ranges and operations can be combined. In that case, the range is applied before the operation.
735+
///
736+
/// \param[in] sliceSpecs the slice specifications for each axis
737+
/// \return the sliced histogram
738+
/// \par See also
739+
/// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly
740+
RHistEngine Slice(const std::vector<RSliceSpec> &sliceSpecs) const
741+
{
742+
bool dropped = false;
743+
return SliceImpl(sliceSpecs, dropped);
744+
}
745+
729746
/// Slice this histogram with an RSliceSpec per dimension.
730747
///
731748
/// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and

hist/histv7/inc/ROOT/RHistStats.hxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class TBuffer;
2323
namespace ROOT {
2424
namespace Experimental {
2525

26+
// forward declaration for friend declaration
27+
template <typename T>
28+
class RHist;
29+
2630
/**
2731
Histogram statistics of unbinned values.
2832
@@ -39,6 +43,9 @@ stats.Fill(1.5);
3943
Feedback is welcome!
4044
*/
4145
class RHistStats final {
46+
template <typename T>
47+
friend class RHist;
48+
4249
public:
4350
/// Statistics for one dimension.
4451
struct RDimensionStats final {

0 commit comments

Comments
 (0)