Skip to main content

oxirs_samm/analytics/
modelanalytics_compute_statistical_metrics_group.rs

1//! # ModelAnalytics - compute_statistical_metrics_group Methods
2//!
3//! This module contains method implementations for `ModelAnalytics`.
4//!
5//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
6
7use 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    /// Compute advanced statistical metrics for model properties
15    ///
16    /// Uses scirs2-stats to provide comprehensive statistical analysis including
17    /// dispersion measures, shape statistics, and robustness metrics.
18    ///
19    /// # Returns
20    ///
21    /// StatisticalMetrics containing advanced statistics about the model
22    ///
23    /// # Example
24    ///
25    /// ```rust,ignore
26    /// use oxirs_samm::analytics::ModelAnalytics;
27    /// use oxirs_samm::metamodel::Aspect;
28    ///
29    /// let aspect = Aspect::new("urn:samm:org.example:1.0.0#MyAspect".to_string());
30    /// let analytics = ModelAnalytics::analyze(&aspect);
31    /// let stats = analytics.compute_statistical_metrics();
32    ///
33    /// println!("Coefficient of Variation: {:.2}%", stats.coefficient_variation * 100.0);
34    /// println!("Median Absolute Deviation: {:.2}", stats.median_abs_deviation);
35    /// ```
36    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    /// Detect statistical anomalies using robust methods
82    ///
83    /// Uses median absolute deviation (MAD) for robust outlier detection,
84    /// which is less sensitive to outliers than standard deviation.
85    ///
86    /// # Returns
87    ///
88    /// Vector of StatisticalAnomaly indicating unusual patterns
89    ///
90    /// # Example
91    ///
92    /// ```rust,ignore
93    /// use oxirs_samm::analytics::ModelAnalytics;
94    ///
95    /// let analytics = ModelAnalytics::analyze(&aspect);
96    /// let anomalies = analytics.detect_statistical_anomalies();
97    ///
98    /// for anomaly in anomalies {
99    ///     println!("âš  {}: {} (score: {:.2})",
100    ///              anomaly.metric_name, anomaly.description, anomaly.deviation_score);
101    /// }
102    /// ```
103    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    /// Assess model quality using statistical hypothesis testing
157    ///
158    /// Applies statistical tests to determine if the model meets quality thresholds.
159    /// Uses robust statistical methods from scirs2-stats.
160    ///
161    /// # Returns
162    ///
163    /// QualityTest results with statistical confidence levels
164    ///
165    /// # Example
166    ///
167    /// ```rust,ignore
168    /// use oxirs_samm::analytics::ModelAnalytics;
169    ///
170    /// let analytics = ModelAnalytics::analyze(&aspect);
171    /// let test = analytics.statistical_quality_test();
172    ///
173    /// if test.passes_threshold {
174    ///     println!("✓ Model meets quality standards (confidence: {:.1}%)",
175    ///              test.confidence_level * 100.0);
176    /// }
177    /// ```
178    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}