Logo ROOT  
Reference Guide
RAxis.hxx
Go to the documentation of this file.
1/// \file ROOT/RAxis.h
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[out] result status of the bin determination.
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 lighOrLow - 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 nbins - 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 nbins - 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 /// \param canGrow - whether this axis can extend its range.
437 explicit RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
438 : RAxisEquidistant("", nbinsNoOver, low, high)
439 {}
440
441 /// Convert to RAxisConfig.
442 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetNBinsNoOver(), GetMinimum(), GetMaximum()); }
443
444 /// Get the number of bins, excluding under- and overflow.
445 int GetNBinsNoOver() const noexcept final override { return fNBinsNoOver; }
446
447 /// Find the adjusted bin index (returning `kUnderflowBin` for underflow and
448 /// `kOverflowBin` for overflow) for the given coordinate.
449 /// \note Passing a bin border coordinate can either return the bin above or
450 /// below the bin border. I.e. don't do that for reliable results!
451 int FindBin(double x) const noexcept final override
452 {
453 double rawbin = FindBinRaw(x);
454 return AdjustOverflowBinNumber(rawbin);
455 }
456
457 /// This axis cannot grow.
458 bool CanGrow() const noexcept override { return false; }
459
460 /// Get the width of the bins.
461 double GetBinWidth() const noexcept { return 1. / fInvBinWidth; }
462
463 /// Get the inverse of the width of the bins.
464 double GetInverseBinWidth() const noexcept { return fInvBinWidth; }
465
466 /// Get the bin center for the given bin index.
467 /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
468 /// returns 0.25.
469 /// The result of this method on an overflow or underflow bin is unspecified.
470 double GetBinCenter(int bin) const final override { return fLow + (bin - GetFirstBin() + 0.5) / fInvBinWidth; }
471
472 /// Get the low bin border for the given bin index.
473 /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
474 /// returns 0.
475 /// The result of this method on an underflow bin is unspecified.
476 double GetBinFrom(int bin) const final override {
477 const double result = (bin == kOverflowBin) ? GetMaximum() : fLow + (bin - GetFirstBin()) / fInvBinWidth;
478 return result;
479 }
480
481 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
482 /// return the bin for which this is a low edge. If it's not a bin edge,
483 /// return `kInvalidBin`.
484 int GetBinIndexForLowEdge(double x) const noexcept final override;
485};
486
487namespace Internal {
488
489template <>
490struct AxisConfigToType<RAxisConfig::kEquidistant> {
492
493 Axis_t operator()(const RAxisConfig &cfg) noexcept
494 {
495 return RAxisEquidistant(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
496 }
497};
498
499} // namespace Internal
500
501/** An axis that can extend its range, keeping the number of its bins unchanged.
502 The axis is constructed with an initial range. Apart from its ability to
503 grow, this axis behaves like a RAxisEquidistant.
504 */
506public:
507 /// Initialize a RAxisGrow.
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[in] title - axis title used for graphics and text representation.
522 /// \param nbins - number of bins in the axis, excluding under- and overflow
523 /// bins. This value is fixed over the lifetime of the object.
524 /// \param low - the initial value for the low axis range. Any coordinate
525 /// below that is considered as underflow. To trigger the growing of the
526 /// axis call `Grow()`.
527 /// \param high - the initial value for the high axis range. Any coordinate
528 /// above that is considered as overflow. To trigger the growing of the
529 /// axis call `Grow()`.
530 explicit RAxisGrow(int nbins, double low, double high) noexcept: RAxisGrow("", nbins, low, high) {}
531
532 /// Convert to RAxisConfig.
534
535 /// Grow this axis to make the "virtual bin" toBin in-range. This keeps the
536 /// non-affected axis limit unchanged, and extends the other axis limit such
537 /// that a number of consecutive bins are merged.
538 ///
539 /// Example, assuming an initial RAxisGrow with 10 bins from 0. to 1.:
540 /// - `Grow(0)`: that (virtual) bin spans from -0.1 to 0. To include it
541 /// in the axis range, the lower limit must be shifted. The minimal number
542 /// of bins that can be merged is 2, thus the new axis will span from
543 /// -1. to 1.
544 /// - `Grow(-1)`: that (virtual) bin spans from -0.2 to 0.1. To include it
545 /// in the axis range, the lower limit must be shifted. The minimal number
546 /// of bins that can be merged is 2, thus the new axis will span from
547 /// -1. to 1.
548 /// - `Grow(50)`: that (virtual) bin spans from 4.9 to 5.0. To include it
549 /// in the axis range, the higher limit must be shifted. Five bins need to
550 /// be merged, making the new axis range 0. to 5.0.
551 ///
552 /// \param toBin - the "virtual" bin number, as if the axis had an infinite
553 /// number of bins with the current bin width. For instance, for an axis
554 /// with ten bins in the range 0. to 1., the coordinate 2.05 has the virtual
555 /// bin index 20.
556 /// \return Returns the number of bins that were merged to reach the value.
557 /// A value of 1 means that no bins were merged (toBin was in the original
558 /// axis range).
559 int Grow(int toBin);
560
561 /// This axis kind can increase its range.
562 bool CanGrow() const noexcept final override { return true; }
563};
564
565namespace Internal {
566
567template <>
570
571 Axis_t operator()(const RAxisConfig &cfg) noexcept
572 {
573 return RAxisGrow(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
574 }
575};
576
577} // namespace Internal
578
579/**
580 An axis with non-equidistant bins (also known as "variable binning"). It is
581 defined by an array of bin borders - one more than the number of
582 (non-overflow-) bins it has! As an example, an axis with two bin needs three
583 bin borders:
584 - lower edge of the first bin;
585 - higher edge of the first bin, identical to the lower edge of the second
586 bin;
587 - higher edge of the second bin
588
589 This axis cannot grow; the size of new bins would not be well defined.
590 */
592private:
593 /// Bin borders, one more than the number of regular bins.
594 std::vector<double> fBinBorders;
595
596protected:
597 /// See RAxisBase::HasSameBinBordersAs
598 bool HasSameBinBordersAs(const RAxisBase& other) const override;
599
600 /// Find the raw bin index (not adjusted) for the given coordinate `x`.
601 /// The resulting raw bin is 1-based.
602 /// \note Passing a bin border coordinate can either return the bin above or
603 /// below the bin border. I.e. don't do that for reliable results!
604 double FindBinRaw(double x) const noexcept
605 {
606 const auto bBegin = fBinBorders.begin();
607 const auto bEnd = fBinBorders.end();
608 // lower_bound finds the first bin border that is >= x.
609 auto iNotLess = std::lower_bound(bBegin, bEnd, x);
610 return iNotLess - bBegin;
611 }
612
613public:
614 RAxisIrregular() = default;
615
616 /// Construct a RAxisIrregular from a vector of bin borders.
617 /// \note The bin borders must be sorted in increasing order!
618 explicit RAxisIrregular(const std::vector<double> &binborders)
619 : RAxisBase(), fBinBorders(binborders)
620 {
621#ifdef R__DO_RANGE_CHECKS
622 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
623 R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
624#endif // R__DO_RANGE_CHECKS
625 }
626
627 /// Construct a RAxisIrregular from a vector of bin borders.
628 /// \note The bin borders must be sorted in increasing order!
629 /// Faster, noexcept version taking an rvalue of binborders. The compiler will
630 /// know when it can take this one.
631 explicit RAxisIrregular(std::vector<double> &&binborders) noexcept
632 : RAxisBase(), fBinBorders(std::move(binborders))
633 {
634#ifdef R__DO_RANGE_CHECKS
635 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
636 R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
637#endif // R__DO_RANGE_CHECKS
638 }
639
640 /// Construct a RAxisIrregular from a vector of bin borders.
641 /// \note The bin borders must be sorted in increasing order!
642 explicit RAxisIrregular(std::string_view title, const std::vector<double> &binborders)
643 : RAxisBase(title), fBinBorders(binborders)
644 {
645#ifdef R__DO_RANGE_CHECKS
646 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
647 R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
648#endif // R__DO_RANGE_CHECKS
649 }
650
651 /// Construct a RAxisIrregular from a vector of bin borders.
652 /// \note The bin borders must be sorted in increasing order!
653 /// Faster, noexcept version taking an rvalue of binborders. The compiler will
654 /// know when it can take this one.
655 explicit RAxisIrregular(std::string_view title, std::vector<double> &&binborders) noexcept
656 : RAxisBase(title), fBinBorders(std::move(binborders))
657 {
658#ifdef R__DO_RANGE_CHECKS
659 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
660 R__ERROR_HERE("HIST") << "Bin borders must be sorted!";
661#endif // R__DO_RANGE_CHECKS
662 }
663
664 /// Convert to RAxisConfig.
665 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinBorders()); }
666
667 /// Get the number of bins, excluding under- and overflow.
668 int GetNBinsNoOver() const noexcept final override { return fBinBorders.size() - 1; }
669
670 /// Find the bin index (adjusted with under- and overflow) for the given coordinate `x`.
671 /// \note Passing a bin border coordinate can either return the bin above or
672 /// below the bin border. I.e. don't do that for reliable results!
673 int FindBin(double x) const noexcept final override
674 {
675 int rawbin = FindBinRaw(x);
676 // No need for AdjustOverflowBinNumber(rawbin) here; lower_bound() is the
677 // answer: e.g. for x < *bBegin, rawbin is -1.
678 if (rawbin < GetFirstBin())
679 return kUnderflowBin;
680 if (rawbin >= GetLastBin() + 1)
681 return kOverflowBin;
682 return rawbin;
683 }
684
685 /// Get the bin center of the bin with the given index.
686 /// The result of this method on an overflow or underflow bin is unspecified.
687 double GetBinCenter(int bin) const final override { return 0.5 * (fBinBorders[bin - 1] + fBinBorders[bin]); }
688
689 /// Get the lower bin border for a given bin index.
690 /// The result of this method on an underflow bin is unspecified.
691 double GetBinFrom(int bin) const final override
692 {
693 if (bin == kOverflowBin)
694 return fBinBorders[GetLastBin()];
695 return fBinBorders[bin - 1];
696 }
697
698 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
699 /// return the bin for which this is a low edge. If it's not a bin edge,
700 /// return `kInvalidBin`.
701 int GetBinIndexForLowEdge(double x) const noexcept final override;
702
703 /// This axis cannot be extended.
704 bool CanGrow() const noexcept final override { return false; }
705
706 /// Access to the bin borders used by this axis.
707 const std::vector<double> &GetBinBorders() const noexcept { return fBinBorders; }
708};
709
710namespace Internal {
711
712template <>
713struct AxisConfigToType<RAxisConfig::kIrregular> {
715
716 Axis_t operator()(const RAxisConfig &cfg) { return RAxisIrregular(cfg.GetTitle(), cfg.GetBinBorders()); }
717};
718
719} // namespace Internal
720
721/**
722 \class RAxisLabels
723 A RAxisGrow that has a label assigned to each bin and a bin width of 1.
724
725 While filling still works through coordinates (i.e. arrays of doubles),
726 RAxisLabels allows to convert a string to a bin number or the bin's coordinate
727 center. The number of labels and the number of bins reported by RAxisGrow might
728 differ: the RAxisGrow will only grow when seeing a Fill(), while the RAxisLabels
729 will add a new label whenever `GetBinCenter()` is called.
730
731 Implementation details:
732 Filling happens often; `GetBinCenter()` needs to be fast. Thus the unordered_map.
733 The painter needs the reverse: it wants the label for bin 0, bin 1 etc. The axis
734 should only store the bin labels once; referencing them is (due to re-allocation,
735 hashing etc) non-trivial. So instead, build a `vector<string_view>` for the few
736 times the axis needs to be painted.
737 */
738class RAxisLabels: public RAxisGrow {
739private:
740 /// Map of label (view on `fLabels`'s elements) to bin index
741 std::unordered_map<std::string, int /*bin number*/> fLabelsIndex;
742
743public:
744 /// Construct a RAxisLables from a `vector` of `string_view`s, with title.
745 explicit RAxisLabels(std::string_view title, const std::vector<std::string_view> &labels)
746 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
747 {
748 for (size_t i = 0, n = labels.size(); i < n; ++i)
749 fLabelsIndex[std::string(labels[i])] = i;
750 }
751
752 /// Construct a RAxisLables from a `vector` of `string`s, with title.
753 explicit RAxisLabels(std::string_view title, const std::vector<std::string> &labels)
754 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
755 {
756 for (size_t i = 0, n = labels.size(); i < n; ++i)
757 fLabelsIndex[labels[i]] = i;
758 }
759
760 /// Construct a RAxisLables from a `vector` of `string_view`s
761 explicit RAxisLabels(const std::vector<std::string_view> &labels): RAxisLabels("", labels) {}
762
763 /// Construct a RAxisLables from a `vector` of `string`s
764 explicit RAxisLabels(const std::vector<std::string> &labels): RAxisLabels("", labels) {}
765
766 /// Convert to RAxisConfig.
767 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinLabels()); }
768
769 /// Get the bin index with label.
770 int FindBinByName(const std::string &label)
771 {
772 auto insertResult = fLabelsIndex.insert({label, -1});
773 if (insertResult.second) {
774 // we have created a new label
775 int idx = fLabelsIndex.size() - 1;
776 insertResult.first->second = idx;
777 return idx;
778 }
779 return insertResult.first->second;
780 }
781
782 /// Get the center of the bin with label.
783 double GetBinCenterByName(const std::string &label)
784 {
785 return FindBinByName(label) + 0.5; // bin *center*
786 }
787
788 /// Build a vector of labels. The position in the vector defines the label's bin.
789 std::vector<std::string_view> GetBinLabels() const
790 {
791 std::vector<std::string_view> vec(fLabelsIndex.size());
792 for (const auto &kv: fLabelsIndex)
793 vec.at(kv.second) = kv.first;
794 return vec;
795 }
796
797 /// Result of an RAxisLabels label set comparison
799 /// Both axes have the same labels, mapping to the same bins
801
802 /// The other axis doesn't have some labels from this axis
804
805 /// The other axis has some labels which this axis doesn't have
807
808 /// The labels shared by both axes do not map into the same bins
810 };
811
812 /// Compare the labels of this axis with those of another axis
813 LabelsCmpFlags CompareBinLabels(const RAxisLabels& other) const noexcept {
814 // This will eventually contain the results of the labels comparison
816 size_t missing_in_other = 0;
817
818 // First, check how this axis' labels map into the other axis
819 for (const auto &kv: fLabelsIndex) {
820 auto iter = other.fLabelsIndex.find(kv.first);
821 if (iter == other.fLabelsIndex.cend()) {
822 ++missing_in_other;
823 } else if (iter->second != kv.second) {
824 result = LabelsCmpFlags(result | kLabelsCmpDisordered);
825 }
826 }
827 if (missing_in_other > 0)
828 result = LabelsCmpFlags(result | kLabelsCmpSubset);
829
830 // If this covered all labels in the other axis, we're done
831 if (fLabelsIndex.size() == other.fLabelsIndex.size() + missing_in_other)
832 return result;
833
834 // Otherwise, we must check the labels of the other axis too
835 for (const auto &kv: other.fLabelsIndex)
836 if (fLabelsIndex.find(kv.first) == fLabelsIndex.cend())
837 return LabelsCmpFlags(result | kLabelsCmpSuperset);
838 return result;
839 }
840};
841
842namespace Internal {
843
844template <>
847
848 Axis_t operator()(const RAxisConfig &cfg) { return RAxisLabels(cfg.GetTitle(), cfg.GetBinLabels()); }
849};
850
851} // namespace Internal
852
853///\name Axis Compatibility
854///\{
856 kIdentical, ///< Source and target axes are identical
857
858 kContains, ///< The source is a subset of bins of the target axis
859
860 /// The bins of the source axis have finer granularity, but the bin borders
861 /// are compatible. Example:
862 /// source: 0., 1., 2., 3., 4., 5., 6.; target: 0., 2., 5., 6.
863 /// Note that this is *not* a symmetrical property: only one of
864 /// CanMerge(source, target), CanMap(target, source) can return kContains.
865 kSampling,
866
867 /// The source axis and target axis have different binning. Example:
868 /// source: 0., 1., 2., 3., 4., target: 0., 0.1, 0.2, 0.3, 0.4
870};
871
872/// Whether (and how) the source axis can be merged into the target axis.
873EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept;
874///\}
875
876} // namespace Experimental
877} // namespace ROOT
878
879#endif // ROOT7_RAxis header guard
double
Definition: Converters.cxx:921
#define R__ERROR_HERE(GROUP)
Definition: RLogger.hxx:183
#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
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:23
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.
Definition: RAxisConfig.hxx:35
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...
Definition: RAxisConfig.hxx:72
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:41
RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition: RAxis.hxx:437
double GetBinWidth() const noexcept
Get the width of the bins.
Definition: RAxis.hxx:461
double GetBinCenter(int bin) const final override
Get the bin center for the given bin index.
Definition: RAxis.hxx:470
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:464
int GetNBinsNoOver() const noexcept final override
Get the number of bins, excluding under- and overflow.
Definition: RAxis.hxx:445
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition: RAxis.cxx:66
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:451
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:476
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:458
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:505
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:530
bool CanGrow() const noexcept final override
This axis kind can increase its range.
Definition: RAxis.hxx:562
An axis with non-equidistant bins (also known as "variable binning").
Definition: RAxis.hxx:591
const std::vector< double > & GetBinBorders() const noexcept
Access to the bin borders used by this axis.
Definition: RAxis.hxx:707
double GetBinFrom(int bin) const final override
Get the lower bin border for a given bin index.
Definition: RAxis.hxx:691
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition: RAxis.cxx:104
RAxisIrregular(std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:631
RAxisIrregular(const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:618
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:81
double GetBinCenter(int bin) const final override
Get the bin center of the bin with the given index.
Definition: RAxis.hxx:687
bool CanGrow() const noexcept final override
This axis cannot be extended.
Definition: RAxis.hxx:704
int GetNBinsNoOver() const noexcept final override
Get the number of bins, excluding under- and overflow.
Definition: RAxis.hxx:668
std::vector< double > fBinBorders
Bin borders, one more than the number of regular bins.
Definition: RAxis.hxx:594
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:673
RAxisIrregular(std::string_view title, std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:655
RAxisIrregular(std::string_view title, const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:642
double FindBinRaw(double x) const noexcept
Find the raw bin index (not adjusted) for the given coordinate x.
Definition: RAxis.hxx:604
A RAxisGrow that has a label assigned to each bin and a bin width of 1.
Definition: RAxis.hxx:738
LabelsCmpFlags
Result of an RAxisLabels label set comparison.
Definition: RAxis.hxx:798
@ kLabelsCmpDisordered
The labels shared by both axes do not map into the same bins.
Definition: RAxis.hxx:809
@ kLabelsCmpSubset
The other axis doesn't have some labels from this axis.
Definition: RAxis.hxx:803
@ kLabelsCmpSame
Both axes have the same labels, mapping to the same bins.
Definition: RAxis.hxx:800
@ kLabelsCmpSuperset
The other axis has some labels which this axis doesn't have.
Definition: RAxis.hxx:806
RAxisLabels(const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings.
Definition: RAxis.hxx:764
RAxisLabels(const std::vector< std::string_view > &labels)
Construct a RAxisLables from a vector of string_views.
Definition: RAxis.hxx:761
std::unordered_map< std::string, int > fLabelsIndex
Map of label (view on fLabels's elements) to bin index.
Definition: RAxis.hxx:741
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:789
double GetBinCenterByName(const std::string &label)
Get the center of the bin with label.
Definition: RAxis.hxx:783
int FindBinByName(const std::string &label)
Get the bin index with label.
Definition: RAxis.hxx:770
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:745
RAxisLabels(std::string_view title, const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings, with title.
Definition: RAxis.hxx:753
LabelsCmpFlags CompareBinLabels(const RAxisLabels &other) const noexcept
Compare the labels of this axis with those of another axis.
Definition: RAxis.hxx:813
Double_t x[n]
Definition: legend1.C:17
const Int_t n
Definition: legend1.C:16
basic_string_view< char > string_view
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:116
bool operator>=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i >= j
Definition: RAxis.hxx:360
bool operator==(const TString &t, const std::string &s)
Definition: REveTypes.cxx:13
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
REveException operator+(const REveException &s1, const std::string &s2)
Definition: REveTypes.cxx:21
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
VecExpr< UnaryOp< Fabs< T >, VecExpr< A, T, D >, T >, T, D > fabs(const VecExpr< A, T, D > &rhs)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Definition: StringConv.hxx:21
Axis_t operator()(const RAxisConfig &cfg) noexcept
Definition: RAxis.hxx:571
Converts a RAxisConfig of whatever kind to the corresponding RAxisBase-derived object.