oxihuman_core/
data_table.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct DataTable {
9 columns: Vec<String>,
10 rows: Vec<Vec<f64>>,
11}
12
13#[allow(dead_code)]
14impl DataTable {
15 pub fn new(columns: &[&str]) -> Self {
16 Self {
17 columns: columns.iter().map(|s| s.to_string()).collect(),
18 rows: Vec::new(),
19 }
20 }
21
22 pub fn add_row(&mut self, values: &[f64]) -> bool {
23 if values.len() != self.columns.len() {
24 return false;
25 }
26 self.rows.push(values.to_vec());
27 true
28 }
29
30 pub fn get(&self, row: usize, col: usize) -> Option<f64> {
31 self.rows.get(row).and_then(|r| r.get(col).copied())
32 }
33
34 pub fn col_index(&self, name: &str) -> Option<usize> {
35 self.columns.iter().position(|c| c == name)
36 }
37
38 pub fn get_by_name(&self, row: usize, col_name: &str) -> Option<f64> {
39 let col = self.col_index(col_name)?;
40 self.get(row, col)
41 }
42
43 pub fn num_rows(&self) -> usize {
44 self.rows.len()
45 }
46
47 pub fn num_cols(&self) -> usize {
48 self.columns.len()
49 }
50
51 pub fn column_sum(&self, col: usize) -> f64 {
52 self.rows.iter().filter_map(|r| r.get(col)).sum()
53 }
54
55 pub fn column_avg(&self, col: usize) -> f64 {
56 if self.rows.is_empty() {
57 return 0.0;
58 }
59 self.column_sum(col) / self.rows.len() as f64
60 }
61
62 pub fn column_names(&self) -> &[String] {
63 &self.columns
64 }
65
66 pub fn is_empty(&self) -> bool {
67 self.rows.is_empty()
68 }
69
70 pub fn clear(&mut self) {
71 self.rows.clear();
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_new() {
81 let dt = DataTable::new(&["x", "y"]);
82 assert_eq!(dt.num_cols(), 2);
83 assert!(dt.is_empty());
84 }
85
86 #[test]
87 fn test_add_row() {
88 let mut dt = DataTable::new(&["a", "b"]);
89 assert!(dt.add_row(&[1.0, 2.0]));
90 assert_eq!(dt.num_rows(), 1);
91 }
92
93 #[test]
94 fn test_add_row_wrong_size() {
95 let mut dt = DataTable::new(&["a", "b"]);
96 assert!(!dt.add_row(&[1.0]));
97 }
98
99 #[test]
100 fn test_get() {
101 let mut dt = DataTable::new(&["x"]);
102 dt.add_row(&[42.0]);
103 assert_eq!(dt.get(0, 0), Some(42.0));
104 assert_eq!(dt.get(1, 0), None);
105 }
106
107 #[test]
108 fn test_get_by_name() {
109 let mut dt = DataTable::new(&["x", "y"]);
110 dt.add_row(&[1.0, 2.0]);
111 assert_eq!(dt.get_by_name(0, "y"), Some(2.0));
112 assert_eq!(dt.get_by_name(0, "z"), None);
113 }
114
115 #[test]
116 fn test_column_sum() {
117 let mut dt = DataTable::new(&["val"]);
118 dt.add_row(&[1.0]);
119 dt.add_row(&[2.0]);
120 dt.add_row(&[3.0]);
121 assert!((dt.column_sum(0) - 6.0).abs() < 1e-12);
122 }
123
124 #[test]
125 fn test_column_avg() {
126 let mut dt = DataTable::new(&["val"]);
127 dt.add_row(&[2.0]);
128 dt.add_row(&[4.0]);
129 assert!((dt.column_avg(0) - 3.0).abs() < 1e-12);
130 }
131
132 #[test]
133 fn test_column_avg_empty() {
134 let dt = DataTable::new(&["val"]);
135 assert!((dt.column_avg(0)).abs() < 1e-12);
136 }
137
138 #[test]
139 fn test_clear() {
140 let mut dt = DataTable::new(&["a"]);
141 dt.add_row(&[1.0]);
142 dt.clear();
143 assert!(dt.is_empty());
144 }
145
146 #[test]
147 fn test_col_index() {
148 let dt = DataTable::new(&["alpha", "beta", "gamma"]);
149 assert_eq!(dt.col_index("beta"), Some(1));
150 assert_eq!(dt.col_index("delta"), None);
151 }
152}