entrenar/quality/failure/
analysis.rs1use std::collections::HashMap;
4
5use super::types::{FailureCategory, FailureContext};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ParetoAnalysis {
10 pub categories: Vec<(FailureCategory, u32)>,
12
13 pub total_failures: u32,
15}
16
17impl ParetoAnalysis {
18 pub fn from_failures(failures: &[FailureContext]) -> Self {
20 let mut counts: HashMap<FailureCategory, u32> = HashMap::new();
21
22 for failure in failures {
23 *counts.entry(failure.category).or_insert(0) += 1;
24 }
25
26 let mut categories: Vec<(FailureCategory, u32)> = counts.into_iter().collect();
27 categories.sort_by(|a, b| b.1.cmp(&a.1)); Self { categories, total_failures: failures.len() as u32 }
30 }
31
32 pub fn top_categories(&self, n: usize) -> Vec<(FailureCategory, u32)> {
34 self.categories.iter().take(n).copied().collect()
35 }
36
37 pub fn percentages(&self) -> Vec<(FailureCategory, f64)> {
39 if self.total_failures == 0 {
40 return Vec::new();
41 }
42
43 let total = f64::from(self.total_failures);
44 self.categories
45 .iter()
46 .map(|(cat, count)| (*cat, (f64::from(*count) / total) * 100.0))
47 .collect()
48 }
49
50 pub fn cumulative_percentages(&self) -> Vec<(FailureCategory, f64)> {
52 if self.total_failures == 0 {
53 return Vec::new();
54 }
55
56 let total = f64::from(self.total_failures);
57 let mut cumulative = 0.0;
58 self.categories
59 .iter()
60 .map(|(cat, count)| {
61 cumulative += (f64::from(*count) / total) * 100.0;
62 (*cat, cumulative)
63 })
64 .collect()
65 }
66
67 pub fn vital_few(&self) -> Vec<(FailureCategory, u32)> {
69 let mut cumulative = 0u32;
70 let threshold = (f64::from(self.total_failures) * 0.8) as u32;
71
72 self.categories
73 .iter()
74 .take_while(|(_, count)| {
75 let result = cumulative < threshold;
76 cumulative += count;
77 result
78 })
79 .copied()
80 .collect()
81 }
82}
83
84pub fn top_failure_categories(failures: &[FailureContext]) -> Vec<(FailureCategory, u32)> {
86 ParetoAnalysis::from_failures(failures).categories
87}