fdars_core/classification/
dd.rs1use crate::depth::fraiman_muniz_1d;
4use crate::error::FdarError;
5use crate::matrix::FdMatrix;
6
7use super::cv::extract_class_data;
8use super::kernel::{argmax_class, scalar_depth_for_obs};
9use super::{compute_accuracy, confusion_matrix, remap_labels, ClassifResult};
10
11fn compute_class_depths(data: &FdMatrix, class_indices: &[Vec<usize>], n: usize) -> FdMatrix {
13 let g = class_indices.len();
14 let mut depth_scores = FdMatrix::zeros(n, g);
15 for c in 0..g {
16 if class_indices[c].is_empty() {
17 continue;
18 }
19 let class_data = extract_class_data(data, &class_indices[c]);
20 let depths = fraiman_muniz_1d(data, &class_data, true);
21 for i in 0..n {
22 depth_scores[(i, c)] = depths[i];
23 }
24 }
25 depth_scores
26}
27
28pub(super) fn blend_scalar_depths(
30 depth_scores: &mut FdMatrix,
31 cov: &FdMatrix,
32 class_indices: &[Vec<usize>],
33 n: usize,
34) {
35 let g = class_indices.len();
36 let p = cov.ncols();
37 for c in 0..g {
38 for i in 0..n {
39 let sd = scalar_depth_for_obs(cov, i, &class_indices[c], p);
40 depth_scores[(i, c)] = 0.7 * depth_scores[(i, c)] + 0.3 * sd;
41 }
42 }
43}
44
45#[must_use = "expensive computation whose result should not be discarded"]
52pub fn fclassif_dd(
53 data: &FdMatrix,
54 y: &[usize],
55 scalar_covariates: Option<&FdMatrix>,
56) -> Result<ClassifResult, FdarError> {
57 let n = data.nrows();
58 if n == 0 || y.len() != n {
59 return Err(FdarError::InvalidDimension {
60 parameter: "data/y",
61 expected: "n > 0 and y.len() == n".to_string(),
62 actual: format!("n={}, y.len()={}", n, y.len()),
63 });
64 }
65
66 let (labels, g) = remap_labels(y);
67 if g < 2 {
68 return Err(FdarError::InvalidParameter {
69 parameter: "y",
70 message: format!("need at least 2 classes, got {g}"),
71 });
72 }
73
74 let class_indices: Vec<Vec<usize>> = (0..g)
75 .map(|c| (0..n).filter(|&i| labels[i] == c).collect())
76 .collect();
77
78 let mut depth_scores = compute_class_depths(data, &class_indices, n);
79
80 if let Some(cov) = scalar_covariates {
81 blend_scalar_depths(&mut depth_scores, cov, &class_indices, n);
82 }
83
84 let predicted: Vec<usize> = (0..n)
85 .map(|i| {
86 let scores: Vec<f64> = (0..g).map(|c| depth_scores[(i, c)]).collect();
87 argmax_class(&scores)
88 })
89 .collect();
90
91 let accuracy = compute_accuracy(&labels, &predicted);
92 let confusion = confusion_matrix(&labels, &predicted, g);
93
94 Ok(ClassifResult {
95 predicted,
96 probabilities: Some(depth_scores),
97 accuracy,
98 confusion,
99 n_classes: g,
100 ncomp: 0,
101 })
102}