use super::StatsError;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GainPoint {
pub fraction: f64,
pub gain: f64,
pub lift: f64,
}
pub fn gain_curve(scores: &[f64], labels: &[bool]) -> Result<Vec<GainPoint>, StatsError> {
if scores.len() != labels.len() {
return Err(StatsError::LengthMismatch {
scores: scores.len(),
labels: labels.len(),
});
}
if scores.is_empty() {
return Err(StatsError::EmptyInput);
}
let total_p = labels.iter().filter(|&&l| l).count();
if total_p == 0 {
return Err(StatsError::NoPositiveLabels);
}
let mut order: Vec<usize> = (0..scores.len()).collect();
order.sort_by(|&a, &b| {
scores[b]
.partial_cmp(&scores[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
let n = scores.len() as f64;
let p = total_p as f64;
let mut points = Vec::with_capacity(order.len() + 1);
points.push(GainPoint {
fraction: 0.0,
gain: 0.0,
lift: f64::NAN,
});
let mut cum_pos = 0usize;
for (k, &i) in order.iter().enumerate() {
if labels[i] {
cum_pos += 1;
}
let fraction = (k + 1) as f64 / n;
let gain = cum_pos as f64 / p;
points.push(GainPoint {
fraction,
gain,
lift: gain / fraction,
});
}
Ok(points)
}