diff --git a/hist/histv7/inc/ROOT/RProfile.hxx b/hist/histv7/inc/ROOT/RProfile.hxx index a081ec31e2acb..38b713faefd61 100644 --- a/hist/histv7/inc/ROOT/RProfile.hxx +++ b/hist/histv7/inc/ROOT/RProfile.hxx @@ -123,6 +123,21 @@ public: Internal::AtomicAdd(&fSum, rhs.fSum); Internal::AtomicAdd(&fSum2, rhs.fSum2); } + + /// Compute the arithmetic mean in this bin. + /// + /// \f[ + /// \mu = \frac{\sum w_i \cdot x_i}{\sum w_i} + /// \f] + /// + /// \return the arithmetic mean in this bin + double ComputeMean() const + { + if (fSum == 0) { + return std::numeric_limits::signaling_NaN(); + } + return fSumValues / fSum; + } }; private: diff --git a/hist/histv7/test/hist_profile.cxx b/hist/histv7/test/hist_profile.cxx index f671367412840..513595e45b14c 100644 --- a/hist/histv7/test/hist_profile.cxx +++ b/hist/histv7/test/hist_profile.cxx @@ -235,6 +235,22 @@ TEST(RProfile, Clone) EXPECT_EQ(profileB.GetBinContent(9).fSumValues, 25.0); } +TEST(RProfile, ComputeMean) +{ + static constexpr std::size_t Bins = 20; + RProfile profile(Bins, {0, Bins}); + EXPECT_TRUE(std::isnan(profile.GetBinContent(0).ComputeMean())); + + profile.Fill(8.5, 23.0); + profile.Fill(8.5, 25.0); + + profile.Fill(9.5, 23.0, RWeight(0.8)); + profile.Fill(9.5, 25.0, RWeight(0.7)); + + EXPECT_EQ(profile.GetBinContent(8).ComputeMean(), 24.0); + EXPECT_FLOAT_EQ(profile.GetBinContent(9).ComputeMean(), 23.933333); +} + TEST(RProfile, Fill) { static constexpr std::size_t Bins = 20; diff --git a/tutorials/hist/histv7/hist010_RProfile.C b/tutorials/hist/histv7/hist010_RProfile.C new file mode 100644 index 0000000000000..8c27f8153d67b --- /dev/null +++ b/tutorials/hist/histv7/hist010_RProfile.C @@ -0,0 +1,90 @@ +/// \file +/// \ingroup tutorial_histv7 +/// +/// Profile histograms with RProfile. +/// +/// \macro_code +/// \macro_output +/// +/// \date July 2026 +/// \author The ROOT Team + +#include +#include +#include + +#include +#include +#include + +void hist010_RProfile() +{ + // Create an axis that can be used for multiple histograms. + ROOT::Experimental::RRegularAxis axis(40, {0.0, 20.0}); + + // Create a profile histogram and fill with random values. + ROOT::Experimental::RProfile profile(40, {0.0, 20.0}); + + std::mt19937 gen; + // Create a first normal distribution with mean 10.0 and stddev 4.0. + std::normal_distribution normal1(10.0, 4.0); + // Create a second normal distribution for the value. + std::normal_distribution normal2(40.0, 10.0); + for (std::size_t i = 0; i < 1000; i++) { + double x = normal1(gen); + double v = normal2(gen); + profile.Fill(x, v); + } + + // Print (some of) the global statistics. + std::cout << "entries = " << profile.GetNEntries() << "\n"; + std::cout << "binned mean = " << profile.ComputeMean(0); + std::cout << ", stddev = " << profile.ComputeStdDev(0); + std::cout << "\n"; + + // "Draw" the entries of the profile histogram with ASCII characters. The height is hard-coded to work for this + // tutorial. + for (int row = 8; row > 0; row--) { + auto print = [&](ROOT::Experimental::RBinIndex bin) { + const auto &content = profile.GetBinContent(bin); + static constexpr int Scale = 10; + std::cout << (content.fSum >= (row * Scale) ? '*' : ' '); + }; + + // First the underflow bin, separated by a vertical bar. + print(ROOT::Experimental::RBinIndex::Underflow()); + std::cout << '|'; + + // Now iterate the normal bins and print a '*' if the value is sufficiently large. + for (auto bin : axis.GetNormalRange()) { + print(bin); + } + + // Finally the overflow bin after a separating vertical bar. + std::cout << '|'; + print(ROOT::Experimental::RBinIndex::Overflow()); + std::cout << "\n"; + } + + std::cout << "\n"; + std::cout << "value mean = " << profile.ComputeMean(1); + std::cout << ", stddev = " << profile.ComputeStdDev(1); + std::cout << "\n"; + + auto printBin = [&](ROOT::Experimental::RBinIndex bin) { + const auto &content = profile.GetBinContent(bin); + std::cout << "entries = " << content.fSum << ", mean = " << content.ComputeMean(); + }; + + std::cout << "underflow bin: "; + printBin(ROOT::Experimental::RBinIndex::Underflow()); + std::cout << "\n"; + + std::cout << "bin #5: "; + printBin(5); + std::cout << "\n"; + + std::cout << "bin #10: "; + printBin(10); + std::cout << "\n"; +} diff --git a/tutorials/hist/histv7/index.md b/tutorials/hist/histv7/index.md index 4dac2c2d43b70..efdc82c34ec91 100644 --- a/tutorials/hist/histv7/index.md +++ b/tutorials/hist/histv7/index.md @@ -1,4 +1,4 @@ -\defgroup tutorial_histv7 RHist tutorials +\defgroup tutorial_histv7 Experimental histogram tutorials \ingroup tutorial_hist Examples demonstrating ROOT's histogram package. @@ -10,6 +10,7 @@ Examples demonstrating ROOT's histogram package. | hist003_RHist_multi.C | Multidimensional RHist with different axis types. | | hist004_RHist_concurrent.C | Concurrent filling of RHist. | | hist005_RHist_convert_TH1.C | Conversion of RHist to TH1. | +| hist010_RProfile.C | Profile histograms with RProfile. | A second set of tutorials demonstrates usage with RDataFrame.