ndhistogram/axis/uniformnoflow.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
use crate::error::AxisError;
use super::{Axis, BinInterval, Uniform};
use std::fmt::{Debug, Display};
use num_traits::{Float, Num, NumCast, NumOps};
/// An axis with equal sized bins and no under/overflow bins.
///
/// An axis with N equally spaced, equal sized, bins between [low, high).
/// Similar to [Uniform] but this axis has no over/underflow bins.
/// Hence it has N bins.
///
/// For floating point types, infinities and NaN do not map to any bin.
///
/// # Example
/// Create a 1D histogram with 10 uniformly spaced bins between -5.0 and 5.0.
/// ```rust
/// use ndhistogram::{ndhistogram, Histogram};
/// use ndhistogram::axis::{Axis, UniformNoFlow, BinInterval};
/// # fn main() -> Result<(), ndhistogram::Error> {
/// let hist = ndhistogram!(UniformNoFlow::new(10, -5.0, 5.0)?);
/// let axis = &hist.axes().as_tuple().0;
/// assert_eq!(axis.bin(0), Some(BinInterval::new(-5.0, -4.0)));
/// assert_eq!(axis.bin(10), None);
/// # Ok(()) }
/// ```
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UniformNoFlow<T = f64> {
axis: Uniform<T>,
}
impl<T> UniformNoFlow<T>
where
T: PartialOrd + NumCast + NumOps + Copy,
{
/// Factory method to create an axis with num uniformly spaced bins in the range [low, high) with no under/overflow bins.
///
/// The parameters have the same constraints as [Uniform::new], otherwise an error is returned.
pub fn new(num: usize, low: T, high: T) -> Result<Self, AxisError>
where
T: Float,
{
Ok(Self {
axis: Uniform::new(num, low, high)?,
})
}
/// Factory method to create an axis with num uniformly spaced bins in the range [low, low+num*step) with no under/overflow bins.
///
/// The parameters have the same constraints as [Uniform::with_step_size], otherwise an error is returned.
pub fn with_step_size(num: usize, low: T, step: T) -> Result<Self, AxisError>
where
T: Num,
{
Ok(Self {
axis: Uniform::with_step_size(num, low, step)?,
})
}
}
impl<T> UniformNoFlow<T> {
/// Return the lowest bin edge.
pub fn low(&self) -> &T {
self.axis.low()
}
/// Return the highest bin edge.
pub fn high(&self) -> &T {
self.axis.high()
}
}
impl<T> Axis for UniformNoFlow<T>
where
T: PartialOrd + NumCast + NumOps + Copy,
{
type Coordinate = T;
type BinInterval = BinInterval<T>;
#[inline]
fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
let index = self.axis.index(coordinate)?;
if index == 0 || index + 1 == self.axis.num_bins() {
return None;
}
Some(index - 1)
}
fn num_bins(&self) -> usize {
self.axis.num_bins() - 2
}
fn bin(&self, index: usize) -> Option<Self::BinInterval> {
let bin = self.axis.bin(index + 1)?;
match bin {
BinInterval::Underflow { end: _ } => None,
BinInterval::Overflow { start: _ } => None,
BinInterval::Bin { start: _, end: _ } => Some(bin),
}
}
}
impl<'a, T> IntoIterator for &'a UniformNoFlow<T>
where
T: PartialOrd + NumCast + NumOps + Copy,
{
type Item = (usize, <Uniform<T> as Axis>::BinInterval);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T> Display for UniformNoFlow<T>
where
T: PartialOrd + NumCast + NumOps + Copy + Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Axis{{# bins={}, range=[{}, {}), class={}}}",
self.num_bins(),
self.axis.low(),
self.axis.high(),
stringify!(UniformNoFlow)
)
}
}