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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! # ModelAnalytics - compute_statistical_metrics_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::{QualityTest, Severity, StatisticalAnomaly, StatisticalMetrics};
use scirs2_core::ndarray_ext::Array1;
use scirs2_stats::{iqr, kurtosis, mean_abs_deviation, median_abs_deviation, skew};
use std::collections::{HashMap, HashSet};
impl ModelAnalytics {
/// Compute advanced statistical metrics for model properties
///
/// Uses scirs2-stats to provide comprehensive statistical analysis including
/// dispersion measures, shape statistics, and robustness metrics.
///
/// # Returns
///
/// StatisticalMetrics containing advanced statistics about the model
///
/// # Example
///
/// ```rust,ignore
/// use oxirs_samm::analytics::ModelAnalytics;
/// use oxirs_samm::metamodel::Aspect;
///
/// let aspect = Aspect::new("urn:samm:org.example:1.0.0#MyAspect".to_string());
/// let analytics = ModelAnalytics::analyze(&aspect);
/// let stats = analytics.compute_statistical_metrics();
///
/// println!("Coefficient of Variation: {:.2}%", stats.coefficient_variation * 100.0);
/// println!("Median Absolute Deviation: {:.2}", stats.median_abs_deviation);
/// ```
pub fn compute_statistical_metrics(&self) -> StatisticalMetrics {
let prop_count = self.distributions.property_distribution.mean;
let data = Array1::from_vec(vec![
prop_count,
self.complexity_assessment.structural,
self.complexity_assessment.cognitive,
self.complexity_assessment.coupling * 100.0,
]);
let n = data.len() as f64;
let sum: f64 = data.iter().sum();
let mean_value = sum / n;
let mut sorted_data = data.to_vec();
sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let median_value = if sorted_data.is_empty() {
0.0
} else {
sorted_data[sorted_data.len() / 2]
};
let sq_diff_sum: f64 = data.iter().map(|x| (x - mean_value).powi(2)).sum();
let var_value = sq_diff_sum / (n - 1.0);
let std_value = var_value.sqrt();
let data_view = data.view();
let mad = mean_abs_deviation(&data_view, None).unwrap_or(0.0);
let median_ad = median_abs_deviation(&data_view, None, Some(1.4826)).unwrap_or(0.0);
let iqr_value = iqr(&data_view, None).unwrap_or(0.0);
let cv = if mean_value.abs() > 0.001 {
std_value / mean_value.abs()
} else {
0.0
};
let skewness = skew(&data_view, false, None).unwrap_or(0.0);
let kurt = kurtosis(&data_view, true, false, None).unwrap_or(0.0);
StatisticalMetrics {
mean: mean_value,
median: median_value,
std_dev: std_value,
variance: var_value,
mean_abs_deviation: mad,
median_abs_deviation: median_ad,
interquartile_range: iqr_value,
coefficient_variation: cv,
skewness,
kurtosis: kurt,
}
}
/// Detect statistical anomalies using robust methods
///
/// Uses median absolute deviation (MAD) for robust outlier detection,
/// which is less sensitive to outliers than standard deviation.
///
/// # Returns
///
/// Vector of StatisticalAnomaly indicating unusual patterns
///
/// # Example
///
/// ```rust,ignore
/// use oxirs_samm::analytics::ModelAnalytics;
///
/// let analytics = ModelAnalytics::analyze(&aspect);
/// let anomalies = analytics.detect_statistical_anomalies();
///
/// for anomaly in anomalies {
/// println!("âš {}: {} (score: {:.2})",
/// anomaly.metric_name, anomaly.description, anomaly.deviation_score);
/// }
/// ```
pub fn detect_statistical_anomalies(&self) -> Vec<StatisticalAnomaly> {
let mut anomalies = Vec::new();
let stats = self.compute_statistical_metrics();
if stats.coefficient_variation > 1.0 {
anomalies.push(StatisticalAnomaly {
metric_name: "Coefficient of Variation".to_string(),
description: format!(
"High variability detected: {:.1}% (threshold: 100%)",
stats.coefficient_variation * 100.0
),
deviation_score: stats.coefficient_variation,
severity: if stats.coefficient_variation > 2.0 {
Severity::Error
} else {
Severity::Warning
},
});
}
if stats.skewness.abs() > 2.0 {
anomalies.push(StatisticalAnomaly {
metric_name: "Skewness".to_string(),
description: format!(
"Highly skewed distribution: {:.2} (threshold: ±2.0)",
stats.skewness
),
deviation_score: stats.skewness.abs(),
severity: Severity::Info,
});
}
if stats.kurtosis.abs() > 3.0 {
anomalies.push(StatisticalAnomaly {
metric_name: "Kurtosis".to_string(),
description: format!(
"Heavy-tailed distribution: {:.2} (threshold: ±3.0)",
stats.kurtosis
),
deviation_score: stats.kurtosis.abs(),
severity: Severity::Info,
});
}
if stats.median_abs_deviation > stats.median * 0.5 {
anomalies.push(StatisticalAnomaly {
metric_name: "Median Absolute Deviation".to_string(),
description: format!(
"High spread around median: {:.2} (>50% of median)",
stats.median_abs_deviation
),
deviation_score: stats.median_abs_deviation / stats.median.max(1.0),
severity: Severity::Warning,
});
}
anomalies
}
/// Assess model quality using statistical hypothesis testing
///
/// Applies statistical tests to determine if the model meets quality thresholds.
/// Uses robust statistical methods from scirs2-stats.
///
/// # Returns
///
/// QualityTest results with statistical confidence levels
///
/// # Example
///
/// ```rust,ignore
/// use oxirs_samm::analytics::ModelAnalytics;
///
/// let analytics = ModelAnalytics::analyze(&aspect);
/// let test = analytics.statistical_quality_test();
///
/// if test.passes_threshold {
/// println!("✓ Model meets quality standards (confidence: {:.1}%)",
/// test.confidence_level * 100.0);
/// }
/// ```
pub fn statistical_quality_test(&self) -> QualityTest {
let stats = self.compute_statistical_metrics();
let cv_ok = stats.coefficient_variation < 0.8;
let skew_ok = stats.skewness.abs() < 1.5;
let score_ok = self.quality_score > stats.median;
let tests_passed = [cv_ok, skew_ok, score_ok].iter().filter(|&&x| x).count();
let confidence = tests_passed as f64 / 3.0;
QualityTest {
passes_threshold: tests_passed >= 2,
confidence_level: confidence,
cv_check: cv_ok,
skewness_check: skew_ok,
score_check: score_ok,
details: format!(
"Passed {}/3 tests: CV={:.1}%, Skewness={:.2}, Score={:.1}",
tests_passed,
stats.coefficient_variation * 100.0,
stats.skewness,
self.quality_score
),
}
}
}