1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! # ModelAnalytics - compute_spearman_correlations_group Methods
//!
//! This module contains method implementations for `ModelAnalytics`.
//!
//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
use super::modelanalytics_type::ModelAnalytics;
use crate::analytics::{
CorrelationDirection, CorrelationInsight, CorrelationStrength, PropertyCorrelationMatrix,
};
use scirs2_core::ndarray_ext::Array1;
use std::collections::{HashMap, HashSet};
fn generate_correlation_interpretation(feat1: &str, feat2: &str, coef: f64) -> String {
let direction = if coef > 0.0 { "increases" } else { "decreases" };
let strength = if coef.abs() > 0.7 {
"strongly"
} else if coef.abs() > 0.5 {
"moderately"
} else {
"weakly"
};
format!(
"As {} increases, {} {} {}",
feat1, feat2, strength, direction
)
}
impl ModelAnalytics {
/// Compute Spearman rank correlations between model properties
///
/// Spearman correlation is a non-parametric measure of rank correlation.
/// It assesses monotonic relationships between variables without assuming linearity.
/// More robust to outliers than Pearson correlation.
///
/// # Returns
///
/// A correlation matrix with Spearman rank correlation coefficients and insights
///
/// # Example
///
/// ```rust,ignore
/// use oxirs_samm::analytics::ModelAnalytics;
/// use oxirs_samm::metamodel::Aspect;
///
/// # fn example(aspect: &Aspect) -> Result<(), Box<dyn std::error::Error>> {
/// let analytics = ModelAnalytics::from_aspect(aspect)?;
/// let spearman = analytics.compute_spearman_correlations();
///
/// println!("Spearman correlation matrix computed");
/// println!("Features analyzed: {:?}", spearman.feature_names);
/// for insight in &spearman.insights {
/// println!(" {} <-> {}: {:.3}", insight.feature1, insight.feature2, insight.coefficient);
/// }
/// # Ok(())
/// # }
/// ```
pub fn compute_spearman_correlations(&self) -> PropertyCorrelationMatrix {
use scirs2_stats::{CorrelationBuilder, CorrelationMethod};
let features = [
(
"property_count",
self.distributions.property_distribution.mean,
),
(
"structural_complexity",
self.complexity_assessment.structural,
),
("cognitive_complexity", self.complexity_assessment.cognitive),
("coupling", self.complexity_assessment.coupling * 100.0),
("quality_score", self.quality_score),
];
let n = features.len();
let mut matrix = vec![vec![0.0; n]; n];
let mut insights = Vec::new();
for i in 0..n {
for j in 0..n {
if i == j {
matrix[i][j] = 1.0;
continue;
}
if i > j {
matrix[i][j] = matrix[j][i];
continue;
}
let x = Array1::from_vec(vec![features[i].1]);
let y = Array1::from_vec(vec![features[j].1]);
let corr_result = CorrelationBuilder::new()
.method(CorrelationMethod::Spearman)
.compute(x.view(), y.view());
let coefficient: f64 = match corr_result {
Ok(result) => result.value.correlation,
Err(_) => 0.0,
};
matrix[i][j] = coefficient;
let abs_coef = coefficient.abs();
if abs_coef > 0.3 && i != j {
let strength = if abs_coef > 0.7 {
CorrelationStrength::Strong
} else if abs_coef > 0.5 {
CorrelationStrength::Moderate
} else {
CorrelationStrength::Weak
};
let direction = if coefficient > 0.0 {
CorrelationDirection::Positive
} else {
CorrelationDirection::Negative
};
insights.push(CorrelationInsight {
feature1: features[i].0.to_string(),
feature2: features[j].0.to_string(),
coefficient,
strength,
direction,
interpretation: generate_correlation_interpretation(
features[i].0,
features[j].0,
coefficient,
),
});
}
}
}
PropertyCorrelationMatrix {
feature_names: features.iter().map(|(name, _)| name.to_string()).collect(),
correlation_matrix: matrix,
insights,
method: "Spearman".to_string(),
}
}
}