mod affinity;
mod dbscan;
mod gmm;
mod hierarchical;
mod kmeans;
mod mean_shift;
mod spectral;
pub use affinity::affinity_propagation;
pub use dbscan::{DbscanResult, dbscan};
pub use gmm::{GmmResult, gmm_em};
pub use hierarchical::{Linkage, agglomerative};
pub use kmeans::{KMeansResult, kmeans};
pub use mean_shift::{MeanShiftResult, mean_shift};
pub use spectral::spectral;
pub const NOISE: usize = usize::MAX;
#[must_use]
pub fn relabel_contiguous(labels: &[usize]) -> Vec<usize> {
let mut mapping: std::collections::HashMap<usize, usize> = std::collections::HashMap::new();
let mut next = 0_usize;
labels
.iter()
.map(|&l| {
if l == NOISE {
return NOISE;
}
*mapping.entry(l).or_insert_with(|| {
let id = next;
next += 1;
id
})
})
.collect()
}
#[must_use]
pub fn cluster_count(labels: &[usize]) -> usize {
labels
.iter()
.filter(|&&l| l != NOISE)
.collect::<std::collections::HashSet<_>>()
.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn relabel_makes_ids_contiguous_by_first_appearance() {
let got = relabel_contiguous(&[5, 5, 9, 9, 2]);
assert_eq!(got, vec![0, 0, 1, 1, 2], "relabeled = {got:?}");
}
#[test]
fn relabel_preserves_noise() {
let got = relabel_contiguous(&[NOISE, 7, NOISE, 7]);
assert_eq!(got, vec![NOISE, 0, NOISE, 0], "relabeled = {got:?}");
}
#[test]
fn cluster_count_ignores_noise() {
assert_eq!(cluster_count(&[0, 1, NOISE, 1, NOISE]), 2);
}
}