File indexing completed on 2025-12-14 10:31:16
0001
0002
0003
0004
0005 #ifndef ROOT_RHistStats
0006 #define ROOT_RHistStats
0007
0008 #include "RHistUtils.hxx"
0009 #include "RLinearizedIndex.hxx"
0010 #include "RWeight.hxx"
0011
0012 #include <cmath>
0013 #include <cstdint>
0014 #include <stdexcept>
0015 #include <tuple>
0016 #include <vector>
0017
0018 class TBuffer;
0019
0020 namespace ROOT {
0021 namespace Experimental {
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 class RHistStats final {
0039 public:
0040
0041 struct RDimensionStats final {
0042 double fSumWX = 0.0;
0043 double fSumWX2 = 0.0;
0044 double fSumWX3 = 0.0;
0045 double fSumWX4 = 0.0;
0046
0047 void Add(double x)
0048 {
0049 fSumWX += x;
0050 fSumWX2 += x * x;
0051 fSumWX3 += x * x * x;
0052 fSumWX4 += x * x * x * x;
0053 }
0054
0055 void Add(double x, double w)
0056 {
0057 fSumWX += w * x;
0058 fSumWX2 += w * x * x;
0059 fSumWX3 += w * x * x * x;
0060 fSumWX4 += w * x * x * x * x;
0061 }
0062
0063 void Add(const RDimensionStats &other)
0064 {
0065 fSumWX += other.fSumWX;
0066 fSumWX2 += other.fSumWX2;
0067 fSumWX3 += other.fSumWX3;
0068 fSumWX4 += other.fSumWX4;
0069 }
0070
0071 void Clear()
0072 {
0073 fSumWX = 0.0;
0074 fSumWX2 = 0.0;
0075 fSumWX3 = 0.0;
0076 fSumWX4 = 0.0;
0077 }
0078 };
0079
0080 private:
0081
0082 std::uint64_t fNEntries = 0;
0083
0084 double fSumW = 0.0;
0085
0086 double fSumW2 = 0.0;
0087
0088 std::vector<RDimensionStats> fDimensionStats;
0089
0090 public:
0091
0092
0093
0094 explicit RHistStats(std::size_t nDimensions)
0095 {
0096 if (nDimensions == 0) {
0097 throw std::invalid_argument("nDimensions must be > 0");
0098 }
0099 fDimensionStats.resize(nDimensions);
0100 }
0101
0102 std::size_t GetNDimensions() const { return fDimensionStats.size(); }
0103
0104 std::uint64_t GetNEntries() const { return fNEntries; }
0105 double GetSumW() const { return fSumW; }
0106 double GetSumW2() const { return fSumW2; }
0107
0108 const RDimensionStats &GetDimensionStats(std::size_t dim = 0) const { return fDimensionStats.at(dim); }
0109
0110
0111
0112
0113
0114
0115 void Add(const RHistStats &other)
0116 {
0117 if (fDimensionStats.size() != other.fDimensionStats.size()) {
0118 throw std::invalid_argument("number of dimensions not identical in Add");
0119 }
0120 fNEntries += other.fNEntries;
0121 fSumW += other.fSumW;
0122 fSumW2 += other.fSumW2;
0123 for (std::size_t i = 0; i < fDimensionStats.size(); i++) {
0124 fDimensionStats[i].Add(other.fDimensionStats[i]);
0125 }
0126 }
0127
0128
0129 void Clear()
0130 {
0131 fNEntries = 0;
0132 fSumW = 0;
0133 fSumW2 = 0;
0134 for (std::size_t i = 0; i < fDimensionStats.size(); i++) {
0135 fDimensionStats[i].Clear();
0136 }
0137 }
0138
0139
0140
0141
0142
0143
0144
0145
0146 double ComputeNEffectiveEntries() const
0147 {
0148 if (fSumW2 == 0) {
0149 return 0;
0150 }
0151 return fSumW * fSumW / fSumW2;
0152 }
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162 double ComputeMean(std::size_t dim = 0) const
0163 {
0164
0165 auto &stats = fDimensionStats.at(dim);
0166 if (fSumW == 0) {
0167 return 0;
0168 }
0169 return stats.fSumWX / fSumW;
0170 }
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188 double ComputeVariance(std::size_t dim = 0) const
0189 {
0190
0191 auto &stats = fDimensionStats.at(dim);
0192 if (fSumW == 0) {
0193 return 0;
0194 }
0195 double mean = ComputeMean(dim);
0196 return stats.fSumWX2 / fSumW - mean * mean;
0197 }
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215 double ComputeStdDev(std::size_t dim = 0) const { return std::sqrt(ComputeVariance(dim)); }
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232 double ComputeSkewness(std::size_t dim = 0) const
0233 {
0234
0235 auto &stats = fDimensionStats.at(dim);
0236 if (fSumW == 0) {
0237 return 0;
0238 }
0239 double mean = ComputeMean(dim);
0240 double var = ComputeVariance(dim);
0241 if (var == 0) {
0242 return 0;
0243 }
0244 double EWX3 = stats.fSumWX3 / fSumW;
0245 double EWX2 = stats.fSumWX2 / fSumW;
0246 return (EWX3 - 3 * EWX2 * mean + 2 * mean * mean * mean) / std::pow(var, 1.5);
0247 }
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269 double ComputeKurtosis(std::size_t dim = 0) const
0270 {
0271
0272 auto &stats = fDimensionStats.at(dim);
0273 if (fSumW == 0) {
0274 return 0;
0275 }
0276 double mean = ComputeMean(dim);
0277 double var = ComputeVariance(dim);
0278 if (var == 0) {
0279 return 0;
0280 }
0281 double EWX4 = stats.fSumWX4 / fSumW;
0282 double EWX3 = stats.fSumWX3 / fSumW;
0283 double EWX2 = stats.fSumWX2 / fSumW;
0284 return (EWX4 - 4 * EWX3 * mean + 6 * EWX2 * mean * mean - 3 * mean * mean * mean * mean) / (var * var) - 3;
0285 }
0286
0287 private:
0288 template <std::size_t I, typename... A>
0289 void FillImpl(const std::tuple<A...> &args)
0290 {
0291 fDimensionStats[I].Add(std::get<I>(args));
0292 if constexpr (I + 1 < sizeof...(A)) {
0293 FillImpl<I + 1>(args);
0294 }
0295 }
0296
0297 template <std::size_t I, std::size_t N, typename... A>
0298 void FillImpl(const std::tuple<A...> &args, double w)
0299 {
0300 fDimensionStats[I].Add(std::get<I>(args), w);
0301 if constexpr (I + 1 < N) {
0302 FillImpl<I + 1, N>(args, w);
0303 }
0304 }
0305
0306 public:
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321 template <typename... A>
0322 void Fill(const std::tuple<A...> &args)
0323 {
0324 if (sizeof...(A) != fDimensionStats.size()) {
0325 throw std::invalid_argument("invalid number of arguments to Fill");
0326 }
0327 fNEntries++;
0328 fSumW++;
0329 fSumW2++;
0330 FillImpl<0>(args);
0331 }
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346 template <typename... A>
0347 void Fill(const std::tuple<A...> &args, RWeight weight)
0348 {
0349 if (sizeof...(A) != fDimensionStats.size()) {
0350 throw std::invalid_argument("invalid number of arguments to Fill");
0351 }
0352 fNEntries++;
0353 double w = weight.fValue;
0354 fSumW += w;
0355 fSumW2 += w * w;
0356 FillImpl<0, sizeof...(A)>(args, w);
0357 }
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377 template <typename... A>
0378 void Fill(const A &...args)
0379 {
0380 auto t = std::forward_as_tuple(args...);
0381 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
0382 static constexpr std::size_t N = sizeof...(A) - 1;
0383 if (N != fDimensionStats.size()) {
0384 throw std::invalid_argument("invalid number of arguments to Fill");
0385 }
0386 fNEntries++;
0387 double w = std::get<N>(t).fValue;
0388 fSumW += w;
0389 fSumW2 += w * w;
0390 FillImpl<0, N>(t, w);
0391 } else {
0392 Fill(t);
0393 }
0394 }
0395
0396
0397 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistStats"); }
0398 };
0399
0400 }
0401 }
0402
0403 #endif