Logo ROOT  
Reference Guide
RAxis.hxx
Go to the documentation of this file.
1/// \file ROOT/RAxis.hxx
2/// \ingroup HistV7
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 */
127 int fCursor = 0; ///< Current iteration position
128
129 public:
130 using iterator_category = std::random_access_iterator_tag;
133 using pointer = const int *;
134 using reference = const int &;
135
136 const_iterator() = default;
137
138 /// Initialize a const_iterator with its position
139 explicit const_iterator(int cursor) noexcept: fCursor(cursor) {}
140
141 /// ++i
143 {
144 // Could check whether fCursor < fEnd - but what for?
145 ++fCursor;
146 return *this;
147 }
148
149 /// --i
151 {
152 // Could check whether fCursor > fBegin - but what for?
153 --fCursor;
154 return *this;
155 }
156
157 /// i++
159 {
160 const_iterator old(*this);
161 ++(*this);
162 return old;
163 }
164
165 // i--
167 {
168 const_iterator old(*this);
169 --(*this);
170 return old;
171 }
172
173 // i += 2
175 {
176 fCursor += d;
177 return *this;
178 }
179
180 // i -= 2
182 {
183 fCursor -= d;
184 return *this;
185 }
186
187 // i + 2
189 {
190 const_iterator ret(*this);
191 ret += d;
192 return ret;
193 }
194 friend const_iterator operator+(int d, const_iterator rhs) noexcept;
195
196 // i - 2
198 {
199 const_iterator ret(*this);
200 ret -= d;
201 return ret;
202 }
203
204 // i - j
205 int operator-(const const_iterator& j) noexcept
206 {
207 return fCursor - j.fCursor;
208 }
209
210 // i[2]
211 int operator[](int d) noexcept
212 {
213 return fCursor + d;
214 }
215
216 // *i
217 int operator*() const noexcept { return fCursor; }
218
219 // i->
220 const int *operator->() const noexcept { return &fCursor; }
221
222 friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept;
223 friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept;
224 friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept;
225 friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept;
226 friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept;
227 friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept;
228 };
229
230 /// Special bin index returned to signify that no bin matches a request.
231 constexpr static const int kInvalidBin = 0;
232
233 /// Index of the underflow bin, if any.
234 constexpr static const int kUnderflowBin = -1;
235
236 /// Index of the overflow bin, if any.
237 constexpr static const int kOverflowBin = -2;
238
239 /// Get the axis's title
240 const std::string &GetTitle() const { return fTitle; }
241
242 /// Whether this axis can grow (and thus has no overflow bins).
243 virtual bool CanGrow() const noexcept = 0;
244
245 /// Get the number of bins, excluding under- and overflow.
246 virtual int GetNBinsNoOver() const noexcept = 0;
247
248 /// Get the number of bins, including under- and overflow.
249 int GetNBins() const noexcept { return GetNBinsNoOver() + GetNOverflowBins(); }
250
251 /// Get the number of over- and underflow bins: 0 for growable axes, 2 otherwise.
252 int GetNOverflowBins() const noexcept
253 {
254 if (CanGrow())
255 return 0;
256 else
257 return 2;
258 };
259
260 /// Get the bin index for the underflow bin (or `kInvalidBin`
261 /// if CanGrow()).
262 int GetUnderflowBin() const noexcept {
263 if (CanGrow())
264 return kInvalidBin;
265 else
266 return kUnderflowBin;
267 }
268
269 /// Get the bin index for the overflow bin (or `kInvalidBin`
270 /// if CanGrow()).
271 int GetOverflowBin() const noexcept {
272 if (CanGrow())
273 return kInvalidBin;
274 else
275 return kOverflowBin;
276 }
277
278 /// Get the bin index for the first bin of the axis
279 int GetFirstBin() const noexcept { return 1; }
280
281 /// Get the bin index for the last bin of the axis
282 int GetLastBin() const noexcept { return GetNBinsNoOver(); }
283
284 ///\name Iterator interfaces
285 ///\{
286
287 /// Get a const_iterator pointing to the first regular bin.
288 const_iterator begin() const noexcept { return const_iterator{GetFirstBin()}; }
289
290 /// Get a const_iterator pointing beyond the last regular bin
291 const_iterator end() const noexcept { return const_iterator{GetLastBin() + 1}; }
292 ///\}
293
294 /// Find the adjusted bin index (returning `kUnderflowBin` for underflow and `kOverflowBin`
295 /// for overflow) for the given coordinate.
296 /// \note Passing a bin border coordinate can either return the bin above or
297 /// below the bin border. I.e. don't do that for reliable results!
298 virtual int FindBin(double x) const noexcept = 0;
299
300 /// Get the bin center for the given bin index.
301 /// The result of this method on an overflow or underflow bin is unspecified.
302 virtual double GetBinCenter(int bin) const = 0;
303
304 /// Get the low bin border ("left edge") for the given bin index.
305 /// The result of this method on an underflow bin is unspecified.
306 virtual double GetBinFrom(int bin) const = 0;
307
308 /// Get the high bin border ("right edge") for the given bin index.
309 /// The result of this method on an overflow bin is unspecified.
310 double GetBinTo(int bin) const {
311 const double result = (bin == kUnderflowBin) ? GetMinimum() : GetBinFrom(bin + 1);
312 return result;
313 }
314
315 /// Get the low end of the axis range.
316 double GetMinimum() const { return GetBinFrom(GetFirstBin()); }
317
318 /// Get the high end of the axis range.
319 double GetMaximum() const { return GetBinTo(GetLastBin()); }
320
321 /// Check if two axes use the same binning convention, i.e.
322 ///
323 /// - Either they are both growable or neither of them is growable.
324 /// - Minimum, maximum, and all bin borders in the middle are the same.
325 /// - Bin labels must match (exactly including order, for now).
326 bool HasSameBinningAs(const RAxisBase& other) const;
327
328 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
329 /// return the bin for which this is a low edge. If it's not a bin edge,
330 /// return `kInvalidBin`.
331 virtual int GetBinIndexForLowEdge(double x) const noexcept = 0;
332
333private:
334 std::string fTitle; ///< Title of this axis, used for graphics / text.
335};
336
337///\name RAxisBase::const_iterator external operators
338///\{
339
340/// 2 + i
342{
343 return rhs + d;
344}
345
346/// i < j
348{
349 return lhs.fCursor < rhs.fCursor;
350}
351
352/// i > j
354{
355 return lhs.fCursor > rhs.fCursor;
356}
357
358/// i <= j
360{
361 return lhs.fCursor <= rhs.fCursor;
362}
363
364/// i >= j
366{
367 return lhs.fCursor >= rhs.fCursor;
368}
369
370/// i == j
372{
373 return lhs.fCursor == rhs.fCursor;
374}
375
376/// i != j
378{
379 return lhs.fCursor != rhs.fCursor;
380}
381///\}
382
383/**
384 Axis with equidistant bin borders. Defined by lower l and upper u limit and
385 the number of bins n. All bins have the same width (u-l)/n.
386
387 This axis cannot grow; use `RAxisGrow` for that.
388 */
390protected:
391 double fLow = 0.; ///< The lower limit of the axis
392 double fInvBinWidth = 0.; ///< The inverse of the bin width
393 unsigned int fNBinsNoOver; ///< Number of bins excluding under- and overflow.
394
395 /// Determine the inverse bin width.
396 /// \param nbinsNoOver - number of bins without unter-/overflow
397 /// \param lowOrHigh - first axis boundary
398 /// \param highOrLow - second axis boundary
399 static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
400 {
401 return nbinsNoOver / std::fabs(highOrLow - lowOrHigh);
402 }
403
404 /// See RAxisBase::HasSameBinBordersAs
405 bool HasSameBinBordersAs(const RAxisBase& other) const override;
406
407 /// Find the raw bin index (not adjusted) for the given coordinate.
408 /// The resulting raw bin is 0-based.
409 /// \note Passing a bin border coordinate can either return the bin above or
410 /// below the bin border. I.e. don't do that for reliable results!
411 double FindBinRaw(double x) const noexcept
412 {
413 return (x - fLow) * fInvBinWidth;
414 }
415
416public:
417 RAxisEquidistant() = default;
418
419 /// Initialize a RAxisEquidistant.
420 /// \param[in] title - axis title used for graphics and text representation.
421 /// \param nbinsNoOver - number of bins in the axis, excluding under- and overflow
422 /// bins.
423 /// \param low - the low axis range. Any coordinate below that is considered
424 /// as underflow. The first bin's lower edge is at this value.
425 /// \param high - the high axis range. Any coordinate above that is considered
426 /// as overflow. The last bin's higher edge is at this value.
427 explicit RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
428 : RAxisBase(title)
429 , fLow(low)
430 , fInvBinWidth(GetInvBinWidth(nbinsNoOver, low, high))
431 , fNBinsNoOver(nbinsNoOver)
432 {}
433
434 /// Initialize a RAxisEquidistant.
435 /// \param nbinsNoOver - number of bins in the axis, excluding under- and overflow
436 /// bins.
437 /// \param low - the low axis range. Any coordinate below that is considered
438 /// as underflow. The first bin's lower edge is at this value.
439 /// \param high - the high axis range. Any coordinate above that is considered
440 /// as overflow. The last bin's higher edge is at this value.
441 explicit RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
442 : RAxisEquidistant("", nbinsNoOver, low, high)
443 {}
444
445 /// Convert to RAxisConfig.
446 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetNBinsNoOver(), GetMinimum(), GetMaximum()); }
447
448 /// Get the number of bins, excluding under- and overflow.
449 int GetNBinsNoOver() const noexcept final { return fNBinsNoOver; }
450
451 /// Find the adjusted bin index (returning `kUnderflowBin` for underflow and
452 /// `kOverflowBin` for overflow) for the given coordinate.
453 /// \note Passing a bin border coordinate can either return the bin above or
454 /// below the bin border. I.e. don't do that for reliable results!
455 int FindBin(double x) const noexcept final
456 {
457 double rawbin = FindBinRaw(x);
458 return AdjustOverflowBinNumber(rawbin);
459 }
460
461 /// This axis cannot grow.
462 bool CanGrow() const noexcept override { return false; }
463
464 /// Get the width of the bins.
465 double GetBinWidth() const noexcept { return 1. / fInvBinWidth; }
466
467 /// Get the inverse of the width of the bins.
468 double GetInverseBinWidth() const noexcept { return fInvBinWidth; }
469
470 /// Get the bin center for the given bin index.
471 /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
472 /// returns 0.25.
473 /// The result of this method on an overflow or underflow bin is unspecified.
474 double GetBinCenter(int bin) const final { return fLow + (bin - GetFirstBin() + 0.5) / fInvBinWidth; }
475
476 /// Get the low bin border for the given bin index.
477 /// For the bin == 1 (the first bin) of 2 bins for an axis (0., 1.), this
478 /// returns 0.
479 /// The result of this method on an underflow bin is unspecified.
480 double GetBinFrom(int bin) const final {
481 const double result = (bin == kOverflowBin) ? GetMaximum() : fLow + (bin - GetFirstBin()) / fInvBinWidth;
482 return result;
483 }
484
485 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
486 /// return the bin for which this is a low edge. If it's not a bin edge,
487 /// return `kInvalidBin`.
488 int GetBinIndexForLowEdge(double x) const noexcept final ;
489};
490
491namespace Internal {
492
493template <>
494struct AxisConfigToType<RAxisConfig::kEquidistant> {
496
497 Axis_t operator()(const RAxisConfig &cfg) noexcept
498 {
499 return RAxisEquidistant(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
500 }
501};
502
503} // namespace Internal
504
505/** An axis that can extend its range, keeping the number of its bins unchanged.
506 The axis is constructed with an initial range. Apart from its ability to
507 grow, this axis behaves like a RAxisEquidistant.
508 */
510public:
511 /// Initialize a RAxisGrow.
512 /// \param[in] title - axis title used for graphics and text representation.
513 /// \param nbins - number of bins in the axis, excluding under- and overflow
514 /// bins. This value is fixed over the lifetime of the object.
515 /// \param low - the initial value for the low axis range. Any coordinate
516 /// below that is considered as underflow. To trigger the growing of the
517 /// axis call `Grow()`.
518 /// \param high - the initial value for the high axis range. Any coordinate
519 /// above that is considered as overflow. To trigger the growing of the
520 /// axis call `Grow()`.
521 explicit RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
522 : RAxisEquidistant(title, nbins, low, high)
523 {}
524
525 /// Initialize a RAxisGrow.
526 /// \param nbins - number of bins in the axis, excluding under- and overflow
527 /// bins. This value is fixed over the lifetime of the object.
528 /// \param low - the initial value for the low axis range. Any coordinate
529 /// below that is considered as underflow. To trigger the growing of the
530 /// axis call `Grow()`.
531 /// \param high - the initial value for the high axis range. Any coordinate
532 /// above that is considered as overflow. To trigger the growing of the
533 /// axis call `Grow()`.
534 explicit RAxisGrow(int nbins, double low, double high) noexcept: RAxisGrow("", nbins, low, high) {}
535
536 /// Convert to RAxisConfig.
538
539 /// Grow this axis to make the "virtual bin" toBin in-range. This keeps the
540 /// non-affected axis limit unchanged, and extends the other axis limit such
541 /// that a number of consecutive bins are merged.
542 ///
543 /// Example, assuming an initial RAxisGrow with 10 bins from 0. to 1.:
544 /// - `Grow(0)`: that (virtual) bin spans from -0.1 to 0. 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(-1)`: that (virtual) bin spans from -0.2 to 0.1. To include it
549 /// in the axis range, the lower limit must be shifted. The minimal number
550 /// of bins that can be merged is 2, thus the new axis will span from
551 /// -1. to 1.
552 /// - `Grow(50)`: that (virtual) bin spans from 4.9 to 5.0. To include it
553 /// in the axis range, the higher limit must be shifted. Five bins need to
554 /// be merged, making the new axis range 0. to 5.0.
555 ///
556 /// \param toBin - the "virtual" bin number, as if the axis had an infinite
557 /// number of bins with the current bin width. For instance, for an axis
558 /// with ten bins in the range 0. to 1., the coordinate 2.05 has the virtual
559 /// bin index 20.
560 /// \return Returns the number of bins that were merged to reach the value.
561 /// A value of 1 means that no bins were merged (toBin was in the original
562 /// axis range).
563 int Grow(int toBin);
564
565 /// This axis kind can increase its range.
566 bool CanGrow() const noexcept final { return true; }
567};
568
569namespace Internal {
570
571template <>
574
575 Axis_t operator()(const RAxisConfig &cfg) noexcept
576 {
577 return RAxisGrow(cfg.GetTitle(), cfg.GetNBinsNoOver(), cfg.GetBinBorders()[0], cfg.GetBinBorders()[1]);
578 }
579};
580
581} // namespace Internal
582
583/**
584 An axis with non-equidistant bins (also known as "variable binning"). It is
585 defined by an array of bin borders - one more than the number of
586 (non-overflow-) bins it has! As an example, an axis with two bin needs three
587 bin borders:
588 - lower edge of the first bin;
589 - higher edge of the first bin, identical to the lower edge of the second
590 bin;
591 - higher edge of the second bin
592
593 This axis cannot grow; the size of new bins would not be well defined.
594 */
596private:
597 /// Bin borders, one more than the number of regular bins.
598 std::vector<double> fBinBorders;
599
600protected:
601 /// See RAxisBase::HasSameBinBordersAs
602 bool HasSameBinBordersAs(const RAxisBase& other) const override;
603
604 /// Find the raw bin index (not adjusted) for the given coordinate `x`.
605 /// The resulting raw bin is 1-based.
606 /// \note Passing a bin border coordinate can either return the bin above or
607 /// below the bin border. I.e. don't do that for reliable results!
608 double FindBinRaw(double x) const noexcept
609 {
610 const auto bBegin = fBinBorders.begin();
611 const auto bEnd = fBinBorders.end();
612 // lower_bound finds the first bin border that is >= x.
613 auto iNotLess = std::lower_bound(bBegin, bEnd, x);
614 return iNotLess - bBegin;
615 }
616
617public:
618 RAxisIrregular() = default;
619
620 /// Construct a RAxisIrregular from a vector of bin borders.
621 /// \note The bin borders must be sorted in increasing order!
622 explicit RAxisIrregular(const std::vector<double> &binborders)
623 : RAxisBase(), fBinBorders(binborders)
624 {
625#ifdef R__DO_RANGE_CHECKS
626 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
627 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
628#endif // R__DO_RANGE_CHECKS
629 }
630
631 /// Construct a RAxisIrregular from a vector of bin borders.
632 /// \note The bin borders must be sorted in increasing order!
633 /// Faster, noexcept version taking an rvalue of binborders. The compiler will
634 /// know when it can take this one.
635 explicit RAxisIrregular(std::vector<double> &&binborders) noexcept
636 : RAxisBase(), fBinBorders(std::move(binborders))
637 {
638#ifdef R__DO_RANGE_CHECKS
639 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
640 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
641#endif // R__DO_RANGE_CHECKS
642 }
643
644 /// Construct a RAxisIrregular from a vector of bin borders.
645 /// \note The bin borders must be sorted in increasing order!
646 explicit RAxisIrregular(std::string_view title, const std::vector<double> &binborders)
647 : RAxisBase(title), fBinBorders(binborders)
648 {
649#ifdef R__DO_RANGE_CHECKS
650 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
651 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
652#endif // R__DO_RANGE_CHECKS
653 }
654
655 /// Construct a RAxisIrregular from a vector of bin borders.
656 /// \note The bin borders must be sorted in increasing order!
657 /// Faster, noexcept version taking an rvalue of binborders. The compiler will
658 /// know when it can take this one.
659 explicit RAxisIrregular(std::string_view title, std::vector<double> &&binborders) noexcept
660 : RAxisBase(title), fBinBorders(std::move(binborders))
661 {
662#ifdef R__DO_RANGE_CHECKS
663 if (!std::is_sorted(fBinBorders.begin(), fBinBorders.end()))
664 R__LOG_ERROR("HIST") << "Bin borders must be sorted!";
665#endif // R__DO_RANGE_CHECKS
666 }
667
668 /// Convert to RAxisConfig.
669 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinBorders()); }
670
671 /// Get the number of bins, excluding under- and overflow.
672 int GetNBinsNoOver() const noexcept final { return fBinBorders.size() - 1; }
673
674 /// Find the bin index (adjusted with under- and overflow) for the given coordinate `x`.
675 /// \note Passing a bin border coordinate can either return the bin above or
676 /// below the bin border. I.e. don't do that for reliable results!
677 int FindBin(double x) const noexcept final
678 {
679 int rawbin = FindBinRaw(x);
680 // No need for AdjustOverflowBinNumber(rawbin) here; lower_bound() is the
681 // answer: e.g. for x < *bBegin, rawbin is -1.
682 if (rawbin < GetFirstBin())
683 return kUnderflowBin;
684 if (rawbin >= GetLastBin() + 1)
685 return kOverflowBin;
686 return rawbin;
687 }
688
689 /// Get the bin center of the bin with the given index.
690 /// The result of this method on an overflow or underflow bin is unspecified.
691 double GetBinCenter(int bin) const final { return 0.5 * (fBinBorders[bin - 1] + fBinBorders[bin]); }
692
693 /// Get the lower bin border for a given bin index.
694 /// The result of this method on an underflow bin is unspecified.
695 double GetBinFrom(int bin) const final
696 {
697 if (bin == kOverflowBin)
698 return fBinBorders[GetLastBin()];
699 return fBinBorders[bin - 1];
700 }
701
702 /// If the coordinate `x` is within 10 ULPs of a bin low edge coordinate,
703 /// return the bin for which this is a low edge. If it's not a bin edge,
704 /// return `kInvalidBin`.
705 int GetBinIndexForLowEdge(double x) const noexcept final ;
706
707 /// This axis cannot be extended.
708 bool CanGrow() const noexcept final { return false; }
709
710 /// Access to the bin borders used by this axis.
711 const std::vector<double> &GetBinBorders() const noexcept { return fBinBorders; }
712};
713
714namespace Internal {
715
716template <>
717struct AxisConfigToType<RAxisConfig::kIrregular> {
719
720 Axis_t operator()(const RAxisConfig &cfg) { return RAxisIrregular(cfg.GetTitle(), cfg.GetBinBorders()); }
721};
722
723} // namespace Internal
724
725/**
726 \class RAxisLabels
727 A RAxisGrow that has a label assigned to each bin and a bin width of 1.
728
729 While filling still works through coordinates (i.e. arrays of doubles),
730 RAxisLabels allows to convert a string to a bin number or the bin's coordinate
731 center. The number of labels and the number of bins reported by RAxisGrow might
732 differ: the RAxisGrow will only grow when seeing a Fill(), while the RAxisLabels
733 will add a new label whenever `GetBinCenter()` is called.
734
735 Implementation details:
736 Filling happens often; `GetBinCenter()` needs to be fast. Thus the unordered_map.
737 The painter needs the reverse: it wants the label for bin 0, bin 1 etc. The axis
738 should only store the bin labels once; referencing them is (due to re-allocation,
739 hashing etc) non-trivial. So instead, build a `vector<string_view>` for the few
740 times the axis needs to be painted.
741 */
742class RAxisLabels: public RAxisGrow {
743private:
744 /// Map of label (view on `fLabels`'s elements) to bin index
745 std::unordered_map<std::string, int /*bin number*/> fLabelsIndex;
746
747public:
748 /// Construct a RAxisLables from a `vector` of `string_view`s, with title.
749 explicit RAxisLabels(std::string_view title, const std::vector<std::string_view> &labels)
750 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
751 {
752 for (size_t i = 0, n = labels.size(); i < n; ++i)
753 fLabelsIndex[std::string(labels[i])] = i;
754 }
755
756 /// Construct a RAxisLables from a `vector` of `string`s, with title.
757 explicit RAxisLabels(std::string_view title, const std::vector<std::string> &labels)
758 : RAxisGrow(title, labels.size(), 0., static_cast<double>(labels.size()))
759 {
760 for (size_t i = 0, n = labels.size(); i < n; ++i)
761 fLabelsIndex[labels[i]] = i;
762 }
763
764 /// Construct a RAxisLables from a `vector` of `string_view`s
765 explicit RAxisLabels(const std::vector<std::string_view> &labels): RAxisLabels("", labels) {}
766
767 /// Construct a RAxisLables from a `vector` of `string`s
768 explicit RAxisLabels(const std::vector<std::string> &labels): RAxisLabels("", labels) {}
769
770 /// Convert to RAxisConfig.
771 operator RAxisConfig() const { return RAxisConfig(GetTitle(), GetBinLabels()); }
772
773 /// Get the bin index with label.
774 int FindBinByName(const std::string &label)
775 {
776 auto insertResult = fLabelsIndex.insert({label, -1});
777 if (insertResult.second) {
778 // we have created a new label
779 int idx = fLabelsIndex.size() - 1;
780 insertResult.first->second = idx;
781 return idx;
782 }
783 return insertResult.first->second;
784 }
785
786 /// Get the center of the bin with label.
787 double GetBinCenterByName(const std::string &label)
788 {
789 return FindBinByName(label) + 0.5; // bin *center*
790 }
791
792 /// Build a vector of labels. The position in the vector defines the label's bin.
793 std::vector<std::string_view> GetBinLabels() const
794 {
795 std::vector<std::string_view> vec(fLabelsIndex.size());
796 for (const auto &kv: fLabelsIndex)
797 vec.at(kv.second) = kv.first;
798 return vec;
799 }
800
801 /// Result of an RAxisLabels label set comparison
803 /// Both axes have the same labels, mapping to the same bins
805
806 /// The other axis doesn't have some labels from this axis
808
809 /// The other axis has some labels which this axis doesn't have
811
812 /// The labels shared by both axes do not map into the same bins
814 };
815
816 /// Compare the labels of this axis with those of another axis
817 LabelsCmpFlags CompareBinLabels(const RAxisLabels& other) const noexcept {
818 // This will eventually contain the results of the labels comparison
820 size_t missing_in_other = 0;
821
822 // First, check how this axis' labels map into the other axis
823 for (const auto &kv: fLabelsIndex) {
824 auto iter = other.fLabelsIndex.find(kv.first);
825 if (iter == other.fLabelsIndex.cend()) {
826 ++missing_in_other;
827 } else if (iter->second != kv.second) {
829 }
830 }
831 if (missing_in_other > 0)
833
834 // If this covered all labels in the other axis, we're done
835 if (fLabelsIndex.size() == other.fLabelsIndex.size() + missing_in_other)
836 return result;
837
838 // Otherwise, we must check the labels of the other axis too
839 for (const auto &kv: other.fLabelsIndex)
840 if (fLabelsIndex.find(kv.first) == fLabelsIndex.cend())
842 return result;
843 }
844};
845
846namespace Internal {
847
848template <>
851
852 Axis_t operator()(const RAxisConfig &cfg) { return RAxisLabels(cfg.GetTitle(), cfg.GetBinLabels()); }
853};
854
855} // namespace Internal
856
857///\name Axis Compatibility
858///\{
860 kIdentical, ///< Source and target axes are identical
861
862 kContains, ///< The source is a subset of bins of the target axis
863
864 /// The bins of the source axis have finer granularity, but the bin borders
865 /// are compatible. Example:
866 /// source: 0., 1., 2., 3., 4., 5., 6.; target: 0., 2., 5., 6.
867 /// Note that this is *not* a symmetrical property: only one of
868 /// CanMerge(source, target), CanMap(target, source) can return kContains.
869 kSampling,
870
871 /// The source axis and target axis have different binning. Example:
872 /// source: 0., 1., 2., 3., 4., target: 0., 0.1, 0.2, 0.3, 0.4
874};
875
876/// Whether (and how) the source axis can be merged into the target axis.
877EAxisCompatibility CanMap(const RAxisEquidistant &target, const RAxisEquidistant &source) noexcept;
878///\}
879
880} // namespace Experimental
881} // namespace ROOT
882
883#endif // ROOT7_RAxis header guard
#define R__LOG_ERROR(...)
Definition: RLogger.hxx:362
#define d(i)
Definition: RSha256.hxx:102
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t cursor
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Random const_iterator through bins.
Definition: RAxis.hxx:126
std::random_access_iterator_tag iterator_category
Definition: RAxis.hxx:130
const_iterator & operator-=(int d) noexcept
Definition: RAxis.hxx:181
const_iterator operator-(int d) noexcept
Definition: RAxis.hxx:197
int operator-(const const_iterator &j) noexcept
Definition: RAxis.hxx:205
const_iterator operator++(int) noexcept
i++
Definition: RAxis.hxx:158
friend bool operator>(const_iterator lhs, const_iterator rhs) noexcept
i > j
Definition: RAxis.hxx:353
const_iterator operator+(int d) noexcept
Definition: RAxis.hxx:188
int fCursor
Current iteration position.
Definition: RAxis.hxx:127
const int * operator->() const noexcept
Definition: RAxis.hxx:220
const_iterator & operator+=(int d) noexcept
Definition: RAxis.hxx:174
const_iterator & operator--() noexcept
–i
Definition: RAxis.hxx:150
const_iterator & operator++() noexcept
++i
Definition: RAxis.hxx:142
const_iterator operator--(int) noexcept
Definition: RAxis.hxx:166
const_iterator(int cursor) noexcept
Initialize a const_iterator with its position.
Definition: RAxis.hxx:139
friend bool operator<=(const_iterator lhs, const_iterator rhs) noexcept
i <= j
Definition: RAxis.hxx:359
friend bool operator<(const_iterator lhs, const_iterator rhs) noexcept
i < j
Definition: RAxis.hxx:347
friend bool operator>=(const_iterator lhs, const_iterator rhs) noexcept
i >= j
Definition: RAxis.hxx:365
friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept
i == j
Definition: RAxis.hxx:371
friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept
i != j
Definition: RAxis.hxx:377
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:279
const_iterator end() const noexcept
Get a const_iterator pointing beyond the last regular bin.
Definition: RAxis.hxx:291
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:252
RAxisBase(const RAxisBase &)=default
double GetMinimum() const
Get the low end of the axis range.
Definition: RAxis.hxx:316
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:234
double GetMaximum() const
Get the high end of the axis range.
Definition: RAxis.hxx:319
std::string fTitle
Title of this axis, used for graphics / text.
Definition: RAxis.hxx:334
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:262
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:240
RAxisBase & operator=(RAxisBase &&)=default
const_iterator begin() const noexcept
Get a const_iterator pointing to the first regular bin.
Definition: RAxis.hxx:288
int GetOverflowBin() const noexcept
Get the bin index for the overflow bin (or kInvalidBin if CanGrow()).
Definition: RAxis.hxx:271
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:237
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:282
double GetBinTo(int bin) const
Get the high bin border ("right edge") for the given bin index.
Definition: RAxis.hxx:310
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:231
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:249
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:389
RAxisEquidistant(std::string_view title, int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition: RAxis.hxx:427
RAxisEquidistant(int nbinsNoOver, double low, double high) noexcept
Initialize a RAxisEquidistant.
Definition: RAxis.hxx:441
double GetBinWidth() const noexcept
Get the width of the bins.
Definition: RAxis.hxx:465
double FindBinRaw(double x) const noexcept
Find the raw bin index (not adjusted) for the given coordinate.
Definition: RAxis.hxx:411
double GetInverseBinWidth() const noexcept
Get the inverse of the width of the bins.
Definition: RAxis.hxx:468
int GetBinIndexForLowEdge(double x) const noexcept final
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
bool HasSameBinBordersAs(const RAxisBase &other) const override
See RAxisBase::HasSameBinBordersAs.
Definition: RAxis.cxx:76
double fInvBinWidth
The inverse of the bin width.
Definition: RAxis.hxx:392
int GetNBinsNoOver() const noexcept final
Get the number of bins, excluding under- and overflow.
Definition: RAxis.hxx:449
double GetBinCenter(int bin) const final
Get the bin center for the given bin index.
Definition: RAxis.hxx:474
double GetBinFrom(int bin) const final
Get the low bin border for the given bin index.
Definition: RAxis.hxx:480
static double GetInvBinWidth(int nbinsNoOver, double lowOrHigh, double highOrLow)
Determine the inverse bin width.
Definition: RAxis.hxx:399
bool CanGrow() const noexcept override
This axis cannot grow.
Definition: RAxis.hxx:462
unsigned int fNBinsNoOver
Number of bins excluding under- and overflow.
Definition: RAxis.hxx:393
int FindBin(double x) const noexcept final
Find the adjusted bin index (returning kUnderflowBin for underflow and kOverflowBin for overflow) for...
Definition: RAxis.hxx:455
double fLow
The lower limit of the axis.
Definition: RAxis.hxx:391
An axis that can extend its range, keeping the number of its bins unchanged.
Definition: RAxis.hxx:509
RAxisGrow(std::string_view title, int nbins, double low, double high) noexcept
Initialize a RAxisGrow.
Definition: RAxis.hxx:521
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:534
bool CanGrow() const noexcept final
This axis kind can increase its range.
Definition: RAxis.hxx:566
An axis with non-equidistant bins (also known as "variable binning").
Definition: RAxis.hxx:595
bool CanGrow() const noexcept final
This axis cannot be extended.
Definition: RAxis.hxx:708
int FindBin(double x) const noexcept final
Find the bin index (adjusted with under- and overflow) for the given coordinate x.
Definition: RAxis.hxx:677
const std::vector< double > & GetBinBorders() const noexcept
Access to the bin borders used by this axis.
Definition: RAxis.hxx:711
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:635
RAxisIrregular(const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:622
double GetBinCenter(int bin) const final
Get the bin center of the bin with the given index.
Definition: RAxis.hxx:691
std::vector< double > fBinBorders
Bin borders, one more than the number of regular bins.
Definition: RAxis.hxx:598
int GetBinIndexForLowEdge(double x) const noexcept final
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
RAxisIrregular(std::string_view title, std::vector< double > &&binborders) noexcept
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:659
double GetBinFrom(int bin) const final
Get the lower bin border for a given bin index.
Definition: RAxis.hxx:695
RAxisIrregular(std::string_view title, const std::vector< double > &binborders)
Construct a RAxisIrregular from a vector of bin borders.
Definition: RAxis.hxx:646
double FindBinRaw(double x) const noexcept
Find the raw bin index (not adjusted) for the given coordinate x.
Definition: RAxis.hxx:608
int GetNBinsNoOver() const noexcept final
Get the number of bins, excluding under- and overflow.
Definition: RAxis.hxx:672
A RAxisGrow that has a label assigned to each bin and a bin width of 1.
Definition: RAxis.hxx:742
LabelsCmpFlags
Result of an RAxisLabels label set comparison.
Definition: RAxis.hxx:802
@ kLabelsCmpDisordered
The labels shared by both axes do not map into the same bins.
Definition: RAxis.hxx:813
@ kLabelsCmpSubset
The other axis doesn't have some labels from this axis.
Definition: RAxis.hxx:807
@ kLabelsCmpSame
Both axes have the same labels, mapping to the same bins.
Definition: RAxis.hxx:804
@ kLabelsCmpSuperset
The other axis has some labels which this axis doesn't have.
Definition: RAxis.hxx:810
RAxisLabels(const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings.
Definition: RAxis.hxx:768
RAxisLabels(const std::vector< std::string_view > &labels)
Construct a RAxisLables from a vector of string_views.
Definition: RAxis.hxx:765
std::unordered_map< std::string, int > fLabelsIndex
Map of label (view on fLabels's elements) to bin index.
Definition: RAxis.hxx:745
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:793
double GetBinCenterByName(const std::string &label)
Get the center of the bin with label.
Definition: RAxis.hxx:787
int FindBinByName(const std::string &label)
Get the bin index with label.
Definition: RAxis.hxx:774
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:749
RAxisLabels(std::string_view title, const std::vector< std::string > &labels)
Construct a RAxisLables from a vector of strings, with title.
Definition: RAxis.hxx:757
LabelsCmpFlags CompareBinLabels(const RAxisLabels &other) const noexcept
Compare the labels of this axis with those of another axis.
Definition: RAxis.hxx:817
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:126
bool operator>=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i >= j
Definition: RAxis.hxx:365
bool operator==(const TString &t, const std::string &s)
Definition: REveTypes.cxx:27
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:353
@ 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:347
bool operator<=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i <= j
Definition: RAxis.hxx:359
bool operator!=(RAxisBase::const_iterator lhs, RAxisBase::const_iterator rhs) noexcept
i != j
Definition: RAxis.hxx:377
VecExpr< UnaryOp< Fabs< T >, VecExpr< A, T, D >, T >, T, D > fabs(const VecExpr< A, T, D > &rhs)
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
Axis_t operator()(const RAxisConfig &cfg) noexcept
Definition: RAxis.hxx:575
Converts a RAxisConfig of whatever kind to the corresponding RAxisBase-derived object.
Definition: civetweb.c:1856