Skip to content

Commit 18c5821

Browse files
committed
[hist] Integrate RHistStats into RProfile
1 parent 654d8f2 commit 18c5821

3 files changed

Lines changed: 112 additions & 8 deletions

File tree

hist/histv7/inc/ROOT/RHistUtils.hxx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
#ifndef ROOT_RHistUtils
66
#define ROOT_RHistUtils
77

8+
#include <tuple>
89
#include <type_traits>
10+
#include <utility>
911

1012
#ifdef _MSC_VER
1113
#include <atomic>
@@ -25,6 +27,19 @@ struct LastType<T> {
2527
using type = T;
2628
};
2729

30+
template <typename... Ts, std::size_t... I, typename A>
31+
std::tuple<const Ts &..., const A &>
32+
AppendReferenceImpl(const std::tuple<Ts...> &t, std::index_sequence<I...>, const A &a)
33+
{
34+
return std::tuple<const Ts &..., const A &>(std::get<I>(t)..., a);
35+
}
36+
37+
template <typename... Ts, typename A>
38+
std::tuple<const Ts &..., const A &> AppendReference(const std::tuple<Ts...> &t, const A &a)
39+
{
40+
return AppendReferenceImpl(t, std::make_index_sequence<sizeof...(Ts)>(), a);
41+
}
42+
2843
#ifdef _MSC_VER
2944
namespace MSVC {
3045
template <std::size_t N>

hist/histv7/inc/ROOT/RProfile.hxx

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "RAxisVariant.hxx"
99
#include "RBinIndex.hxx"
1010
#include "RHistEngine.hxx"
11+
#include "RHistStats.hxx"
1112
#include "RHistUtils.hxx"
1213
#include "RRegularAxis.hxx"
1314
#include "RWeight.hxx"
@@ -97,12 +98,23 @@ public:
9798
private:
9899
/// The histogram engine including the bin contents.
99100
RHistEngine<RProfileBin> fEngine;
101+
/// The global histogram statistics.
102+
RHistStats fStats;
100103

101104
public:
102105
/// Construct a profile histogram.
103106
///
104107
/// \param[in] axes the axis objects, must have size > 0
105-
explicit RProfile(std::vector<RAxisVariant> axes) : fEngine(std::move(axes)) {}
108+
explicit RProfile(std::vector<RAxisVariant> axes) : fEngine(std::move(axes)), fStats(fEngine.GetNDimensions() + 1)
109+
{
110+
// The axes parameter was moved, use from the engine.
111+
const auto &engineAxes = fEngine.GetAxes();
112+
for (std::size_t i = 0; i < engineAxes.size(); i++) {
113+
if (engineAxes[i].GetCategoricalAxis() != nullptr) {
114+
fStats.DisableDimension(i);
115+
}
116+
}
117+
}
106118

107119
/// Construct a profile histogram.
108120
///
@@ -163,11 +175,29 @@ public:
163175
/// \{
164176

165177
const RHistEngine<RProfileBin> &GetEngine() const { return fEngine; }
178+
const RHistStats &GetStats() const { return fStats; }
166179

167180
const std::vector<RAxisVariant> &GetAxes() const { return fEngine.GetAxes(); }
168181
std::size_t GetNDimensions() const { return fEngine.GetNDimensions(); }
169182
std::uint64_t GetTotalNBins() const { return fEngine.GetTotalNBins(); }
170183

184+
std::uint64_t GetNEntries() const { return fStats.GetNEntries(); }
185+
186+
/// \}
187+
/// \name Computations
188+
/// \{
189+
190+
/// \copydoc RHistStats::ComputeNEffectiveEntries()
191+
double ComputeNEffectiveEntries() const { return fStats.ComputeNEffectiveEntries(); }
192+
/// \copydoc RHistStats::ComputeMean()
193+
double ComputeMean(std::size_t dim = 0) const { return fStats.ComputeMean(dim); }
194+
/// \copydoc RHistStats::ComputeStdDev()
195+
double ComputeStdDev(std::size_t dim = 0) const { return fStats.ComputeStdDev(dim); }
196+
197+
/// \}
198+
/// \name Accessors
199+
/// \{
200+
171201
/// Get the content of a single bin.
172202
///
173203
/// \code
@@ -266,6 +296,8 @@ public:
266296
{
267297
RValueWrapper wrapper(value);
268298
fEngine.Fill(args, wrapper);
299+
// Avoid a second conversion of value, which we already did in wrapper.
300+
fStats.Fill(Internal::AppendReference(args, wrapper.fValue));
269301
}
270302

271303
/// Fill an entry into the profile histogram with a weight.
@@ -293,6 +325,8 @@ public:
293325
{
294326
RValueWeightWrapper wrapper(value, weight.fValue);
295327
fEngine.Fill(args, wrapper);
328+
// Avoid a second conversion of value, which we already did in wrapper.
329+
fStats.Fill(Internal::AppendReference(args, wrapper.fValue), weight);
296330
}
297331

298332
/// Fill an entry into the profile histogram.
@@ -340,6 +374,7 @@ public:
340374
RValueWrapper wrapper(std::get<N>(t));
341375
fEngine.FillImpl<N>(t, wrapper);
342376
}
377+
fStats.Fill(args...);
343378
}
344379
}
345380

