| 143 | } |
| 144 | |
| 145 | pub fn feature_pca_analysis( |
| 146 | feature_rows: &[Vec<f64>], |
| 147 | feature_importance_mean: &[f64], |
| 148 | variance_thresh: f64, |
| 149 | ) -> Result<PcaCorrelation, String> { |
| 150 | if feature_rows.is_empty() { |
| 151 | return Err("feature_rows cannot be empty".to_string()); |
| 152 | } |
| 153 | let n_features = feature_rows[0].len(); |
| 154 | if feature_importance_mean.len() != n_features { |
| 155 | return Err("feature_importance_mean length mismatch".to_string()); |
| 156 | } |
| 157 | |
| 158 | let (eval, evec, _) = compute_pca(feature_rows, variance_thresh)?; |
| 159 | |
| 160 | let pcs = eval.len(); |
| 161 | let mut all_eigs = Vec::with_capacity(n_features * pcs); |
| 162 | for c in 0..pcs { |
| 163 | for r in 0..n_features { |
| 164 | all_eigs.push((evec[(r, c)] * eval[c]).abs()); |
| 165 | } |
| 166 | } |
| 167 | let mut repeated_imp = Vec::with_capacity(n_features * pcs); |
| 168 | for _ in 0..pcs { |
| 169 | repeated_imp.extend_from_slice(feature_importance_mean); |
| 170 | } |
| 171 | |
| 172 | let pearson = pearson_corr(&repeated_imp, &all_eigs); |
| 173 | let spearman = spearman_corr(&repeated_imp, &all_eigs); |
| 174 | let kendall = kendall_tau(&repeated_imp, &all_eigs); |
| 175 | |
| 176 | let mut pca_strength = vec![0.0; n_features]; |
| 177 | for r in 0..n_features { |
| 178 | let mut s = 0.0; |
| 179 | for c in 0..pcs { |
| 180 | s += (evec[(r, c)] * eval[c]).abs(); |
| 181 | } |
| 182 | pca_strength[r] = s; |
| 183 | } |
| 184 | let pca_rank = rank_desc(&pca_strength); |
| 185 | let inv_rank: Vec<f64> = pca_rank.iter().map(|r| 1.0 / *r as f64).collect(); |
| 186 | let weighted = weighted_kendall_tau(feature_importance_mean, &inv_rank); |
| 187 | |
| 188 | Ok(PcaCorrelation { pearson, spearman, kendall, weighted_kendall_rank: weighted }) |
| 189 | } |
| 190 | |
| 191 | pub fn plot_feature_importance( |
| 192 | importance: &BTreeMap<String, ImportanceStats>, |