use ndarray::Array1;
use smartcore::metrics::{ClassificationMetrics, Metrics};
use super::Scorer;
#[derive(Debug, Clone, Copy)]
pub struct SmartcoreF1 {
pub beta: f64,
}
impl Default for SmartcoreF1 {
fn default() -> Self {
Self { beta: 1.0 }
}
}
impl Scorer for SmartcoreF1 {
fn score(&self, y_true: &Array1<f64>, y_pred: &Array1<f64>) -> f64 {
let t = y_true.to_vec();
let p = y_pred.to_vec();
ClassificationMetrics::<f64>::f1(self.beta).get_score(&t, &p)
}
fn name(&self) -> &str {
"f1"
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SmartcoreRocAuc;
impl Scorer for SmartcoreRocAuc {
fn score(&self, y_true: &Array1<f64>, y_pred: &Array1<f64>) -> f64 {
let t = y_true.to_vec();
let p = y_pred.to_vec();
ClassificationMetrics::<f64>::roc_auc_score().get_score(&t, &p)
}
fn name(&self) -> &str {
"roc_auc"
}
}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::array;
#[test]
fn f1_perfect_prediction_is_one() {
let t = array![0.0, 1.0, 1.0, 0.0, 1.0];
let f1 = SmartcoreF1::default().score(&t, &t);
assert!((f1 - 1.0).abs() < 1e-9, "f1 = {f1}");
}
#[test]
fn roc_auc_perfect_ranking_is_one() {
let t = array![0.0, 0.0, 1.0, 1.0];
let scores = array![0.1, 0.2, 0.8, 0.9];
let auc = SmartcoreRocAuc.score(&t, &scores);
assert!((auc - 1.0).abs() < 1e-9, "auc = {auc}");
}
#[test]
fn matches_calling_smartcore_directly() {
let t = array![0.0, 1.0, 1.0, 0.0, 1.0, 0.0];
let p = array![0.0, 1.0, 0.0, 0.0, 1.0, 1.0];
let via_adapter = SmartcoreF1::default().score(&t, &p);
let direct = ClassificationMetrics::<f64>::f1(1.0).get_score(&t.to_vec(), &p.to_vec());
assert_eq!(via_adapter, direct);
}
}