hist/histv7/test/hist_profile.cxx

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ TEST(RProfile, Constructor)
2323
EXPECT_EQ(profile.GetNDimensions(), 2);
2424
const auto &engine = profile.GetEngine();
2525
EXPECT_EQ(engine.GetNDimensions(), 2);
26+
const auto &stats = profile.GetStats();
27+
EXPECT_EQ(stats.GetNDimensions(), 3);
2628
EXPECT_EQ(profile.GetAxes().size(), 2);
2729
// Both axes include underflow and overflow bins.
2830
EXPECT_EQ(profile.GetTotalNBins(), (Bins + 2) * (Bins + 2));
@@ -73,6 +75,13 @@ TEST(RProfile, Fill)
7375
EXPECT_EQ(bin9.fSumValues2, 625.0);
7476
EXPECT_EQ(bin9.fSum, 1.0);
7577
EXPECT_EQ(bin9.fSum2, 1.0);
78+
79+
EXPECT_EQ(profile.GetNEntries(), 2);
80+
EXPECT_FLOAT_EQ(profile.ComputeNEffectiveEntries(), 2);
81+
EXPECT_FLOAT_EQ(profile.ComputeMean(0), 9);
82+
EXPECT_FLOAT_EQ(profile.ComputeStdDev(0), 0.5);
83+
EXPECT_FLOAT_EQ(profile.ComputeMean(1), 24.0);
84+
EXPECT_FLOAT_EQ(profile.ComputeStdDev(1), 1.0);
7685
}
7786

7887
TEST(RProfile, FillInvalidNumberOfArguments)
@@ -111,6 +120,14 @@ TEST(RProfile, FillWeight)
111120
EXPECT_FLOAT_EQ(bin9.fSumValues2, 562.5);
112121
EXPECT_FLOAT_EQ(bin9.fSum, 0.9);
113122
EXPECT_FLOAT_EQ(bin9.fSum2, 0.81);
123+
124+
EXPECT_EQ(profile.GetNEntries(), 2);
125+
EXPECT_FLOAT_EQ(profile.GetStats().GetSumW(), 1.7);
126+
EXPECT_FLOAT_EQ(profile.GetStats().GetSumW2(), 1.45);
127+
// Cross-checked with TH1
128+
EXPECT_FLOAT_EQ(profile.ComputeNEffectiveEntries(), 1.9931034);
129+
EXPECT_FLOAT_EQ(profile.ComputeMean(0), 9.0294118);
130+
EXPECT_FLOAT_EQ(profile.ComputeStdDev(0), 0.49913420);
114131
}
115132

116133
TEST(RProfile, FillWeightInvalidNumberOfArguments)
@@ -151,6 +168,9 @@ TEST(RProfile, FillCategorical)
151168
EXPECT_EQ(bin2.fSumValues2, 625.0);
152169
EXPECT_EQ(bin2.fSum, 1.0);
153170
EXPECT_EQ(bin2.fSum2, 1.0);
171+
172+
EXPECT_EQ(profile.GetNEntries(), 2);
173+
EXPECT_FLOAT_EQ(profile.ComputeNEffectiveEntries(), 2);
154174
}
155175

