fdars_core/classification/
mod.rs1use crate::error::FdarError;
14use crate::matrix::FdMatrix;
15use crate::regression::fdata_to_pc_1d;
16
17pub mod cv;
18pub mod dd;
19pub mod fit;
20pub mod kernel;
21pub mod knn;
22pub mod lda;
23pub mod qda;
24
25#[cfg(test)]
26mod tests;
27
28#[derive(Debug, Clone, PartialEq)]
34#[non_exhaustive]
35pub struct ClassifResult {
36 pub predicted: Vec<usize>,
38 pub probabilities: Option<FdMatrix>,
40 pub accuracy: f64,
42 pub confusion: Vec<Vec<usize>>,
44 pub n_classes: usize,
46 pub ncomp: usize,
48}
49
50#[derive(Debug, Clone, PartialEq)]
52#[non_exhaustive]
53pub struct ClassifCvResult {
54 pub error_rate: f64,
56 pub fold_errors: Vec<f64>,
58 pub best_ncomp: usize,
60}
61
62pub(crate) fn remap_labels(y: &[usize]) -> (Vec<usize>, usize) {
68 let mut labels: Vec<usize> = y.to_vec();
69 let mut unique: Vec<usize> = y.to_vec();
70 unique.sort_unstable();
71 unique.dedup();
72 let g = unique.len();
73 for label in &mut labels {
74 *label = unique.iter().position(|&u| u == *label).unwrap_or(0);
75 }
76 (labels, g)
77}
78
79fn confusion_matrix(true_labels: &[usize], pred_labels: &[usize], g: usize) -> Vec<Vec<usize>> {
81 let mut cm = vec![vec![0usize; g]; g];
82 for (&t, &p) in true_labels.iter().zip(pred_labels.iter()) {
83 if t < g && p < g {
84 cm[t][p] += 1;
85 }
86 }
87 cm
88}
89
90pub(crate) fn class_means_and_priors(
92 features: &FdMatrix,
93 labels: &[usize],
94 g: usize,
95) -> (Vec<Vec<f64>>, Vec<usize>, Vec<f64>) {
96 let n = features.nrows();
97 let d = features.ncols();
98 let mut counts = vec![0usize; g];
99 let mut class_means = vec![vec![0.0; d]; g];
100 for i in 0..n {
101 let c = labels[i];
102 counts[c] += 1;
103 for j in 0..d {
104 class_means[c][j] += features[(i, j)];
105 }
106 }
107 for c in 0..g {
108 if counts[c] > 0 {
109 for j in 0..d {
110 class_means[c][j] /= counts[c] as f64;
111 }
112 }
113 }
114 let priors: Vec<f64> = counts.iter().map(|&c| c as f64 / n as f64).collect();
115 (class_means, counts, priors)
116}
117
118fn compute_accuracy(true_labels: &[usize], pred_labels: &[usize]) -> f64 {
120 let n = true_labels.len();
121 if n == 0 {
122 return 0.0;
123 }
124 let correct = true_labels
125 .iter()
126 .zip(pred_labels.iter())
127 .filter(|(&t, &p)| t == p)
128 .count();
129 correct as f64 / n as f64
130}
131
132pub(crate) fn build_feature_matrix(
134 data: &FdMatrix,
135 scalar_covariates: Option<&FdMatrix>,
136 ncomp: usize,
137) -> Result<(FdMatrix, Vec<f64>, FdMatrix), FdarError> {
138 let fpca = fdata_to_pc_1d(data, ncomp)?;
139 let n = data.nrows();
140 let d_pc = fpca.scores.ncols();
141 let d_cov = scalar_covariates.map_or(0, super::matrix::FdMatrix::ncols);
142 let d = d_pc + d_cov;
143
144 let mut features = FdMatrix::zeros(n, d);
145 for i in 0..n {
146 for j in 0..d_pc {
147 features[(i, j)] = fpca.scores[(i, j)];
148 }
149 if let Some(cov) = scalar_covariates {
150 for j in 0..d_cov {
151 features[(i, d_pc + j)] = cov[(i, j)];
152 }
153 }
154 }
155
156 Ok((features, fpca.mean, fpca.rotation))
157}
158
159pub use cv::fclassif_cv;
164pub use dd::fclassif_dd;
165pub(crate) use fit::classif_predict_probs;
166pub use fit::{
167 fclassif_cv_with_config, fclassif_knn_fit, fclassif_lda_fit, fclassif_qda_fit, ClassifCvConfig,
168 ClassifFit, ClassifMethod,
169};
170pub use kernel::fclassif_kernel;
171pub use knn::fclassif_knn;
172pub use lda::fclassif_lda;
173pub use qda::fclassif_qda;