Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RAxis.hxx
Go to the documentation of this file.
1/// \file ROOT/RAxis.hxx
2/// \ingroup Hist ROOT7
3/// \author Axel Naumann <axel@cern.ch>
4/// \date 2015-03-23
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2015, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#ifndef ROOT7_RAxis
17#define ROOT7_RAxis
18
19#include <algorithm>
20#include <cmath>
21#include <limits>
22#include <string>
23#include <unordered_map>
24#include <vector>
25
26#include "ROOT/RAxisConfig.hxx"
27#include "ROOT/RStringView.hxx"
28#include "ROOT/RLogger.hxx"
29
30namespace ROOT {
31namespace Experimental {
32
33/**
34 \class RAxisBase
35 Histogram axis base class. Keeps track of the number of bins and overflow
36 handling. Offers bin iteration.
37
38 Regular bin indices are starting from 1, up to N + 1 for an axis with N bins.
39 Index -1 is for the underflow bin, representing values that are lower than
40 the axis range. Index -2 is the overflow bin for values larger than the axis
41 range.
42 Growable axes do not have underflow or overflow bins, as they don't need them.
43 */
44class RAxisBase {
45protected:
46 ///\name Inaccessible copy, assignment
47 /// The copy and move constructors and assignment operators are protected to
48 /// prevent slicing.
49 ///\{
50 RAxisBase(const RAxisBase &) = default;
51 RAxisBase(RAxisBase &&) = default;
52 RAxisBase &operator=(const RAxisBase &) = default;
54 ///\}
55
56 /// Default construct a RAxisBase (for use by derived classes for I/O)
57 RAxisBase() noexcept(noexcept(std::string())) = default;
58
59 /// Virtual destructor needed in this inheritance-based design
60 virtual ~RAxisBase();
61
62 /// Construct a RAxisBase.
63 ///
64 ///\param[in] title - axis title used for graphics and text representation.
65 RAxisBase(std::string_view title) noexcept: fTitle(title) {}
66
67 /// Given rawbin (`<0` for underflow, `>=GetNBinsNoOver()` for overflow),
68 /// determine the bin number taking into account how over/underflow
69 /// should be handled.
70 ///
71 /// \param[in] rawbin for which to determine the bin number.
72 /// \return Returns the bin number adjusted for potential over- and underflow
73 /// bins. Returns `kInvalidBin` if the axis cannot handle the over- / underflow.
74 ///
75 int AdjustOverflowBinNumber(double rawbin) const
76 {
77 ++rawbin;
78
79 // Underflow: Put in underflow bin if any, otherwise ignore
80 if (rawbin < GetFirstBin())
81 return CanGrow() ? kInvalidBin : GetUnderflowBin();
82
83 // Overflow: Put in overflow bin if any, otherwise ignore
84 // `rawbin` is not an integer, cannot compare `rawbin > GetLastBin()`.
85 if (rawbin >= GetLastBin() + 1)
86 return CanGrow() ? kInvalidBin : GetOverflowBin();
87
88 // Bin index is in range and has been corrected for over/underflow
89 return (int)rawbin;
90 }
91
92 /// Check if two axis have the same bin borders
93 ///
94 /// Default implementation should work for any RAxis type, but is quite
95 /// inefficient as it does virtual GetBinFrom calls in a loop. RAxis
96 /// implementations are encouraged to provide optimized overrides for common
97 /// axis binning comparison scenarios.
98 virtual bool HasSameBinBordersAs(const RAxisBase& other) const {
99 // Axis growability (and thus under/overflow bin existence) must match
100 if (CanGrow() != other.CanGrow())
101 return false;
102
103 // Number of normal bins must match
104 if (GetNBinsNoOver() != other.GetNBinsNoOver())
105 return false;
106
107 // Left borders of normal bins must match
108 for (int bin: *this)
109 if (GetBinFrom(bin) != other.GetBinFrom(bin))
110 return false;
111
112 // Right border of the last normal bin (aka maximum) must also match
113 if (GetMaximum() != other.GetMaximum())
114 return false;
115
116 // If all of these checks passed, the two axes have the same bin borders
117 return true;
118 }
119
120public:
121 /**
122 \class const_iterator
123 Random const_iterator through bins. Represents the bin index, not a bin
124 content: the axis has no notion of any content.
125 */
126 class const_iterator: public std::iterator<std::random_access_iterator_tag, int /*value*/, int /*distance*/,
127 const int * /*pointer*/, const int & /*ref*/> {
128 int fCursor = 0; ///< Current iteration position
129
130 public:
131 const_iterator() = default;
132
133 /// Initialize a const_iterator with its position
134 explicit const_iterator(int cursor) noexcept: fCursor(cursor) {}
135
136 /// ++i
138 {
139 // Could check whether fCursor < fEnd - but what for?
140 ++fCursor;
141 return *this;
142 }
143
144 /// --i
146 {
147 // Could check whether fCursor > fBegin - but what for?
148 --fCursor;
149 return *this;
150 }
151
152 /// i++
154 {
155 const_iterator old(*this);
156 ++(*this);
157 return old;
158 }
159
160 // i--
162 {
163 const_iterator old(*this);
164 --(*this);
165 return old;
166 }
167
168 // i += 2
170 {
171 fCursor += d;
172 return *this;
173 }
174
175 // i -= 2
177 {
178 fCursor -= d;
179 return *this;
180 }
181
182 // i + 2
184 {
185 const_iterator ret(*this);
186 ret += d;
187 return ret;
188 }
189 friend const_iterator operator+(int d, const_iterator rhs) noexcept;
190
191 // i - 2
193 {
194 const_iterator ret(*this);
195 ret -= d;
196 return ret;
197 }
198
199 // i - j
200 int operator-(const const_iterator& j) noexcept
201 {
202 return fCursor - j.fCursor;
203 }
204
205 // i[2]
206 int operator[](int d) noexcept
207 {
208 return fCursor + d;
209 }
210
211 // *i
212 int operator*() const noexcept { return fCursor; }
213
214 // i->
215 const int *operator->() const noexcept { return &fCursor; }
216
217 friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept;
218 friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept;
219 friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept;
220 friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept;
221 friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept;
222 friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept;
223 };
224
225 /// Special bin index returned to signify that no bin matches a request.
226 constexpr static const int kInvalidBin = 0;
227
228 /// Index of the underflow bin, if any.
229 constexpr static const int kUnderflowBin = -1;
230
231 /// Index of the overflow bin, if any.
232 constexpr static const int kOverflowBin = -2;
233
234 /// Get the axis's title
235 const std::string &GetTitle() const { return fTitle; }
236
237 /// Whether this axis can grow (and thus has no overflow bins).
238 virtual bool CanGrow() const noexcept = 0;
239
240 /// Get the number of bins, excluding under- and overflow.
241 virtual int GetNBinsNoOver() const noexcept = 0;
242
243 /// Get the number of bins, including under- and overflow.
244 int GetNBins() const noexcept { return GetNBinsNoOver() + GetNOverflowBins(); }
245
246 /// Get the number of over- and underflow bins: 0 for growable axes, 2 otherwise.
247 int GetNOverflowBins() const noexcept
248 {
249 if (CanGrow())
250 return 0;
251 else
252 return 2;
253 };
254
255 /// Get the bin index for the underflow bin (or `kInvalidBin`
256 /// if CanGrow()).
257 int GetUnderflowBin() const noexcept {
258 if (CanGrow())
259 return kInvalidBin;
260 else
261 return kUnderflowBin;
262 }
263
264 /// Get the bin index for the overflow bin (or `kInvalidBin`
265 /// if CanGrow()).
266 int GetOverflowBin() const noexcept {
267 if (CanGrow())
268 return kInvalidBin;
269 else
270 return kOverflowBin;
271 }
272
273 /// Get the bin index for the first bin of the axis
274 int GetFirstBin() const noexcept { return 1; }
275
276 /// Get the bin index for the last bin of the axis
277 int GetLastBin() const noexcept { return GetNBinsNoOver(); }
278
279 ///\name Iterator interfaces
280 ///\{
281
282 /// Get a const_iterator pointing to the first regular bin.
283 const_iterator begin() const noexcept { return const_iterator{GetFirstBin()}; }
284
285 /// Get a const_iterator pointing beyond the last regular bin
286 const_iterator end() const noexcept { return const_iterator{GetLastBin() + 1}; }
287 ///\}
288
289 /// Find the adjusted bin index (returning `kUnderflowBin` for underflow and `kOverflowBin`
290 /// for overflow) for the given coordinate.
291 /// \note Passing a bin border coordinate can either return the bin above or
292 /// below the bin border. I.e. don't do that for reliable results!
293 virtual int FindBin(double x) const noexcept = 0;
294
295 /// Get the bin center for the given bin index.
296 /// The result of this method on an overflow or underflow bin is unspecified.
297 virtual double GetBinCenter(int bin) const = 0;
298
299 /// Get the low bin border ("left edge") for the given bin index.
300 /// The result of this method on an underflow bin is unspecified.
301 virtual double GetBinFrom(int bin) const = 0;
302
303 /// Get the high bin border ("right edge") for the given bin index.
304 /// The result of this method on an overflow bin is unspecified.
305 double GetBinTo(int bin) const {
306 const double result = (bin == kUnderflowBin) ? GetMinimum() : GetBinFrom(bin + 1);
307 return result;
308 }
309
310 /// Get the low end of the axis range.
311 double GetMinimum() const { return GetBinFrom(GetFirstBin()); }
312
313 /// Get the high end of the axis range.
314 double GetMaximum() const { return GetBinTo(GetLastBin()); }
315
316 /// Check if two axes use the same binning convention, i.e.
317 ///
318 /// - Either they are both growable or neither of them is growable.
319 /// - Minimum, maximum, and all bin borders in the middle are the same.
320 /// - Bin labels must match (exactly including order, for now).
321 bool HasSameBinningAs(const RAxisBase& other) const;
322
323 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
324 /// return the bin for which this is a low edge. If it's not a bin edge,
325 /// return `kInvalidBin`.
326 virtual int GetBinIndexForLowEdge(double x) const noexcept = 0;
327
328private:
329 std::string fTitle; ///< Title of this axis, used for graphics / text.
330};
331
332///\name RAxisBase::const_iterator external operators
333///\{
334
335/// 2 + i
337{
338 return rhs + d;
339}
340
341/// i < j
343{
344 return lhs.fCursor < rhs.fCursor;
345}
346
347/// i > j
349{
350 return lhs.fCursor > rhs.fCursor;
351}
352
353/// i <= j
355{
356 return lhs.fCursor <= rhs.fCursor;
357}
358
359/// i >= j
361{
362 return lhs.fCursor >= rhs.fCursor;
363}
364
365/// i == j
367{
368 return lhs.fCursor == rhs.fCursor;
369}
370
371/// i != j
373{
374 return lhs.fCursor != rhs.fCursor;
375}
376///\}
377
378/**
379 Axis with equidistant bin borders. Defined by lower l and upper u limit and
380 the number of bins n. All bins have the same width (u-l)/n.
381
382 This axis cannot grow; use `RAxisGrow` for that.
383 */
385protected:
386 double fLow = 0.; ///< The lower limit of the axis
387 double fInvBinWidth = 0.; ///< The inverse of the bin width
388 unsigned int fNBinsNoOver; ///< Number of bins excluding under- and overflow.
389
390 /// Determine the inverse bin width.
391 /// \param nbinsNoOver - number of bins without unter-/overflow
392 /// \param lowOrHigh - first axis boundary
393 /// \param highOrLow - second axis boundary
394 static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
395 {
396 return nbinsNoOver / std::fabs(highOrLow - lowOrHigh);
397 }
398
399 /// See RAxisBase::HasSameBinBordersAs
400 bool HasSameBinBordersAs(const RAxisBase& other) const override;
401
402 /// Find the raw bin index (not adjusted) for the given coordinate.
403 /// The resulting raw bin is 0-based.
404 /// \note Passing a bin border coordinate can either return the bin above or
405 /// below the bin border. I.e. don't do that for reliable results!
406 double FindBinRaw(double x) const noexcept
407 {
408 return (x - fLow) * fInvBinWidth;
409 }
410
411public:
412 RAxisEquidistant() = default;
413
414 /// Initialize a RAxisEquidistant.
415 /// \param[in] title - axis title used for graphics and text representation.
416 /// \param nbinsNoOver - number of bins in the axis, excluding under- and overflow
417 /// bins.
418 /// \param low - the low axis range. Any coordinate below that is considered
419 /// as underflow. The first bin's lower edge is at this value.
420 /// \param high - the high axis range. Any coordinate above that is considered
421 /// as overflow. The last bin's higher edge is at this value.
422 explicit RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
423 : RAxisBase(title)
424 , fLow(low)
425 , fInvBinWidth(GetInvBinWidth(nbinsNoOver, low, high))
426 , fNBinsNoOver(nbinsNoOver)
427 {}
428
429 /// Initialize a RAxisEquidistant.
430 /// \param nbinsNoOver - number of bins in the axis, excluding under- and overflow
431 /// bins.
432 /// \param low - the low axis range. Any coordinate below that is considered
433 /// as underflow. The first bin's lower edge is at this value.
434 /// \param high - the high axis range. Any coordinate above that is considered
435 /// as overflow. The last bin's higher edge is at this value.
436 explicit RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
437 : RAxisEquidistant("", nbinsNoOver, low, high)
438 {}
439
440 /// Convert to RAxisConfig.
441 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetNBinsNoOver(), GetMinimum(), GetMaximum()); }
442
443 /// Get the number of bins, excluding under- and overflow.
444 int GetNBinsNoOver() const noexcept final override { return fNBinsNoOver; }
445
446 /// Find the adjusted bin index (returning `kUnderflowBin` for underflow and
447 /// `kOverflowBin` for overflow) for the given coordinate.
448 /// \note Passing a bin border coordinate can either return the bin above or
449 /// below the bin border. I.e. don't do that for reliable results!
450 int FindBin(double x) const noexcept final override
451 {
452 double rawbin = FindBinRaw(x);
453 return AdjustOverflowBinNumber(rawbin);
454 }
455
456 /// This axis cannot grow.
457 bool CanGrow() const noexcept override { return false; }
458
459 /// Get the width of the bins.
460 double GetBinWidth() const noexcept { return 1. / fInvBinWidth; }
461
462 /// Get the inverse of the width of the bins.
463 double GetInverseBinWidth() const noexcept { return fInvBinWidth; }
464
465 /// Get the bin center for the given bin index.
466 /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
467 /// returns 0.25.
468 /// The result of this method on an overflow or underflow bin is unspecified.
469 double GetBinCenter(int bin) const final override { return fLow + (bin - GetFirstBin() + 0.5) / fInvBinWidth; }
470
471 /// Get the low bin border for the given bin index.
472 /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
473 /// returns 0.
474 /// The result of this method on an underflow bin is unspecified.
475 double GetBinFrom(int bin) const final override {
476 const double result = (bin == kOverflowBin) ? GetMaximum() : fLow + (bin - GetFirstBin()) / fInvBinWidth;
477 return result;
478 }
479
480 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
481 /// return the bin for which this is a low edge. If it's not a bin edge,
482 /// return `kInvalidBin`.
483 int GetBinIndexForLowEdge(double x) const noexcept final override;
484};
485
486namespace Internal {
487
488template <>
489struct AxisConfigToType<RAxisConfig::kEquidistant> {
491
492 Axis_t operator()(const RAxisConfig &cfg) noexcept
493 {
494 return RAxisEquidistant(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
495 }
496};
497
498} // namespace Internal
499
500/** An axis that can extend its range, keeping the number of its bins unchanged.
501 The axis is constructed with an initial range. Apart from its ability to
502 grow, this axis behaves like a RAxisEquidistant.
503 */
505public:
506 /// Initialize a RAxisGrow.
507 /// \param[in] title - axis title used for graphics and text representation.
508 /// \param nbins - number of bins in the axis, excluding under- and overflow
509 /// bins. This value is fixed over the lifetime of the object.
510 /// \param low - the initial value for the low axis range. Any coordinate
511 /// below that is considered as underflow. To trigger the growing of the
512 /// axis call `Grow()`.
513 /// \param high - the initial value for the high axis range. Any coordinate
514 /// above that is considered as overflow. To trigger the growing of the
515 /// axis call `Grow()`.
516 explicit RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
517 : RAxisEquidistant(title, nbins, low, high)
518 {}
519
520 /// Initialize a RAxisGrow.
521 /// \param nbins - number of bins in the axis, excluding under- and overflow
522 /// bins. This value is fixed over the lifetime of the object.
523 /// \param low - the initial value for the low axis range. Any coordinate
524 /// below that is considered as underflow. To trigger the growing of the
525 /// axis call `Grow()`.
526 /// \param high - the initial value for the high axis range. Any coordinate
527 /// above that is considered as overflow. To trigger the growing of the
528 /// axis call `Grow()`.
529 explicit RAxisGrow(int nbins, double low, double high) noexcept: RAxisGrow("", nbins, low, high) {}
530
531 /// Convert to RAxisConfig.
533
534 /// Grow this axis to make the "virtual bin" toBin in-range. This keeps the
535 /// non-affected axis limit unchanged, and extends the other axis limit such
536 /// that a number of consecutive bins are merged.
537 ///
538 /// Example, assuming an initial RAxisGrow with 10 bins from 0. to 1.:
539 /// - `Grow(0)`: that (virtual) bin spans from -0.1 to 0. To include it
540 /// in the axis range, the lower limit must be shifted. The minimal number
541 /// of bins that can be merged is 2, thus the new axis will span from
542 /// -1. to 1.
543 /// - `Grow(-1)`: that (virtual) bin spans from -0.2 to 0.1. To include it
544 /// in the axis range, the lower limit must be shifted. The minimal number
545 /// of bins that can be merged is 2, thus the new axis will span from
546 /// -1. to 1.
547 /// - `Grow(50)`: that (virtual) bin spans from 4.9 to 5.0. To include it
548 /// in the axis range, the higher limit must be shifted. Five bins need to
549 /// be merged, making the new axis range 0. to 5.0.
550 ///
551 /// \param toBin - the "virtual" bin number, as if the axis had an infinite
552 /// number of bins with the current bin width. For instance, for an axis
553 /// with ten bins in the range 0. to 1., the coordinate 2.05 has the virtual
554 /// bin index 20.
555 /// \return Returns the number of bins that were merged to reach the value.
556 /// A value of 1 means that no bins were merged (toBin was in the original
557 /// axis range).
558 int Grow(int toBin);
559
560 /// This axis kind can increase its range.
561 bool CanGrow() const noexcept final override { return true; }
562};
563
564namespace Internal {
565
566template <>
569
570 Axis_t operator()(const RAxisConfig &cfg) noexcept
571 {
572 return RAxisGrow(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
573 }
574};
575
576} // namespace Internal
577
578/**
579 An axis with non-equidistant bins (also known as "variable binning"). It is
580 defined by an array of bin borders - one more than the number of
581 (non-overflow-) bins it has! As an example, an axis with two bin needs three
582 bin borders:
583 - lower edge of the first bin;
584 - higher edge of the first bin, identical to the lower edge of the second
585 bin;
586 - higher edge of the second bin
587
588 This axis cannot grow; the size of new bins would not be well defined.
589 */
591private:
592 /// Bin borders, one more than the number of regular bins.
593 std::vector<double> fBinBorders;
594
595protected:
596 /// See RAxisBase::HasSameBinBordersAs
597 bool HasSameBinBordersAs(const RAxisBase& other) const override;
598
599 /// Find the raw bin index (not adjusted) for the given coordinate `x`.
600 /// The resulting raw bin is 1-based.
601 /// \note Passing a bin border coordinate can either return the bin above or
602 /// below the bin border. I.e. don't do that for reliable results!
603 double FindBinRaw(double x) const noexcept
604 {
605 const auto bBegin = fBinBorders.begin();
606 const auto bEnd = fBinBorders.end();
607 // lower_bound finds the first bin border that is >= x.
608 auto iNotLess = std::lower_bound(bBegin, bEnd, x);
609 return iNotLess - bBegin;
610 }
611
612public:
613 RAxisIrregular() = default;
614
615 /// Construct a RAxisIrregular from a vector of bin borders.
616 /// \note The bin borders must be sorted in increasing order!
617 explicit RAxisIrregular(const std::vector<double> &binborders)
618 : RAxisBase(), fBinBorders(binborders)
619 {
620#ifdef R__DO_RANGE_CHECKS
621 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
622 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
623#endif // R__DO_RANGE_CHECKS
624 }
625
626 /// Construct a RAxisIrregular from a vector of bin borders.
627 /// \note The bin borders must be sorted in increasing order!
628 /// Faster, noexcept version taking an rvalue of binborders. The compiler will
629 /// know when it can take this one.
630 explicit RAxisIrregular(std::vector<double> &&binborders) noexcept
631 : RAxisBase(), fBinBorders(std::move(binborders))
632 {
633#ifdef R__DO_RANGE_CHECKS
634 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
635 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
636#endif // R__DO_RANGE_CHECKS
637 }
638
639 /// Construct a RAxisIrregular from a vector of bin borders.
640 /// \note The bin borders must be sorted in increasing order!
641 explicit RAxisIrregular(std::string_view title, const std::vector<double> &binborders)
642 : RAxisBase(title), fBinBorders(binborders)
643 {
644#ifdef R__DO_RANGE_CHECKS
645 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
646 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
647#endif // R__DO_RANGE_CHECKS
648 }
649
650 /// Construct a RAxisIrregular from a vector of bin borders.
651 /// \note The bin borders must be sorted in increasing order!
652 /// Faster, noexcept version taking an rvalue of binborders. The compiler will
653 /// know when it can take this one.
654 explicit RAxisIrregular(std::string_view title, std::vector<double> &&binborders) noexcept
655 : RAxisBase(title), fBinBorders(std::move(binborders))
656 {
657#ifdef R__DO_RANGE_CHECKS
658 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
659 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
660#endif // R__DO_RANGE_CHECKS
661 }
662
663 /// Convert to RAxisConfig.
664 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinBorders()); }
665
666 /// Get the number of bins, excluding under- and overflow.
667 int GetNBinsNoOver() const noexcept final override { return fBinBorders.size() - 1; }
668
669 /// Find the bin index (adjusted with under- and overflow) for the given coordinate `x`.
670 /// \note Passing a bin border coordinate can either return the bin above or
671 /// below the bin border. I.e. don't do that for reliable results!
672 int FindBin(double x) const noexcept final override
673 {
674 int rawbin = FindBinRaw(x);
675 // No need for AdjustOverflowBinNumber(rawbin) here; lower_bound() is the
676 // answer: e.g. for x < *bBegin, rawbin is -1.
677 if (rawbin < GetFirstBin())
678 return kUnderflowBin;
679 if (rawbin >= GetLastBin() + 1)
680 return kOverflowBin;
681 return rawbin;
682 }
683
684 /// Get the bin center of the bin with the given index.
685 /// The result of this method on an overflow or underflow bin is unspecified.
686 double GetBinCenter(int bin) const final override { return 0.5 * (fBinBorders[bin - 1] + fBinBorders[bin]); }
687
688 /// Get the lower bin border for a given bin index.
689 /// The result of this method on an underflow bin is unspecified.
690 double GetBinFrom(int bin) const final override
691 {
692 if (bin == kOverflowBin)
693 return fBinBorders[GetLastBin()];
694 return fBinBorders[bin - 1];
695 }
696
697 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
698 /// return the bin for which this is a low edge. If it's not a bin edge,
699 /// return `kInvalidBin`.
700 int GetBinIndexForLowEdge(double x) const noexcept final override;
701
702 /// This axis cannot be extended.
703 bool CanGrow() const noexcept final override { return false; }
704
705 /// Access to the bin borders used by this axis.
706 const std::vector<double> &GetBinBorders() const noexcept { return fBinBorders; }
707};
708
709namespace Internal {
710
711template <>
712struct AxisConfigToType<RAxisConfig::kIrregular> {
714
715 Axis_t operator()(const RAxisConfig &cfg) { return RAxisIrregular(cfg.GetTitle(), cfg.GetBinBorders()); }
716};
717
718} // namespace Internal
719
720/**
721 \class RAxisLabels
722 A RAxisGrow that has a label assigned to each bin and a bin width of 1.
723
724 While filling still works through coordinates (i.e. arrays of doubles),
725 RAxisLabels allows to convert a string to a bin number or the bin's coordinate
726 center. The number of labels and the number of bins reported by RAxisGrow might
727 differ: the RAxisGrow will only grow when seeing a Fill(), while the RAxisLabels
728 will add a new label whenever `GetBinCenter()` is called.
729
730 Implementation details:
731 Filling happens often; `GetBinCenter()` needs to be fast. Thus the unordered_map.
732 The painter needs the reverse: it wants the label for bin 0, bin 1 etc. The axis
733 should only store the bin labels once; referencing them is (due to re-allocation,
734 hashing etc) non-trivial. So instead, build a `vector<string_view>` for the few
735 times the axis needs to be painted.
736 */
737class RAxisLabels: public RAxisGrow {
738private:
739 /// Map of label (view on `fLabels`'s elements) to bin index
740 std::unordered_map<std::string, int /*bin number*/> fLabelsIndex;
741
742public:
743 /// Construct a RAxisLables from a `vector` of `string_view`s, with title.
744 explicit RAxisLabels(std::string_view title, const std::vector<std::string_view> &labels)
745 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
746 {
747 for (size_t i = 0, n = labels.size(); i < n; ++i)
748 fLabelsIndex[std::string(labels[i])] = i;
749 }
750
751 /// Construct a RAxisLables from a `vector` of `string`s, with title.
752 explicit RAxisLabels(std::string_view title, const std::vector<std::string> &labels)
753 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
754 {
755 for (size_t i = 0, n = labels.size(); i < n; ++i)
756 fLabelsIndex[labels[i]] = i;
757 }
758
759 /// Construct a RAxisLables from a `vector` of `string_view`s
760 explicit RAxisLabels(const std::vector<std::string_view> &labels): RAxisLabels("", labels) {}
761
762 /// Construct a RAxisLables from a `vector` of `string`s
763 explicit RAxisLabels(const std::vector<std::string> &labels): RAxisLabels("", labels) {}
764
765 /// Convert to RAxisConfig.
766 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinLabels()); }
767
768 /// Get the bin index with label.
769 int FindBinByName(const std::string &label)
770 {
771 auto insertResult = fLabelsIndex.insert({label, -1});
772 if (insertResult.second) {
773 // we have created a new label
774 int idx = fLabelsIndex.size() - 1;
775 insertResult.first->second = idx;
776 return idx;
777 }
778 return insertResult.first->second;
779 }
780
781 /// Get the center of the bin with label.
782 double GetBinCenterByName(const std::string &label)
783 {
784 return FindBinByName(label) + 0.5; // bin *center*
785 }
786
787 /// Build a vector of labels. The position in the vector defines the label's bin.
788 std::vector<std::string_view> GetBinLabels() const
789 {
790 std::vector<std::string_view> vec(fLabelsIndex.size());
791 for (const auto &kv: fLabelsIndex)
792 vec.at(kv.second) = kv.first;
793 return vec;
794 }
795
796 /// Result of an RAxisLabels label set comparison
798 /// Both axes have the same labels, mapping to the same bins
800
801 /// The other axis doesn't have some labels from this axis
803
804 /// The other axis has some labels which this axis doesn't have
806
807 /// The labels shared by both axes do not map into the same bins
809 };
810
811 /// Compare the labels of this axis with those of another axis
812 LabelsCmpFlags CompareBinLabels(const RAxisLabels& other) const noexcept {
813 // This will eventually contain the results of the labels comparison
815 size_t missing_in_other = 0;
816
817 // First, check how this axis' labels map into the other axis
818 for (const auto &kv: fLabelsIndex) {
819 auto iter = other.fLabelsIndex.find(kv.first);
820 if (iter == other.fLabelsIndex.cend()) {
821 ++missing_in_other;
822 } else if (iter->second != kv.second) {
823 result = LabelsCmpFlags(result | kLabelsCmpDisordered);
824 }
825 }
826 if (missing_in_other > 0)
827 result = LabelsCmpFlags(result | kLabelsCmpSubset);
828
829 // If this covered all labels in the other axis, we're done
830 if (fLabelsIndex.size() == other.fLabelsIndex.size() + missing_in_other)
831 return result;
832
833 // Otherwise, we must check the labels of the other axis too
834 for (const auto &kv: other.fLabelsIndex)
835 if (fLabelsIndex.find(kv.first) == fLabelsIndex.cend())
836 return LabelsCmpFlags(result | kLabelsCmpSuperset);
837 return result;
838 }
839};
840
841namespace Internal {
842
843template <>
846
847 Axis_t operator()(const RAxisConfig &cfg) { return RAxisLabels(cfg.GetTitle(), cfg.GetBinLabels()); }
848};
849
850} // namespace Internal
851
852///\name Axis Compatibility
853///\{
855 kIdentical, ///< Source and target axes are identical
856
857 kContains, ///< The source is a subset of bins of the target axis
858
859 /// The bins of the source axis have finer granularity, but the bin borders
860 /// are compatible. Example:
861 /// source: 0., 1., 2., 3., 4., 5., 6.; target: 0., 2., 5., 6.
862 /// Note that this is *not* a symmetrical property: only one of
863 /// CanMerge(source, target), CanMap(target, source) can return kContains.
864 kSampling,
865
866 /// The source axis and target axis have different binning. Example:
867 /// source: 0., 1., 2., 3., 4., target: 0., 0.1, 0.2, 0.3, 0.4
869};
870
871/// Whether (and how) the source axis can be merged into the target axis.
872EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept;
873///\}
874
875} // namespace Experimental
876} // namespace ROOT
877
878#endif // ROOT7_RAxis header guard
double
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
#define d(i)
Definition RSha256.hxx:102
Random const_iterator through bins.
Definition RAxis.hxx:127
const_iterator & operator-=(int d) noexcept
Definition RAxis.hxx:176
const_iterator operator-(int d) noexcept
Definition RAxis.hxx:192
int operator-(const const_iterator &j) noexcept
Definition RAxis.hxx:200
const_iterator operator++(int) noexcept
i++
Definition RAxis.hxx:153
friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept
i > j
Definition RAxis.hxx:348
const_iterator operator+(int d) noexcept
Definition RAxis.hxx:183
int fCursor
Current iteration position.
Definition RAxis.hxx:128
friend const_iterator operator+(int d, const_iterator rhs) noexcept
2 + i
Definition RAxis.hxx:336
const int * operator->() const noexcept
Definition RAxis.hxx:215
const_iterator & operator+=(int d) noexcept
Definition RAxis.hxx:169
const_iterator & operator--() noexcept
–i
Definition RAxis.hxx:145
const_iterator & operator++() noexcept
++i
Definition RAxis.hxx:137
const_iterator operator--(int) noexcept
Definition RAxis.hxx:161
const_iterator(int cursor) noexcept
Initialize a const_iterator with its position.
Definition RAxis.hxx:134
friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept
i <= j
Definition RAxis.hxx:354
friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept
i < j
Definition RAxis.hxx:342
friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept
i >= j
Definition RAxis.hxx:360
friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept
i == j
Definition RAxis.hxx:366
friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept
i != j
Definition RAxis.hxx:372
Histogram axis base class.
Definition RAxis.hxx:44
int GetFirstBin() const noexcept
Get the bin index for the first bin of the axis.
Definition RAxis.hxx:274
const_iterator end() const noexcept
Get a const_iterator pointing beyond the last regular bin.
Definition RAxis.hxx:286
virtual int GetBinIndexForLowEdge(double x) const noexcept=0
If the coordinate x is within 10 ULPs of a bin low edge coordinate, return the bin for which this is ...
int GetNOverflowBins() const noexcept
Get the number of over- and underflow bins: 0 for growable axes, 2 otherwise.
Definition RAxis.hxx:247
RAxisBase(const RAxisBase &)=default
double GetMinimum() const
Get the low end of the axis range.
Definition RAxis.hxx:311
virtual int GetNBinsNoOver() const noexcept=0
Get the number of bins, excluding under- and overflow.
static constexpr const int kUnderflowBin
Index of the underflow bin, if any.
Definition RAxis.hxx:229
double GetMaximum() const
Get the high end of the axis range.
Definition RAxis.hxx:314
std::string fTitle
Title of this axis, used for graphics / text.
Definition RAxis.hxx:329
virtual int FindBin(double x) const noexcept=0
Find the adjusted bin index (returning kUnderflowBin for underflow and kOverflowBin for overflow) for...
int GetUnderflowBin() const noexcept
Get the bin index for the underflow bin (or kInvalidBin if CanGrow()).
Definition RAxis.hxx:257
virtual double GetBinFrom(int bin) const =0
Get the low bin border ("left edge") for the given bin index.
const std::string & GetTitle() const
Get the axis's title.
Definition RAxis.hxx:235
RAxisBase & operator=(RAxisBase &&)=default
const_iterator begin() const noexcept
Get a const_iterator pointing to the first regular bin.
Definition RAxis.hxx:283
int GetOverflowBin() const noexcept
Get the bin index for the overflow bin (or kInvalidBin if CanGrow()).
Definition RAxis.hxx:266
virtual double GetBinCenter(int bin) const =0
Get the bin center for the given bin index.
static constexpr const int kOverflowBin
Index of the overflow bin, if any.
Definition RAxis.hxx:232
RAxisBase() noexcept(noexcept(std::string()))=default
Default construct a RAxisBase (for use by derived classes for I/O)
virtual bool CanGrow() const noexcept=0
Whether this axis can grow (and thus has no overflow bins).
int AdjustOverflowBinNumber(double rawbin) const
Given rawbin (<0 for underflow, >=GetNBinsNoOver() for overflow), determine the bin number taking int...
Definition RAxis.hxx:75
int GetLastBin() const noexcept
Get the bin index for the last bin of the axis.
Definition RAxis.hxx:277
double GetBinTo(int bin) const
Get the high bin border ("right edge") for the given bin index.
Definition RAxis.hxx:305
virtual bool HasSameBinBordersAs(const RAxisBase &other) const
Check if two axis have the same bin borders.
Definition RAxis.hxx:98
RAxisBase(RAxisBase &&)=default
static constexpr const int kInvalidBin
Special bin index returned to signify that no bin matches a request.
Definition RAxis.hxx:226
bool HasSameBinningAs(const RAxisBase &other) const
Check if two axes use the same binning convention, i.e.
Definition RAxis.cxx:33
int GetNBins() const noexcept
Get the number of bins, including under- and overflow.
Definition RAxis.hxx:244
RAxisBase & operator=(const RAxisBase &)=default
Objects used to configure the different axis types.
const std::vector< std::string > & GetBinLabels() const noexcept
Get the bin labels; non-empty if the GetKind() == kLabels.
const std::vector< double > & GetBinBorders() const noexcept
Get the bin borders; non-empty if the GetKind() == kIrregular.
static constexpr const Grow_t Grow
Tag signalling that an axis should be able to grow; used for calling the appropriate constructor like...
const std::string & GetTitle() const
Get the axis's title.
Axis with equidistant bin borders.
Definition RAxis.hxx:384
RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition RAxis.hxx:422
int GetBinIndexForLowEdge(double x) const noexcept final override
If the coordinate x is within 10 ULPs of a bin low edge coordinate, return the bin for which this is ...
Definition RAxis.cxx:51
RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition RAxis.hxx:436
double GetBinWidth() const noexcept
Get the width of the bins.
Definition RAxis.hxx:460
double GetBinCenter(int bin) const final override
Get the bin center for the given bin index.
Definition RAxis.hxx:469
double FindBinRaw(double x) const noexcept
Find the raw bin index (not adjusted) for the given coordinate.
Definition RAxis.hxx:406
double GetInverseBinWidth() const noexcept
Get the inverse of the width of the bins.
Definition RAxis.hxx:463
int GetNBinsNoOver() const noexcept final override
Get the number of bins, excluding under- and overflow.
Definition RAxis.hxx:444
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition RAxis.cxx:76
int FindBin(double x) const noexcept final override
Find the adjusted bin index (returning kUnderflowBin for underflow and kOverflowBin for overflow) for...
Definition RAxis.hxx:450
double fInvBinWidth
The inverse of the bin width.
Definition RAxis.hxx:387
double GetBinFrom(int bin) const final override
Get the low bin border for the given bin index.
Definition RAxis.hxx:475
static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
Determine the inverse bin width.
Definition RAxis.hxx:394
bool CanGrow() const noexcept override
This axis cannot grow.
Definition RAxis.hxx:457
unsigned int fNBinsNoOver
Number of bins excluding under- and overflow.
Definition RAxis.hxx:388
double fLow
The lower limit of the axis.
Definition RAxis.hxx:386
An axis that can extend its range, keeping the number of its bins unchanged.
Definition RAxis.hxx:504
RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
Initialize a RAxisGrow.
Definition RAxis.hxx:516
int Grow(int toBin)
Grow this axis to make the "virtual bin" toBin in-range.
RAxisGrow(int nbins, double low, double high) noexcept
Initialize a RAxisGrow.
Definition RAxis.hxx:529
bool CanGrow() const noexcept final override
This axis kind can increase its range.
Definition RAxis.hxx:561
An axis with non-equidistant bins (also known as "variable binning").
Definition RAxis.hxx:590
const std::vector< double > & GetBinBorders() const noexcept
Access to the bin borders used by this axis.
Definition RAxis.hxx:706
double GetBinFrom(int bin) const final override
Get the lower bin border for a given bin index.
Definition RAxis.hxx:690
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition RAxis.cxx:114
RAxisIrregular(std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition RAxis.hxx:630
RAxisIrregular(const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition RAxis.hxx:617
int GetBinIndexForLowEdge(double x) const noexcept final override
If the coordinate x is within 10 ULPs of a bin low edge coordinate, return the bin for which this is ...
Definition RAxis.cxx:91
double GetBinCenter(int bin) const final override
Get the bin center of the bin with the given index.
Definition RAxis.hxx:686
bool CanGrow() const noexcept final override
This axis cannot be extended.
Definition RAxis.hxx:703
int GetNBinsNoOver() const noexcept final override
Get the number of bins, excluding under- and overflow.
Definition RAxis.hxx:667
std::vector< double > fBinBorders
Bin borders, one more than the number of regular bins.
Definition RAxis.hxx:593
int FindBin(double x) const noexcept final override
Find the bin index (adjusted with under- and overflow) for the given coordinate x.
Definition RAxis.hxx:672
RAxisIrregular(std::string_view title, std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition RAxis.hxx:654
RAxisIrregular(std::string_view title, const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition RAxis.hxx:641
double FindBinRaw(double x) const noexcept
Find the raw bin index (not adjusted) for the given coordinate x.
Definition RAxis.hxx:603
A RAxisGrow that has a label assigned to each bin and a bin width of 1.
Definition RAxis.hxx:737
LabelsCmpFlags
Result of an RAxisLabels label set comparison.
Definition RAxis.hxx:797
@ kLabelsCmpDisordered
The labels shared by both axes do not map into the same bins.
Definition RAxis.hxx:808
@ kLabelsCmpSubset
The other axis doesn't have some labels from this axis.
Definition RAxis.hxx:802
@ kLabelsCmpSame
Both axes have the same labels, mapping to the same bins.
Definition RAxis.hxx:799
@ kLabelsCmpSuperset
The other axis has some labels which this axis doesn't have.
Definition RAxis.hxx:805
RAxisLabels(const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings.
Definition RAxis.hxx:763
RAxisLabels(const std::vector< std::string_view > &labels)
Construct a RAxisLables from a vector of string_views.
Definition RAxis.hxx:760
std::unordered_map< std::string, int > fLabelsIndex
Map of label (view on fLabels's elements) to bin index.
Definition RAxis.hxx:740
std::vector< std::string_view > GetBinLabels() const
Build a vector of labels. The position in the vector defines the label's bin.
Definition RAxis.hxx:788
double GetBinCenterByName(const std::string &label)
Get the center of the bin with label.
Definition RAxis.hxx:782
int FindBinByName(const std::string &label)
Get the bin index with label.
Definition RAxis.hxx:769
RAxisLabels(std::string_view title, const std::vector< std::string_view > &labels)
Construct a RAxisLables from a vector of string_views, with title.
Definition RAxis.hxx:744
RAxisLabels(std::string_view title, const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings, with title.
Definition RAxis.hxx:752
LabelsCmpFlags CompareBinLabels(const RAxisLabels &other) const noexcept
Compare the labels of this axis with those of another axis.
Definition RAxis.hxx:812
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept
Whether (and how) the source axis can be merged into the target axis.
Definition RAxis.cxx:126
bool operator>=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i >= j
Definition RAxis.hxx:360
ELogLevel operator+(ELogLevel severity, int offset)
Definition RLogger.hxx:45
bool operator>(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i > j
Definition RAxis.hxx:348
@ kContains
The source is a subset of bins of the target axis.
@ kIdentical
Source and target axes are identical.
@ kIncompatible
The source axis and target axis have different binning.
@ kSampling
The bins of the source axis have finer granularity, but the bin borders are compatible.
bool operator<(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i < j
Definition RAxis.hxx:342
bool operator<=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i <= j
Definition RAxis.hxx:354
bool operator!=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i != j
Definition RAxis.hxx:372
bool operator==(const RConcurrentHashColl::HashValue &lhs, const RConcurrentHashColl::HashValue &rhs)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Converts a RAxisConfig of whatever kind to the corresponding RAxisBase-derived object.