pub fn feature_pca_analysis(
feature_rows: &[Vec<f64>],
feature_importance_mean: &[f64],
variance_thresh: f64,
) -> Result<PcaCorrelation, FeatureImportanceError>Expand description
Correlate an importance vector with PCA loadings (AFML §8.4.2, Snippet 8.6).
The PCA is that of get_orthogonal_features with variance_thresh. With k kept
components, the loadings |eigenvector_ij * eigenvalue_j| (all features, component by
component) are correlated with feature_importance_mean repeated k times, giving Pearson,
Spearman and Kendall tau-b as scipy.stats computes them (average ranks for the ties the
repetition creates). The weighted Kendall compares the importances with 1 / pca_rank, where
pca_rank ranks each feature’s summed absolute loading. Agreement between the supervised
and unsupervised rankings is weak evidence that the model has not simply overfit.
§Errors
FeatureImportanceError::Empty("feature_rows") iffeature_rowsis empty.FeatureImportanceError::LengthMismatch("feature_importance_mean") if the importance vector does not have one entry per column of the first row.FeatureImportanceError::RaggedFeatureRowsif the rows differ in length.FeatureImportanceError::Empty("feature columns") if the rows have no columns (andfeature_importance_meanis empty).
use openquant::feature_importance::feature_pca_analysis;
// Columns 0 and 1 are identical and orthogonal to column 2: eigenvalues 8, 4, 0.
let rows = vec![
vec![1.0, 1.0, 1.0],
vec![1.0, 1.0, -1.0],
vec![-1.0, -1.0, 1.0],
vec![-1.0, -1.0, -1.0],
];
// A 0.5 threshold keeps only the first component, loadings (8/sqrt 2, 8/sqrt 2, 0).
let corr = feature_pca_analysis(&rows, &[0.5, 0.4, 0.1], 0.5).unwrap();
assert!((corr.pearson - 0.970725).abs() < 1e-6);
assert!((corr.spearman - 0.75f64.sqrt()).abs() < 1e-12);
assert!((corr.kendall - 2.0 / 6f64.sqrt()).abs() < 1e-12);
assert!((corr.weighted_kendall_rank - 0.768706).abs() < 1e-6);