156176
TEST(RProfile, FillCategoricalWeight)
@@ -173,6 +193,37 @@ TEST(RProfile, FillCategoricalWeight)
173193
EXPECT_FLOAT_EQ(bin2.fSumValues2, 562.5);
174194
EXPECT_FLOAT_EQ(bin2.fSum, 0.9);
175195
EXPECT_FLOAT_EQ(bin2.fSum2, 0.81);
196+
197+
EXPECT_EQ(profile.GetNEntries(), 2);
198+
EXPECT_FLOAT_EQ(profile.GetStats().GetSumW(), 1.7);
199+
EXPECT_FLOAT_EQ(profile.GetStats().GetSumW2(), 1.45);
200+
// Cross-checked with TH1
201+
EXPECT_FLOAT_EQ(profile.ComputeNEffectiveEntries(), 1.9931034);
202+
}
203+
204+
TEST(RProfile, FillExceptionSafety)
205+
{
206+
static constexpr std::size_t Bins = 20;
207+
const RRegularAxis axis(Bins, {0, Bins});
208+
RProfile profile({axis, axis});
209+
210+
profile.Fill(1.5, 2.5, 3.5);
211+
ASSERT_EQ(profile.GetNEntries(), 1);
212+
ASSERT_EQ(profile.GetBinContent(RBinIndex(1), RBinIndex(2)).fSumValues, 3.5);
213+
214+
EXPECT_THROW(profile.Fill(1.5, "b", 3.5), std::invalid_argument);
215+
EXPECT_THROW(profile.Fill(std::make_tuple(1.5, "b"), 3.5), std::invalid_argument);
216+
EXPECT_THROW(profile.Fill(1.5, "b", 3.5, RWeight(1)), std::invalid_argument);
217+
EXPECT_THROW(profile.Fill(std::make_tuple(1.5, "b"), 3.5, RWeight(1)), std::invalid_argument);
218+
219+
// Verify exception safety. Only the first entry should be there.
220+
EXPECT_EQ(profile.GetNEntries(), 1);
221+
EXPECT_EQ(profile.GetBinContent(RBinIndex(1), RBinIndex(2)).fSumValues, 3.5);
222+
EXPECT_EQ(profile.GetStats().GetSumW(), 1);
223+
EXPECT_EQ(profile.GetStats().GetSumW2(), 1);
224+
EXPECT_EQ(profile.GetStats().GetDimensionStats(0).fSumWX, 1.5);
225+
EXPECT_EQ(profile.GetStats().GetDimensionStats(1).fSumWX, 2.5);
226+
EXPECT_EQ(profile.GetStats().GetDimensionStats(2).fSumWX, 3.5);
176227
}
177228

178229
class CopyArgument final {
@@ -201,20 +252,23 @@ int CopyArgument::gCopies = 0;
201252
TEST(RProfile, FillForward)
202253
{
203254
static constexpr std::size_t Bins = 20;
204-
RProfile profile(Bins, {0, Bins});
255+
const RRegularAxis axis(Bins, {0, Bins});
256+
RProfile profile(axis, axis);
205257
CopyArgument value(23.0);
206258

207-
std::tuple<CopyArgument> args(1.5);
259+
std::tuple<CopyArgument, CopyArgument> args(1.5, 2.5);
208260
profile.Fill(args, value);
209261
profile.Fill(args, value, RWeight(0.5));
210-
EXPECT_EQ(profile.GetBinContent(1).fSumValues, 34.5);
262+
EXPECT_EQ(profile.GetNEntries(), 2);
263+
EXPECT_EQ(profile.GetBinContent(1, 2).fSumValues, 34.5);
211264

212265
ASSERT_FALSE(CopyArgument::HasBeenCopied());
213266

214-
CopyArgument arg(2.5);
215-
profile.Fill(arg, value);
216-
profile.Fill(arg, value, RWeight(0.5));
217-
EXPECT_EQ(profile.GetBinContent(2).fSumValues, 34.5);
267+
CopyArgument arg1(3.5), arg2(4.5);
268+
profile.Fill(arg1, arg2, value);
269+
profile.Fill(arg1, arg2, value, RWeight(0.5));
270+
EXPECT_EQ(profile.GetNEntries(), 4);
271+
EXPECT_EQ(profile.GetBinContent(3, 4).fSumValues, 34.5);
218272

219273
ASSERT_FALSE(CopyArgument::HasBeenCopied());
220274
}

0 commit comments

Comments
 (0)