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