| 19 | } |
| 20 | |
| 21 | pub fn mean_decrease_impurity( |
| 22 | per_tree_importances: &[Vec<f64>], |
| 23 | feature_names: &[String], |
| 24 | ) -> Result<BTreeMap<String, ImportanceStats>, String> { |
| 25 | if per_tree_importances.is_empty() { |
| 26 | return Err("per_tree_importances cannot be empty".to_string()); |
| 27 | } |
| 28 | let n_features = feature_names.len(); |
| 29 | if n_features == 0 { |
| 30 | return Err("feature_names cannot be empty".to_string()); |
| 31 | } |
| 32 | if per_tree_importances.iter().any(|r| r.len() != n_features) { |
| 33 | return Err("importance row length mismatch".to_string()); |
| 34 | } |
| 35 | |
| 36 | let mut means = vec![0.0; n_features]; |
| 37 | let mut stderrs = vec![0.0; n_features]; |
| 38 | for j in 0..n_features { |
| 39 | let col: Vec<f64> = per_tree_importances |
| 40 | .iter() |
| 41 | .map(|r| if r[j] == 0.0 { f64::NAN } else { r[j] }) |
| 42 | .collect(); |
| 43 | let (m, s) = nan_mean_std(&col); |
| 44 | means[j] = m; |
| 45 | stderrs[j] = s * (per_tree_importances.len() as f64).powf(-0.5); |
| 46 | } |
| 47 | |
| 48 | let denom: f64 = means.iter().filter(|v| v.is_finite()).sum(); |
| 49 | let mut out = BTreeMap::new(); |
| 50 | for (j, name) in feature_names.iter().enumerate() { |
| 51 | let mean = if denom > 0.0 && means[j].is_finite() { means[j] / denom } else { 0.0 }; |
| 52 | let std = if denom > 0.0 && stderrs[j].is_finite() { stderrs[j] / denom } else { 0.0 }; |
| 53 | out.insert(name.clone(), ImportanceStats { mean, std }); |
| 54 | } |
| 55 | Ok(out) |
| 56 | } |
| 57 | |
| 58 | pub fn mean_decrease_accuracy<C: SimpleClassifier>( |
| 59 | model: &mut C, |