use ndarray::{Array1, ArrayBase, AsArray, Dimension, ViewRepr, Zip};
use crate::prelude::*;
use crate::statistics::min_max;
#[inline]
pub fn histogram<'a, T, A, D>(
data: A,
bins: Option<usize>,
threads: Option<usize>,
) -> Result<Array1<i64>, ImgalError>
where
A: AsArray<'a, T, D>,
D: Dimension,
T: 'a + AsNumeric,
{
let data: ArrayBase<ViewRepr<&'a T>, D> = data.into();
let bins = bins.unwrap_or(256);
if data.is_empty() {
return Err(ImgalError::InvalidParameterEmptyArray { param_name: "data" });
}
if bins == 0 {
return Err(ImgalError::InvalidParameterValueEqual {
param_name: "bins",
value: 0,
});
}
let (min, max) = min_max(&data, threads)?;
let bin_width: f64 = (max.to_f64() - min.to_f64()) / bins as f64;
let hist_seq = || {
let mut hist = vec![0; bins];
data.iter().for_each(|&v| {
let bin_index: usize = ((v.to_f64() - min.to_f64()) / bin_width) as usize;
let bin_index = bin_index.min(bins - 1);
hist[bin_index] += 1;
});
Array1::from_vec(hist)
};
let hist_par = || {
let hist = Zip::from(&data).par_fold(
|| vec![0; bins],
|mut thread_hist, &v| {
let bin_index: usize = ((v.to_f64() - min.to_f64()) / bin_width) as usize;
let bin_index = bin_index.min(bins - 1);
thread_hist[bin_index] += 1;
thread_hist
},
|mut hist_a, hist_b| {
hist_a
.iter_mut()
.zip(hist_b.iter())
.for_each(|(a, b)| *a += b);
hist_a
},
);
Array1::from_vec(hist)
};
Ok(par!(threads, seq_exp: hist_seq(), par_exp: hist_par()))
}
#[inline]
pub fn histogram_bin_midpoint<T>(index: usize, min: T, max: T, bins: usize) -> Result<T, ImgalError>
where
T: AsNumeric,
{
if bins == 0 {
return Err(ImgalError::InvalidParameterValueEqual {
param_name: "bins",
value: 0,
});
}
let min = min.to_f64();
let max = max.to_f64();
let bin_width = (max - min) / bins as f64;
Ok(T::from_f64(min + (index as f64 + 0.5) * bin_width))
}
#[inline]
pub fn histogram_bin_range<T>(
index: usize,
min: T,
max: T,
bins: usize,
) -> Result<(T, T), ImgalError>
where
T: AsNumeric,
{
if bins == 0 {
return Err(ImgalError::InvalidParameterValueEqual {
param_name: "bins",
value: 0,
});
}
let min = min.to_f64();
let max = max.to_f64();
let bin_width = (max - min) / bins as f64;
let bin_start = min + (index as f64 * bin_width);
let bin_end = bin_start + bin_width;
Ok((T::from_f64(bin_start), T::from_f64(bin_end)))
}