#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ClassPrediction {
pub class_id: usize,
pub confidence: f32,
}
impl ClassPrediction {
pub fn new(class_id: usize, confidence: f32) -> Self {
Self {
class_id,
confidence,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Classifications {
pub predictions: Vec<ClassPrediction>,
}
impl Classifications {
pub fn new(predictions: Vec<ClassPrediction>) -> Self {
Self { predictions }
}
pub fn from_scores(scores: &[f32]) -> Self {
Self::new(
scores
.iter()
.enumerate()
.map(|(class_id, &confidence)| ClassPrediction::new(class_id, confidence))
.collect(),
)
}
pub fn top_k(&self, k: usize) -> Vec<ClassPrediction> {
let mut sorted = self.predictions.clone();
sorted.sort_by(|a, b| b.confidence.total_cmp(&a.confidence));
sorted.truncate(k);
sorted
}
pub fn top1(&self) -> Option<ClassPrediction> {
self.predictions
.iter()
.copied()
.max_by(|a, b| a.confidence.total_cmp(&b.confidence))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_scores_indexes_predictions_by_position() {
let classifications = Classifications::from_scores(&[0.1, 0.7, 0.2]);
assert_eq!(classifications.predictions.len(), 3);
assert_eq!(classifications.predictions[1], ClassPrediction::new(1, 0.7));
}
#[test]
fn top1_returns_the_highest_confidence_prediction() {
let classifications = Classifications::from_scores(&[0.1, 0.7, 0.2]);
assert_eq!(classifications.top1(), Some(ClassPrediction::new(1, 0.7)));
}
#[test]
fn top1_of_empty_is_none() {
assert_eq!(Classifications::default().top1(), None);
}
#[test]
fn top_k_sorts_descending_and_truncates() {
let classifications = Classifications::from_scores(&[0.1, 0.7, 0.5, 0.2]);
let top2 = classifications.top_k(2);
assert_eq!(
top2,
vec![ClassPrediction::new(1, 0.7), ClassPrediction::new(2, 0.5)]
);
}
#[test]
fn top_k_larger_than_predictions_returns_all() {
let classifications = Classifications::from_scores(&[0.1, 0.7]);
assert_eq!(classifications.top_k(10).len(), 2);
}
}