File indexing completed on 2025-04-19 09:13:39
0001
0002
0003
0004
0005
0006 #ifndef YODA_BinnedEstimate_h
0007 #define YODA_BinnedEstimate_h
0008
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/BinnedStorage.h"
0011 #include "YODA/Estimate.h"
0012 #include "YODA/Scatter.h"
0013 #include "YODA/Transformation.h"
0014 #include "YODA/Utils/BinnedUtils.h"
0015 #include <regex>
0016
0017 namespace YODA {
0018
0019
0020 namespace {
0021
0022 template<size_t axisN, typename BinT>
0023 std::pair<double, double> nullifyIfDisc(const BinT& , const double , std::false_type, const double null = 0.0) {
0024 return { null, null };
0025 }
0026
0027
0028
0029 template<size_t axisN, typename BinT>
0030 std::pair<double, double> nullifyIfDisc(const BinT& b, const double val, std::true_type, const double = 0.0) {
0031 return { val - b.template min<axisN>(), b.template max<axisN>() - val };
0032 }
0033
0034
0035 template<size_t axisN, typename BinT>
0036 double unifyIfDisc(const BinT& , std::false_type, const double unity = 1.0) {
0037 return unity;
0038 }
0039
0040
0041 template<size_t axisN, typename BinT>
0042 double unifyIfDisc(const BinT& b, std::true_type, const double = 1.0) {
0043 return b.template width<axisN>();
0044 }
0045
0046
0047 template<size_t axisN, typename BinT>
0048 double coordPicker(const BinT& b, std::false_type, std::false_type) {
0049 return b.index();
0050 }
0051
0052
0053 template<size_t axisN, typename BinT>
0054 double coordPicker(const BinT& b, std::true_type, std::false_type) {
0055 return b.template edge<axisN>();
0056 }
0057
0058 template<size_t axisN, typename BinT>
0059 double coordPicker(const BinT& b, std::true_type, std::true_type) {
0060 return b.template mid<axisN>();
0061 }
0062 }
0063
0064
0065
0066 template <typename... AxisT>
0067 class BinnedEstimate;
0068
0069
0070
0071
0072
0073
0074 template<typename... AxisT>
0075 class EstimateStorage
0076 : public BinnedStorage<Estimate, AxisT...>,
0077 public AnalysisObject {
0078 protected:
0079
0080 using BaseT = BinnedStorage<Estimate, AxisT...>;
0081 using BinningT = typename BaseT::BinningT;
0082 using BinT = typename BaseT::BinT;
0083
0084 public:
0085
0086 using BinType = BinT;
0087 using AnalysisObject::operator =;
0088
0089
0090
0091
0092
0093 EstimateStorage(const std::string& path = "", const std::string& title = "")
0094 : BaseT(), AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0095
0096
0097
0098
0099
0100
0101 EstimateStorage(std::vector<AxisT>&&... binsEdges, const std::string& path = "", const std::string& title = "")
0102 : BaseT(Axis<AxisT>(std::move(binsEdges))...), AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0103
0104
0105
0106
0107
0108
0109 EstimateStorage(const std::vector<AxisT>&... binsEdges, const std::string& path = "", const std::string& title = "")
0110 : BaseT(Axis<AxisT>(binsEdges)...), AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0111
0112
0113
0114
0115
0116
0117 EstimateStorage(std::initializer_list<AxisT>&&... binsEdges, const std::string& path = "", const std::string& title = "")
0118 : BaseT(Axis<AxisT>(std::move(binsEdges))...), AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0119
0120
0121 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT...>>
0122 EstimateStorage(const std::vector<size_t>& nBins,
0123 const std::vector<std::pair<EdgeT, EdgeT>>& limitsLowUp,
0124 const std::string& path = "", const std::string& title = "")
0125
0126 : BaseT( _mkBinning(nBins, limitsLowUp, std::make_index_sequence<sizeof...(AxisT)>{}) ),
0127 AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0128
0129
0130 EstimateStorage(const BinningT& binning, const std::string& path = "", const std::string& title = "")
0131 : BaseT(binning), AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0132
0133
0134 EstimateStorage(BinningT&& binning, const std::string& path = "", const std::string& title = "")
0135 : BaseT(std::move(binning)), AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0136
0137
0138 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT...>>
0139 EstimateStorage(const ScatterND<sizeof...(AxisT)+1>& s, const std::string& path = "", const std::string& title = "")
0140 : BaseT(_mkBinning(s, std::make_index_sequence<sizeof...(AxisT)>{})),
0141 AnalysisObject(mkTypeString<AxisT...>(), path, title) { }
0142
0143
0144
0145
0146 EstimateStorage(const EstimateStorage& other, const std::string& path = "") : BaseT(other),
0147 AnalysisObject(mkTypeString<AxisT...>(), path!=""? path : other.path(), other, other.title()) { }
0148
0149
0150
0151
0152 EstimateStorage(EstimateStorage&& other, const std::string& path = "") : BaseT(std::move(other)),
0153 AnalysisObject(mkTypeString<AxisT...>(), path!=""? path : other.path(), other, other.title()) { }
0154
0155
0156 EstimateStorage clone() const noexcept {
0157 return EstimateStorage(*this);
0158 }
0159
0160
0161 EstimateStorage* newclone() const noexcept {
0162 return new EstimateStorage(*this);
0163 }
0164
0165
0166
0167
0168
0169
0170
0171 private:
0172
0173
0174 template<size_t... Is>
0175 void _renderYODA_aux(std::ostream& os, const int width, std::index_sequence<Is...>) const noexcept {
0176
0177
0178 BaseT::_binning._renderYODA(os);
0179
0180
0181
0182 const std::vector<std::string> labels = this->sources();
0183 if (labels.size()) {
0184 os << "ErrorLabels: [";
0185 for (size_t i = 0; i < labels.size(); ++i) {
0186 const std::string& src = labels[i];
0187 if (i) os << ", ";
0188 os << std::quoted(src);
0189 }
0190 os << "]\n";
0191 }
0192
0193
0194 os << std::setw(width) << std::left << "# value" << "\t";
0195 const int errwidth = std::max(int(std::to_string(labels.size()).size()+7), width);
0196 for (size_t i = 0; i < labels.size(); ++i) {
0197 const std::string& src = labels[i];
0198 if (src.empty()) {
0199 os << std::setw(errwidth) << std::left << "totalDn" << "\t"
0200 << std::setw(errwidth) << std::left << "totalUp" << "\t";
0201 }
0202 else {
0203 os << std::setw(errwidth) << std::left << ("errDn(" + std::to_string(i+1) + ")") << "\t"
0204 << std::setw(errwidth) << std::left << ("errUp(" + std::to_string(i+1) + ")") << "\t";
0205 }
0206 }
0207 os << "\n";
0208
0209 for (const auto& b : BaseT::bins(true, true)) {
0210 os << std::setw(width) << std::left << b.val() << "\t";
0211
0212 for (const std::string& src : labels) {
0213 if (!b.hasSource(src)) {
0214 os << std::setw(errwidth) << std::left << "---" << "\t"
0215 << std::setw(errwidth) << std::left << "---" << "\t";
0216 continue;
0217 }
0218 const auto& err = b.err(src);
0219 os << std::setw(errwidth) << std::left << err.first << "\t"
0220 << std::setw(errwidth) << std::left << err.second << "\t";
0221 }
0222 os << "\n";
0223 }
0224 }
0225
0226 public:
0227
0228
0229
0230
0231
0232 void _renderYODA(std::ostream& os, const int width = 13) const noexcept {
0233 _renderYODA_aux(os, width, std::make_index_sequence<sizeof...(AxisT)>{});
0234 }
0235
0236
0237 void _renderFLAT(std::ostream& os, const int width = 13) const noexcept {
0238 const ScatterND<sizeof...(AxisT)+1> tmp = mkScatter();
0239 tmp._renderYODA(os, width);
0240 }
0241
0242
0243
0244
0245
0246
0247
0248
0249 void scale(const double scalefactor) noexcept {
0250 setAnnotation("ScaledBy", annotation<double>("ScaledBy", 1.0) * scalefactor);
0251 for (auto& bin : BaseT::_bins) {
0252 bin.scale(scalefactor);
0253 }
0254 }
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265 template <size_t axisN>
0266 void rebinBy(unsigned int n, size_t begin=1, size_t end=UINT_MAX) {
0267 if (n < 1) throw UserError("Rebinning requested in groups of 0!");
0268 if (!begin) throw UserError("Visible bins start with index 1!");
0269 if (end > BaseT::numBinsAt(axisN)+1) end = BaseT::numBinsAt(axisN) + 1;
0270 for (size_t m = begin; m < end; ++m) {
0271 if (m > BaseT::numBinsAt(axisN)+1) break;
0272 const size_t myend = (m+n-1 < BaseT::numBinsAt(axisN)+1) ? m+n-1 : BaseT::numBinsAt(axisN);
0273 if (myend > m) {
0274 BaseT::template mergeBins<axisN>({m, myend});
0275 end -= myend-m;
0276 }
0277 }
0278 }
0279
0280
0281 template <size_t axisN>
0282 void rebin(unsigned int n, size_t begin=1, size_t end=UINT_MAX) {
0283 rebinBy<axisN>(n, begin, end);
0284 }
0285
0286
0287 template <size_t axisN>
0288 void rebinTo(const std::vector<typename BinningT::template getAxisT<axisN>::EdgeT>& newedges) {
0289 if (newedges.size() < 2)
0290 throw UserError("Requested rebinning to an edge list which defines no bins");
0291 using thisAxisT = typename BinningT::template getAxisT<axisN>;
0292 using thisEdgeT = typename thisAxisT::EdgeT;
0293
0294 thisAxisT& oldAxis = BaseT::_binning.template axis<axisN>();
0295 const thisAxisT newAxis(std::move(newedges));
0296 const std::vector<thisEdgeT> eshared = oldAxis.sharedEdges(newAxis);
0297 if (eshared.size() != newAxis.edges().size())
0298 throw BinningError("Requested rebinning to incompatible edges");
0299
0300 for (size_t begin = 0; begin < eshared.size() - 1; ++begin) {
0301
0302
0303 size_t end = oldAxis.index(eshared[begin+1]) - 1;
0304
0305
0306 if (begin == newAxis.numBins(true)-1) end = oldAxis.numBins(true)-1;
0307
0308 if (end > begin) BaseT::template mergeBins<axisN>({begin, end});
0309 if (eshared.size() == oldAxis.edges().size()) break;
0310 }
0311 }
0312
0313
0314 template <size_t axisN>
0315 void rebin(const std::vector<typename BinningT::template getAxisT<axisN>::EdgeT>& newedges) {
0316 rebinTo<axisN>(std::move(newedges));
0317 }
0318
0319
0320
0321
0322 void reset() noexcept { BaseT::clearBins(); }
0323
0324
0325 EstimateStorage& operator = (const EstimateStorage& est) noexcept {
0326 if (this != &est) {
0327 AnalysisObject::operator = (est);
0328 BaseT::operator = (est);
0329 }
0330 return *this;
0331 }
0332
0333
0334 EstimateStorage& operator = (EstimateStorage&& est) noexcept {
0335 if (this != &est) {
0336 AnalysisObject::operator = (est);
0337 BaseT::operator = (std::move(est));
0338 }
0339 return *this;
0340 }
0341
0342
0343
0344
0345
0346 EstimateStorage& add(const EstimateStorage& est,
0347 const std::string& pat_uncorr="^stat|^uncor" ) {
0348 if (*this != est)
0349 throw BinningError("Arithmetic operation requires compatible binning!");
0350 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0351 for (size_t i = 0; i< BaseT::numBins(true, true); ++i) {
0352 BaseT::bin(i).add(est.bin(i), pat_uncorr);
0353 }
0354 BaseT::maskBins(est.maskedBins(), true);
0355 return *this;
0356 }
0357
0358 EstimateStorage& operator += (const EstimateStorage& est) {
0359 return add(est);
0360 }
0361
0362
0363
0364
0365
0366 EstimateStorage& add(EstimateStorage&& est,
0367 const std::string& pat_uncorr="^stat|^uncor" ) {
0368 if (*this != est)
0369 throw BinningError("Arithmetic operation requires compatible binning!");
0370 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0371 for (size_t i = 0; i< BaseT::numBins(true, true); ++i) {
0372 BaseT::bin(i).add(std::move(est.bin(i)), pat_uncorr);
0373 }
0374 BaseT::maskBins(est.maskedBins(), true);
0375 return *this;
0376 }
0377
0378 EstimateStorage& operator += (EstimateStorage&& est) {
0379 return add(std::move(est));
0380 }
0381
0382
0383
0384
0385
0386
0387 EstimateStorage& subtract(const EstimateStorage& est,
0388 const std::string& pat_uncorr="^stat|^uncor" ) {
0389 if (*this != est)
0390 throw BinningError("Arithmetic operation requires compatible binning!");
0391 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0392 for (size_t i = 0; i< BaseT::numBins(true, true); ++i) {
0393 BaseT::bin(i).subtract(est.bin(i), pat_uncorr);
0394 }
0395 BaseT::maskBins(est.maskedBins(), true);
0396 return *this;
0397 }
0398
0399 EstimateStorage& operator -= (const EstimateStorage& est) {
0400 return subtract(est);
0401 }
0402
0403
0404 EstimateStorage& subtract(EstimateStorage&& est,
0405 const std::string& pat_uncorr="^stat|^uncor" ) {
0406 if (*this != est)
0407 throw BinningError("Arithmetic operation requires compatible binning!");
0408 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0409 for (size_t i = 0; i< BaseT::numBins(true, true); ++i) {
0410 BaseT::bin(i) -= std::move(est.bin(i), pat_uncorr);
0411 }
0412 BaseT::maskBins(est.maskedBins(), true);
0413 return *this;
0414 }
0415
0416 EstimateStorage& operator -= (EstimateStorage&& est) {
0417 return subtract(std::move(est));
0418 }
0419
0420
0421
0422
0423
0424
0425
0426
0427 size_t dim() const noexcept { return sizeof...(AxisT) + 1; }
0428
0429
0430 std::string _config() const noexcept { return mkAxisConfig<AxisT...>(); }
0431
0432
0433
0434
0435
0436 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0437 std::vector<E> edges(const bool includeOverflows = false) const noexcept {
0438 return BaseT::_binning.template edges<I>(includeOverflows);
0439 }
0440
0441
0442
0443
0444
0445
0446
0447 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0448 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
0449 widths(const bool includeOverflows = false) const noexcept {
0450 return BaseT::_binning.template widths<I>(includeOverflows);
0451 }
0452
0453
0454
0455
0456 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0457 std::enable_if_t<std::is_floating_point<E>::value, E> min() const noexcept {
0458 return BaseT::_binning.template min<I>();
0459 }
0460
0461
0462
0463
0464 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0465 std::enable_if_t<std::is_floating_point<E>::value, E> max() const noexcept {
0466 return BaseT::_binning.template max<I>();
0467 }
0468
0469
0470
0471
0472
0473
0474
0475
0476 std::vector<double> vals(const bool includeOverflows=false,
0477 const bool includeMaskedBins=false) const {
0478 std::vector<double> rtn;
0479 rtn.reserve(BaseT::numBins(includeOverflows, includeMaskedBins));
0480 for (const auto& b : BaseT::bins(includeOverflows, includeMaskedBins)) {
0481 rtn.push_back(b.val());
0482 }
0483 return rtn;
0484 }
0485
0486
0487 std::vector<std::string> sources() const {
0488
0489
0490 std::vector<std::string> rtn;
0491
0492 for (const auto& b : BaseT::bins(true)) {
0493 std::vector<std::string> keys = b.sources();
0494 rtn.insert(std::end(rtn),
0495 std::make_move_iterator(std::begin(keys)),
0496 std::make_move_iterator(std::end(keys)));
0497 }
0498 std::sort(rtn.begin(), rtn.end());
0499 rtn.erase( std::unique(rtn.begin(), rtn.end()), rtn.end() );
0500
0501 return rtn;
0502 }
0503
0504
0505
0506
0507 double areaUnderCurve(const bool includeBinVol=true,
0508 const bool includeOverflows=false,
0509 const bool includeMaskedBins=false) const {
0510 double ret = 0.;
0511 for (const auto& b : BaseT::bins(includeOverflows, includeMaskedBins)) {
0512 const double val = fabs(b.val());
0513 const double vol = includeBinVol? b.dVol() : 1.0;
0514 if (std::isfinite(vol)) ret += val*vol;
0515 }
0516 return ret;
0517 }
0518
0519
0520 double auc(const bool includeBinVol=true,
0521 const bool includeOverflows=false,
0522 const bool includeMaskedBins=false) const {
0523 return areaUnderCurve(includeBinVol, includeOverflows, includeMaskedBins);
0524 }
0525
0526
0527
0528
0529 std::vector<std::vector<double> > covarianceMatrix(const bool ignoreOffDiagonalTerms=false,
0530 const bool includeOverflows=false,
0531 const bool includeMaskedBins=false,
0532 const std::string& pat_uncorr="^stat|^uncor") const {
0533 const size_t nBins = BaseT::numBins(includeOverflows,includeMaskedBins);
0534 std::vector<std::vector<double> > covM(nBins);
0535
0536
0537 for (size_t i = 0; i < nBins; ++i) {
0538 covM[i] = std::vector<double>(nBins, 0.0);
0539 }
0540
0541 const std::vector<std::string> error_sources = sources();
0542
0543
0544 if (error_sources.size() == 1 && error_sources[0] == "") {
0545 size_t i = 0;
0546 for (const auto& b : BaseT::bins(includeOverflows,includeMaskedBins)) {
0547 covM[i][i] = b.hasSource("")? sqr(0.5*(b.err("").first+b.err("").second)) : 1.0;
0548 ++i;
0549 }
0550 return covM;
0551 }
0552
0553
0554 std::smatch match;
0555 const std::regex re(pat_uncorr, std::regex_constants::icase);
0556 for (const std::string& sname : error_sources) {
0557 if (sname == "") continue;
0558 std::vector<double> systErrs(nBins, 0.0);
0559 size_t k = 0;
0560 for (const auto& b : BaseT::bins(includeOverflows,includeMaskedBins)) {
0561 if (b.hasSource(sname)) {
0562 const auto& errs = b.err(sname);
0563 systErrs[k] = 0.5 *( fabs(errs.first)+fabs(errs.second));
0564 }
0565 ++k;
0566 }
0567 const bool skipOffDiag = (ignoreOffDiagonalTerms
0568 || std::regex_search(sname, match, re));
0569 for (size_t i = 0; i < nBins; ++i) {
0570 for (size_t j = 0; j < nBins; ++j) {
0571 if (skipOffDiag && i != j) continue;
0572 covM[i][j] += systErrs[i]*systErrs[j];
0573 }
0574 }
0575 }
0576
0577 return covM;
0578 }
0579
0580
0581
0582
0583
0584
0585
0586 auto mkScatter(const std::string& path="", const std::string& pat_match = "",
0587 const bool includeOverflows=false,
0588 const bool includeMaskedBins=false) const {
0589
0590 constexpr size_t N = sizeof...(AxisT);
0591
0592 ScatterND<N+1> rtn;
0593 for (const std::string& a : annotations()) {
0594 if (a != "Type") rtn.setAnnotation(a, annotation(a));
0595 }
0596 rtn.setAnnotation("Path", path);
0597
0598 const bool incOF = all_CAxes<AxisT...>::value && includeOverflows;
0599 for (const auto& b : BaseT::bins(incOF, includeMaskedBins)) {
0600
0601
0602 Utils::ndarray<double, N+1> vals;
0603
0604 auto indexIfDiscrete = [&vals, &b](auto I) {
0605 using isContinuous = typename BinningT::template is_CAxis<I>;
0606 using isArithmetic = typename BinningT::template is_Arithmetic<I>;
0607 vals[I] = coordPicker<I>(b, std::integral_constant<bool, isArithmetic::value>{},
0608 std::integral_constant<bool, isContinuous::value>{});
0609 };
0610 MetaUtils::staticFor<BinningT::Dimension::value>(indexIfDiscrete);
0611
0612 vals[N] = b.val();
0613
0614
0615 Utils::ndarray<std::pair<double,double>, N+1> errs;
0616 auto nullifyDiscrete = [&errs, &vals, &b](auto I) {
0617 using isContinuous = typename BinningT::template is_CAxis<I>;
0618 errs[I] = nullifyIfDisc<I>(b, vals[I], std::integral_constant<bool, isContinuous::value>{});
0619 };
0620 MetaUtils::staticFor<BinningT::Dimension::value>(nullifyDiscrete);
0621 const double tot = fabs(b.totalErrPos(pat_match));
0622 errs[N] = { tot, tot };
0623
0624
0625 rtn.addPoint( PointND<N+1>(vals, errs) );
0626 }
0627
0628
0629 const BinningT& binning = BaseT::_binning;
0630 auto decorateEdges = [&rtn, &binning](auto I) {
0631 using isContinuous = typename BinningT::template is_CAxis<I>;
0632 if constexpr( !isContinuous::value ) {
0633 const auto& axis = binning.template axis<I>();
0634 if (axis.numBins()) {
0635 std::stringstream ss;
0636 axis._renderYODA(ss);
0637 rtn.setAnnotation("EdgesA" + std::to_string(I+1), ss.str());
0638 }
0639 }
0640 };
0641 MetaUtils::staticFor<BinningT::Dimension::value>(decorateEdges);
0642
0643 return rtn;
0644 }
0645
0646
0647
0648
0649
0650 template<size_t axisN, typename = std::enable_if_t< (axisN < sizeof...(AxisT) && sizeof...(AxisT)>=2) >>
0651 auto mkEstimates(const std::string& path="", const bool includeOverflows=false) const {
0652
0653
0654 auto how2add = [](auto& pivot, const BinType& toCopy) { pivot = toCopy; };
0655 auto rtn = BaseT::template mkBinnedSlices<axisN, BinnedEstimate>(how2add, includeOverflows);
0656 for (const std::string& a : annotations()) {
0657 if (a == "Type") continue;
0658 for (size_t i = 0; i < rtn.size(); ++i) {
0659 rtn[i].setAnnotation(a, annotation(a));
0660 }
0661 }
0662 for (size_t i = 0; i < rtn.size(); ++i) {
0663 rtn[i].setAnnotation("Path", path);
0664 }
0665
0666 return rtn;
0667 }
0668
0669
0670
0671 AnalysisObject* mkInert(const std::string& path = "",
0672 const std::string& source = "") const noexcept {
0673 EstimateStorage* rtn = newclone();
0674 rtn->setPath(path);
0675 for (auto& b : rtn->bins(true, true)) {
0676 if (b.numErrs() == 1) {
0677 try {
0678 b.renameSource("", source);
0679 }
0680 catch (YODA::UserError& e) { }
0681 }
0682 }
0683 return rtn;
0684 }
0685
0686
0687
0688
0689
0690
0691 size_t lengthContent(bool fixed_length = false) const noexcept {
0692 size_t rtn = 0;
0693 for (const auto& bin : BaseT::bins(true, true)) {
0694 rtn += bin._lengthContent(fixed_length);
0695 }
0696 return rtn;
0697 }
0698
0699 std::vector<double> serializeContent(bool fixed_length = false) const noexcept {
0700 std::vector<double> rtn;
0701 const size_t nBins = BaseT::numBins(true, true);
0702 rtn.reserve(nBins * 4);
0703 for (size_t i = 0; i < nBins; ++i) {
0704 const auto& b = BaseT::bin(i);
0705 std::vector<double> bdata = b._serializeContent(fixed_length);
0706 rtn.insert(std::end(rtn),
0707 std::make_move_iterator(std::begin(bdata)),
0708 std::make_move_iterator(std::end(bdata)));
0709 }
0710 return rtn;
0711 }
0712
0713
0714 void deserializeContent(const std::vector<double>& data) {
0715
0716 const size_t nBins = BaseT::numBins(true, true);
0717 const size_t minLen = 2*nBins;
0718 if (data.size() < minLen)
0719 throw UserError("Length of serialized data should be at least " + std::to_string(minLen)+"!");
0720
0721 size_t i = 0;
0722 auto itr = data.cbegin();
0723 const auto itrEnd = data.cend();
0724 const bool fixedLen = data.size() == 2*minLen;
0725 while (itr != itrEnd) {
0726
0727
0728 const size_t nErrs = fixedLen? 1 : (*(itr + 1) + 0.5);
0729 auto last = itr + 2*(nErrs+1);
0730 BaseT::bin(i)._deserializeContent(std::vector<double>{itr, last}, fixedLen);
0731
0732 itr = last;
0733 ++i;
0734 }
0735 }
0736
0737
0738
0739 private:
0740
0741
0742
0743 template<size_t... Is>
0744 BinningT _mkBinning(const std::vector<size_t>& nBins,
0745 const std::vector<std::pair<double, double>>& limitsLowUp,
0746 std::index_sequence<Is...>) const {
0747 return BinningT({((void)Is, Axis<AxisT>(nBins[Is], limitsLowUp[Is].first, limitsLowUp[Is].second))...});
0748 }
0749
0750
0751 template<size_t... Is>
0752 BinningT _mkBinning(const ScatterND<sizeof...(AxisT)+1>& s, std::index_sequence<Is...>) const {
0753 return BinningT({((void)Is, Axis<AxisT>(s.edges(Is)))...});
0754 }
0755
0756 };
0757
0758
0759
0760
0761 template <typename... AxisT>
0762 class BinnedEstimate : public EstimateStorage<AxisT...> {
0763 public:
0764 using EstimateT = BinnedEstimate<AxisT...>;
0765 using BaseT = EstimateStorage<AxisT...>;
0766 using BinType = typename BaseT::BinT;
0767 using Ptr = std::shared_ptr<EstimateT>;
0768
0769
0770 using BaseT::BaseT;
0771
0772 BinnedEstimate(const EstimateT&) = default;
0773 BinnedEstimate(EstimateT&&) = default;
0774 BinnedEstimate& operator =(const EstimateT&) = default;
0775 BinnedEstimate& operator =(EstimateT&&) = default;
0776 using AnalysisObject::operator =;
0777
0778
0779
0780
0781 BinnedEstimate(const BaseT& other) : BaseT(other) {}
0782
0783 BinnedEstimate(const EstimateT& other, const std::string& path) : BaseT(other, path) {}
0784
0785
0786 BinnedEstimate(BaseT&& other) : BaseT(std::move(other)) {}
0787
0788 BinnedEstimate(EstimateT&& other, const std::string& path) : BaseT(std::move(other), path) {}
0789
0790 };
0791
0792
0793
0794 template <typename AxisT>
0795 class BinnedEstimate<AxisT>
0796 : public EstimateStorage<AxisT>,
0797 public XAxisMixin<BinnedEstimate<AxisT>, AxisT> {
0798 public:
0799 using EstimateT = BinnedEstimate<AxisT>;
0800 using BaseT = EstimateStorage<AxisT>;
0801 using BinType = typename BaseT::BinT;
0802 using Ptr = std::shared_ptr<EstimateT>;
0803
0804
0805 using BaseT::BaseT;
0806
0807 BinnedEstimate(const EstimateT&) = default;
0808 BinnedEstimate(EstimateT&&) = default;
0809 BinnedEstimate& operator =(const EstimateT&) = default;
0810 BinnedEstimate& operator =(EstimateT&&) = default;
0811 using AnalysisObject::operator =;
0812
0813
0814
0815
0816 BinnedEstimate(const BaseT& other) : BaseT(other) {}
0817
0818 BinnedEstimate(const EstimateT& other, const std::string& path) : BaseT(other, path) {}
0819
0820
0821 BinnedEstimate(BaseT&& other) : BaseT(std::move(other)) {}
0822
0823 BinnedEstimate(EstimateT&& other, const std::string& path) : BaseT(std::move(other), path) {}
0824
0825
0826 BinnedEstimate(std::vector<AxisT>&& edges, const std::string& path="", const std::string& title="")
0827 : BaseT(std::move(edges), path, title) {}
0828
0829 BinnedEstimate(const std::vector<AxisT>& edges, const std::string& path="", const std::string& title="")
0830 : BaseT(edges, path, title) {}
0831
0832
0833
0834
0835
0836
0837
0838 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT>>
0839 BinnedEstimate(size_t nbins, double lower, double upper, const std::string& path = "", const std::string& title = "")
0840 : BaseT({nbins}, {{lower, upper}}, path, title) {}
0841
0842
0843 EstimateT clone() const noexcept {
0844 return EstimateT(*this);
0845 }
0846
0847
0848 EstimateT* newclone() const noexcept {
0849 return new EstimateT(*this);
0850 }
0851
0852
0853 size_t indexAt(const AxisT xCoord) const noexcept {
0854 return BaseT::binAt( {xCoord} ).index();
0855 }
0856
0857
0858 void maskBinAt(const AxisT xCoord, const bool status = true) noexcept {
0859 return BaseT::maskBin({xCoord}, status);
0860 }
0861
0862 };
0863
0864
0865
0866
0867 template <typename AxisT1, typename AxisT2>
0868 class BinnedEstimate<AxisT1, AxisT2>
0869 : public EstimateStorage<AxisT1, AxisT2>,
0870 public XAxisMixin<BinnedEstimate<AxisT1, AxisT2>, AxisT1>,
0871 public YAxisMixin<BinnedEstimate<AxisT1, AxisT2>, AxisT2> {
0872 public:
0873 using EstimateT = BinnedEstimate<AxisT1, AxisT2>;
0874 using BaseT = EstimateStorage<AxisT1, AxisT2>;
0875 using BinType = typename BaseT::BinT;
0876 using Ptr = std::shared_ptr<EstimateT>;
0877
0878
0879 using BaseT::BaseT;
0880
0881 BinnedEstimate(const EstimateT&) = default;
0882 BinnedEstimate(EstimateT&&) = default;
0883 BinnedEstimate& operator =(const EstimateT&) = default;
0884 BinnedEstimate& operator =(EstimateT&&) = default;
0885 using AnalysisObject::operator =;
0886
0887
0888
0889
0890 BinnedEstimate(const BaseT& other) : BaseT(std::move(other)) {}
0891
0892 BinnedEstimate(const EstimateT& other, const std::string& path) : BaseT(other, path) {}
0893
0894
0895 BinnedEstimate(BaseT&& other) : BaseT(std::move(other)) {}
0896
0897 BinnedEstimate(EstimateT&& other, const std::string& path) : BaseT(std::move(other), path) {}
0898
0899 BinnedEstimate(std::vector<AxisT1>&& xEdges, std::vector<AxisT2>&& yEdges,
0900 const std::string& path="", const std::string& title="")
0901 : BaseT(std::move(xEdges), std::move(yEdges), path, title) {}
0902
0903 BinnedEstimate(const std::vector<AxisT1>& xEdges, const std::vector<AxisT2>& yEdges,
0904 const std::string& path="", const std::string& title="")
0905 : BaseT(xEdges, yEdges, path, title) {}
0906
0907
0908
0909
0910
0911
0912
0913 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT1, AxisT2>>
0914 BinnedEstimate(size_t nbinsX, double lowerX, double upperX,
0915 size_t nbinsY, double lowerY, double upperY,
0916 const std::string& path = "", const std::string& title = "")
0917 : BaseT({nbinsX, nbinsY}, {{lowerX, upperX}, {lowerY, upperY}}, path, title) {}
0918
0919
0920 EstimateT clone() const noexcept {
0921 return EstimateT(*this);
0922 }
0923
0924
0925 EstimateT* newclone() const noexcept {
0926 return new EstimateT(*this);
0927 }
0928
0929
0930 BinType& bin(const size_t index) noexcept {
0931 return BaseT::bin(index);
0932 }
0933
0934
0935 const BinType& bin(const size_t index) const noexcept {
0936 return BaseT::bin(index);
0937 }
0938
0939
0940 BinType& bin(const size_t localX, const size_t localY) noexcept {
0941 return BaseT::bin( {localX, localY} );
0942 }
0943
0944
0945 const BinType& bin(const size_t localX, const size_t localY) const noexcept {
0946 return BaseT::bin( {localX, localY} );
0947 }
0948
0949
0950 BinType& binAt(const AxisT1 xCoord, const AxisT2 yCoord) noexcept {
0951 return BaseT::binAt( {xCoord, yCoord} );
0952 }
0953
0954
0955 const BinType& binAt(const AxisT1 xCoord, const AxisT2 yCoord) const noexcept {
0956 return BaseT::binAt( {xCoord, yCoord} );
0957 }
0958
0959
0960 size_t indexAt(const AxisT1 xCoord, const AxisT2 yCoord) const noexcept {
0961 return BaseT::binAt( {xCoord, yCoord} ).index();
0962 }
0963
0964
0965 void maskBinAt(const AxisT1 xCoord, const AxisT2 yCoord, const bool status = true) noexcept {
0966 return BaseT::maskBin({xCoord, yCoord}, status);
0967 }
0968
0969 };
0970
0971
0972
0973 template <typename AxisT1, typename AxisT2, typename AxisT3>
0974 class BinnedEstimate<AxisT1, AxisT2, AxisT3>
0975 : public EstimateStorage<AxisT1, AxisT2, AxisT3>,
0976 public XAxisMixin<BinnedEstimate<AxisT1, AxisT2, AxisT3>, AxisT1>,
0977 public YAxisMixin<BinnedEstimate<AxisT1, AxisT2, AxisT3>, AxisT2>,
0978 public ZAxisMixin<BinnedEstimate<AxisT1, AxisT2, AxisT3>, AxisT3> {
0979 public:
0980 using EstimateT = BinnedEstimate<AxisT1, AxisT2, AxisT3>;
0981 using BaseT = EstimateStorage<AxisT1, AxisT2, AxisT3>;
0982 using BinType = typename BaseT::BinT;
0983 using Ptr = std::shared_ptr<EstimateT>;
0984
0985
0986 using BaseT::BaseT;
0987
0988 BinnedEstimate(const EstimateT&) = default;
0989 BinnedEstimate(EstimateT&&) = default;
0990 BinnedEstimate& operator =(const EstimateT&) = default;
0991 BinnedEstimate& operator =(EstimateT&&) = default;
0992 using AnalysisObject::operator =;
0993
0994
0995
0996
0997 BinnedEstimate(const BaseT& other) : BaseT(other) {}
0998
0999 BinnedEstimate(const EstimateT& other, const std::string& path) : BaseT(other, path) {}
1000
1001
1002 BinnedEstimate(BaseT&& other) : BaseT(std::move(other)) {}
1003
1004 BinnedEstimate(EstimateT&& other, const std::string& path) : BaseT(std::move(other), path) {}
1005
1006
1007
1008
1009
1010
1011
1012 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT1, AxisT2, AxisT3>>
1013 BinnedEstimate(size_t nbinsX, double lowerX, double upperX,
1014 size_t nbinsY, double lowerY, double upperY,
1015 size_t nbinsZ, double lowerZ, double upperZ,
1016 const std::string& path = "", const std::string& title = "")
1017 : BaseT({nbinsX, nbinsY, nbinsZ}, {{lowerX, upperX}, {lowerY, upperY},
1018 {lowerZ, upperZ}}, path, title) {}
1019
1020
1021 EstimateT clone() const noexcept {
1022 return EstimateT(*this);
1023 }
1024
1025
1026 EstimateT* newclone() const noexcept {
1027 return new EstimateT(*this);
1028 }
1029
1030
1031 BinType& bin(const size_t index) noexcept {
1032 return BaseT::bin(index);
1033 }
1034
1035
1036 const BinType& bin(const size_t index) const noexcept {
1037 return BaseT::bin(index);
1038 }
1039
1040
1041 BinType& bin(const size_t localX, const size_t localY, const size_t localZ) noexcept {
1042 return BaseT::bin( {localX, localY, localZ} );
1043 }
1044
1045
1046 const BinType& bin(const size_t localX, const size_t localY, const size_t localZ) const noexcept {
1047 return BaseT::bin( {localX, localY, localZ} );
1048 }
1049
1050
1051 BinType& binAt(const AxisT1 xCoord, const AxisT2 yCoord, const AxisT3 zCoord) noexcept {
1052 return BaseT::binAt( {xCoord, yCoord, zCoord} );
1053 }
1054
1055
1056 const BinType& binAt(const AxisT1 xCoord, const AxisT2 yCoord, const AxisT3 zCoord) const noexcept {
1057 return BaseT::binAt( {xCoord, yCoord, zCoord} );
1058 }
1059
1060
1061 size_t indexAt(const AxisT1 xCoord, const AxisT2 yCoord, const AxisT3 zCoord) const noexcept {
1062 return BaseT::binAt( {xCoord, yCoord, zCoord} ).index();
1063 }
1064
1065
1066 void maskBinAt(const AxisT1 xCoord, const AxisT2 yCoord, const AxisT3 zCoord, const bool status = true) noexcept {
1067 return BaseT::maskBin({xCoord, yCoord, zCoord}, status);
1068 }
1069
1070 };
1071
1072
1073
1074
1075
1076 template <typename... AxisT>
1077 inline BinnedEstimate<AxisT...>
1078 operator + (BinnedEstimate<AxisT...> first, const BinnedEstimate<AxisT...>& second) {
1079 first += second;
1080 return first;
1081 }
1082
1083
1084
1085 template <typename... AxisT>
1086 inline BinnedEstimate<AxisT...>
1087 operator + (BinnedEstimate<AxisT...> first, BinnedEstimate<AxisT...>&& second) {
1088 first += std::move(second);
1089 return first;
1090 }
1091
1092
1093
1094 template <typename... AxisT>
1095 inline BinnedEstimate< AxisT...>
1096 operator - (BinnedEstimate<AxisT...> first, const BinnedEstimate<AxisT...>& second) {
1097 first -= second;
1098 return first;
1099 }
1100
1101
1102 template <typename... AxisT>
1103 inline BinnedEstimate< AxisT...>
1104 operator - (BinnedEstimate<AxisT...> first, BinnedEstimate<AxisT...>&& second) {
1105 first -= std::move(second);
1106 return first;
1107 }
1108
1109
1110 template <typename... AxisT>
1111 inline BinnedEstimate<AxisT...>
1112 divide(const BinnedEstimate<AxisT...>& numer, const BinnedEstimate<AxisT...>& denom,
1113 const std::string& pat_uncorr="^stat|^uncor" ) {
1114 if (numer != denom) {
1115 throw BinningError("Arithmetic operation requires compatible binning!");
1116 }
1117
1118 BinnedEstimate<AxisT...> rtn(numer.binning());
1119 if (numer.path() == denom.path()) rtn.setPath(numer.path());
1120 if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy");
1121
1122 for (const auto& b_num : numer.bins(true, true)) {
1123 const size_t idx = b_num.index();
1124 rtn.bin(idx) = divide(b_num, denom.bin(idx), pat_uncorr);
1125 }
1126 rtn.maskBins(denom.maskedBins(), true);
1127
1128 return rtn;
1129 }
1130
1131
1132 template <typename... AxisT>
1133 inline BinnedEstimate<AxisT...>
1134 operator / (const BinnedEstimate<AxisT...>& numer, const BinnedEstimate<AxisT...>& denom) {
1135 return divide(numer, denom);
1136 }
1137
1138
1139 template <typename... AxisT>
1140 inline BinnedEstimate<AxisT...>
1141 operator / (BinnedEstimate<AxisT...>&& numer, const BinnedEstimate<AxisT...>& denom) {
1142 return divide(std::move(numer), denom);
1143 }
1144
1145
1146 template <typename... AxisT>
1147 inline BinnedEstimate<AxisT...>
1148 operator / (const BinnedEstimate<AxisT...>& numer, BinnedEstimate<AxisT...>&& denom) {
1149 return divide(numer, std::move(denom));
1150 }
1151
1152
1153 template <typename... AxisT>
1154 inline BinnedEstimate<AxisT...>
1155 operator / (BinnedEstimate<AxisT...>&& numer, BinnedEstimate<AxisT...>&& denom) {
1156 return divide(std::move(numer), std::move(denom));
1157 }
1158
1159
1160
1161
1162
1163
1164 template <typename... AxisT>
1165 inline BinnedEstimate<AxisT...>
1166 efficiency(const BinnedEstimate<AxisT...>& accepted, const BinnedEstimate<AxisT...>& total,
1167 const std::string& pat_uncorr="^stat|^uncor" ) {
1168
1169 if (accepted != total) {
1170 throw BinningError("Arithmetic operation requires compatible binning!");
1171 }
1172
1173 BinnedEstimate<AxisT...> rtn(accepted.binning());
1174
1175 for (const auto& b_acc : accepted.bins(true, true)) {
1176 Estimate est;
1177 const size_t idx = b_acc.index();
1178 try {
1179 est = efficiency(b_acc, total.bin(idx), pat_uncorr);
1180 } catch (const UserError& e) {
1181
1182 }
1183 rtn.bin(idx).set(est);
1184 }
1185 return rtn;
1186 }
1187
1188
1189
1190 template <typename... AxisT>
1191 inline BinnedEstimate<AxisT...>
1192 asymm(const BinnedEstimate<AxisT...>& a,
1193 const BinnedEstimate<AxisT...>& b,
1194 const std::string& pat_uncorr="^stat|^uncor" ) {
1195 return divde(subtract(a-b, pat_uncorr), add(a+b, pat_uncorr), pat_uncorr);
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205 template <typename... AxisT>
1206 inline void transform(BinnedEstimate<AxisT...>& est, const Trf<1>& fn) {
1207 for (auto& b : est.bins(true, true)) {
1208 b.transform(fn);
1209 }
1210 }
1211
1212 template <typename... AxisT, typename FN>
1213 inline void transform(BinnedEstimate<AxisT...>& est, const FN& fn) {
1214 transform(est, Trf<1>(fn));
1215 }
1216
1217
1218
1219
1220
1221
1222
1223
1224 template<typename A1>
1225 using BinnedEstimate1D = BinnedEstimate<A1>;
1226
1227 template <typename A1, typename A2>
1228 using BinnedEstimate2D = BinnedEstimate<A1, A2>;
1229
1230 template <typename A1, typename A2, typename A3>
1231 using BinnedEstimate3D = BinnedEstimate<A1, A2, A3>;
1232
1233
1234 namespace {
1235 template <class T>
1236 struct EstimateMaker;
1237
1238 template<size_t... Is>
1239 struct EstimateMaker<std::index_sequence<Is...>> {
1240 using type = BinnedEstimate< std::decay_t<decltype((void)Is, std::declval<double>())>... >;
1241 };
1242 }
1243
1244 template<size_t N>
1245 using EstimateND = typename EstimateMaker<std::make_index_sequence<N>>::type;
1246
1247
1248
1249 using Estimate1D = BinnedEstimate<double>;
1250 using Estimate2D = BinnedEstimate<double,double>;
1251 using Estimate3D = BinnedEstimate<double,double,double>;
1252
1253
1254
1255 }
1256
1257 #endif