use ndarray::{ArrayBase, ArrayD, AsArray, Dimension, ViewRepr, Zip};
use crate::error::ImgalError;
use crate::statistics::linear_percentile;
use crate::traits::numeric::AsNumeric;
pub fn percentile_normalize<'a, T, A, D>(
data: A,
min: f64,
max: f64,
clip: Option<bool>,
epsilon: Option<f64>,
) -> Result<ArrayD<f64>, ImgalError>
where
A: AsArray<'a, T, D>,
D: Dimension,
T: 'a + AsNumeric,
{
if min < 0.0 {
return Err(ImgalError::InvalidParameterValueOutsideRange {
param_name: "min",
value: min,
min: 0.0,
max: 1.0,
});
}
if max < 0.0 {
return Err(ImgalError::InvalidParameterValueOutsideRange {
param_name: "max",
value: max,
min: 0.0,
max: 1.0,
});
}
let view: ArrayBase<ViewRepr<&'a T>, D> = data.into();
let clip = clip.unwrap_or(false);
let epsilon = epsilon.unwrap_or(1e-20);
let per_min: f64 = linear_percentile(&view, min, None, None).unwrap()[0];
let per_max: f64 = linear_percentile(&view, max, None, None).unwrap()[0];
let denom = per_max - per_min + epsilon;
let mut norm_arr = ArrayD::<f64>::zeros(view.shape());
Zip::from(view.into_dyn())
.and(norm_arr.view_mut())
.for_each(|v, n| {
*n = (v.to_f64() - per_min) / denom;
});
if clip {
Zip::from(&mut norm_arr).for_each(|v| {
*v = (*v).clamp(0.0, 1.0);
})
}
Ok(norm_arr)
}