oxirs_samm/analytics/
modelanalytics_compute_statistical_metrics_group.rs1use super::modelanalytics_type::ModelAnalytics;
8use crate::analytics::{QualityTest, Severity, StatisticalAnomaly, StatisticalMetrics};
9use scirs2_core::ndarray_ext::Array1;
10use scirs2_stats::{iqr, kurtosis, mean_abs_deviation, median_abs_deviation, skew};
11use std::collections::{HashMap, HashSet};
12
13impl ModelAnalytics {
14 pub fn compute_statistical_metrics(&self) -> StatisticalMetrics {
37 let prop_count = self.distributions.property_distribution.mean;
38 let data = Array1::from_vec(vec![
39 prop_count,
40 self.complexity_assessment.structural,
41 self.complexity_assessment.cognitive,
42 self.complexity_assessment.coupling * 100.0,
43 ]);
44 let n = data.len() as f64;
45 let sum: f64 = data.iter().sum();
46 let mean_value = sum / n;
47 let mut sorted_data = data.to_vec();
48 sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
49 let median_value = if sorted_data.is_empty() {
50 0.0
51 } else {
52 sorted_data[sorted_data.len() / 2]
53 };
54 let sq_diff_sum: f64 = data.iter().map(|x| (x - mean_value).powi(2)).sum();
55 let var_value = sq_diff_sum / (n - 1.0);
56 let std_value = var_value.sqrt();
57 let data_view = data.view();
58 let mad = mean_abs_deviation(&data_view, None).unwrap_or(0.0);
59 let median_ad = median_abs_deviation(&data_view, None, Some(1.4826)).unwrap_or(0.0);
60 let iqr_value = iqr(&data_view, None).unwrap_or(0.0);
61 let cv = if mean_value.abs() > 0.001 {
62 std_value / mean_value.abs()
63 } else {
64 0.0
65 };
66 let skewness = skew(&data_view, false, None).unwrap_or(0.0);
67 let kurt = kurtosis(&data_view, true, false, None).unwrap_or(0.0);
68 StatisticalMetrics {
69 mean: mean_value,
70 median: median_value,
71 std_dev: std_value,
72 variance: var_value,
73 mean_abs_deviation: mad,
74 median_abs_deviation: median_ad,
75 interquartile_range: iqr_value,
76 coefficient_variation: cv,
77 skewness,
78 kurtosis: kurt,
79 }
80 }
81 pub fn detect_statistical_anomalies(&self) -> Vec<StatisticalAnomaly> {
104 let mut anomalies = Vec::new();
105 let stats = self.compute_statistical_metrics();
106 if stats.coefficient_variation > 1.0 {
107 anomalies.push(StatisticalAnomaly {
108 metric_name: "Coefficient of Variation".to_string(),
109 description: format!(
110 "High variability detected: {:.1}% (threshold: 100%)",
111 stats.coefficient_variation * 100.0
112 ),
113 deviation_score: stats.coefficient_variation,
114 severity: if stats.coefficient_variation > 2.0 {
115 Severity::Error
116 } else {
117 Severity::Warning
118 },
119 });
120 }
121 if stats.skewness.abs() > 2.0 {
122 anomalies.push(StatisticalAnomaly {
123 metric_name: "Skewness".to_string(),
124 description: format!(
125 "Highly skewed distribution: {:.2} (threshold: ±2.0)",
126 stats.skewness
127 ),
128 deviation_score: stats.skewness.abs(),
129 severity: Severity::Info,
130 });
131 }
132 if stats.kurtosis.abs() > 3.0 {
133 anomalies.push(StatisticalAnomaly {
134 metric_name: "Kurtosis".to_string(),
135 description: format!(
136 "Heavy-tailed distribution: {:.2} (threshold: ±3.0)",
137 stats.kurtosis
138 ),
139 deviation_score: stats.kurtosis.abs(),
140 severity: Severity::Info,
141 });
142 }
143 if stats.median_abs_deviation > stats.median * 0.5 {
144 anomalies.push(StatisticalAnomaly {
145 metric_name: "Median Absolute Deviation".to_string(),
146 description: format!(
147 "High spread around median: {:.2} (>50% of median)",
148 stats.median_abs_deviation
149 ),
150 deviation_score: stats.median_abs_deviation / stats.median.max(1.0),
151 severity: Severity::Warning,
152 });
153 }
154 anomalies
155 }
156 pub fn statistical_quality_test(&self) -> QualityTest {
179 let stats = self.compute_statistical_metrics();
180 let cv_ok = stats.coefficient_variation < 0.8;
181 let skew_ok = stats.skewness.abs() < 1.5;
182 let score_ok = self.quality_score > stats.median;
183 let tests_passed = [cv_ok, skew_ok, score_ok].iter().filter(|&&x| x).count();
184 let confidence = tests_passed as f64 / 3.0;
185 QualityTest {
186 passes_threshold: tests_passed >= 2,
187 confidence_level: confidence,
188 cv_check: cv_ok,
189 skewness_check: skew_ok,
190 score_check: score_ok,
191 details: format!(
192 "Passed {}/3 tests: CV={:.1}%, Skewness={:.2}, Score={:.1}",
193 tests_passed,
194 stats.coefficient_variation * 100.0,
195 stats.skewness,
196 self.quality_score
197 ),
198 }
199 }
200}