use num_traits::ToPrimitive;
pub struct UniqueVal {
pub val: f64,
pub first: usize,
pub last: usize,
}
pub struct Bin {
pub bin_start: f64,
pub bin_end: f64,
pub count: u64,
}
impl PartialEq for Bin {
fn eq(&self, other: &Self) -> bool {
let starts_eq: bool = self.bin_start == other.bin_start;
let ends_eq: bool = self.bin_end == other.bin_end;
let counts_eq: bool = self.count == other.count;
starts_eq && ends_eq && counts_eq
}
}
pub type Classification = Vec<Bin>;
pub fn to_vec_f64<T: ToPrimitive>(data: &[T]) -> Vec<f64> {
let mut result: Vec<f64> = vec![];
for item in data {
result.push(item.to_f64().unwrap());
}
result
}
pub fn create_unique_val_mapping(unique_val_map: &mut Vec<UniqueVal>, vals: &[f64]) {
unique_val_map.clear();
let mut idx: i64 = -1;
for (i, item) in vals.iter().enumerate() {
if unique_val_map.is_empty() {
idx += 1;
unique_val_map.push(UniqueVal {
val: *item,
first: i,
last: i,
});
} else if unique_val_map[idx as usize].val != *item {
unique_val_map[idx as usize].last = i - 1;
idx += 1;
unique_val_map.push(UniqueVal {
val: *item,
first: i,
last: i,
});
}
}
}
pub fn unique_to_normal_breaks(
u_val_breaks: &Vec<usize>,
u_val_map: &[UniqueVal],
normal_breaks: &mut Vec<usize>,
) {
if normal_breaks.len() != u_val_breaks.len() {
normal_breaks.resize(u_val_breaks.len(), 0);
}
for i in 0..u_val_breaks.len() {
normal_breaks[i] = u_val_map[u_val_breaks[i]].first;
}
}
pub fn breaks_to_classification<T: ToPrimitive>(breaks: &Vec<f64>, data: &[T]) -> Classification {
let data = to_vec_f64(data);
let mut min_value = data[0];
let mut max_value = data[0];
for item in &data {
if *item < min_value {
min_value = *item;
}
if *item > max_value {
max_value = *item;
}
}
let mut bounds: Vec<f64> = vec![min_value];
for item in breaks {
bounds.push(*item);
}
bounds.push(max_value);
let mut results: Classification = vec![];
for i in 0..(bounds.len() - 1) {
results.push(Bin {
bin_start: bounds[i],
bin_end: bounds[i + 1],
count: 0,
});
}
let num_bins = results.len();
for bin in results.iter_mut().take(num_bins) {
for item in &data {
if bin.bin_start <= *item && *item < bin.bin_end {
bin.count += 1;
}
}
}
results[num_bins - 1].count += 1;
results
}
pub fn classify_val(val: f64, class: &Classification) -> Option<usize> {
if val < class[0].bin_start || val > class[class.len() - 1].bin_end {
return None;
}
for (i, _item) in class.iter().enumerate() {
if class[i].bin_start <= val && val < class[i].bin_end {
return Some(i);
}
}
Some(class.len() - 1) }