1#[derive(Debug, Clone)]
10pub struct Series {
11 pub data: Vec<f64>,
13}
14
15impl Series {
16 pub fn new(data: Vec<f64>) -> Self {
27 Self { data }
28 }
29
30 pub fn len(&self) -> usize {
32 self.data.len()
33 }
34
35 pub fn is_empty(&self) -> bool {
37 self.data.is_empty()
38 }
39
40 pub fn min(&self) -> Option<f64> {
45 self.data
46 .iter()
47 .copied()
48 .filter(|v| v.is_finite())
49 .reduce(f64::min)
50 }
51
52 pub fn max(&self) -> Option<f64> {
57 self.data
58 .iter()
59 .copied()
60 .filter(|v| v.is_finite())
61 .reduce(f64::max)
62 }
63
64 pub fn bounds(&self) -> Option<(f64, f64)> {
67 Some((self.min()?, self.max()?))
68 }
69}
70
71pub trait IntoSeries {
82 fn into_series(self) -> Series;
84}
85
86impl IntoSeries for Vec<f64> {
89 fn into_series(self) -> Series {
91 Series::new(self)
92 }
93}
94
95impl IntoSeries for &[f64] {
96 fn into_series(self) -> Series {
98 Series::new(self.to_vec())
99 }
100}
101
102impl IntoSeries for &Vec<f64> {
103 fn into_series(self) -> Series {
105 Series::new(self.clone())
106 }
107}
108
109impl<const N: usize> IntoSeries for [f64; N] {
110 fn into_series(self) -> Series {
112 Series::new(self.to_vec())
113 }
114}
115
116impl<const N: usize> IntoSeries for &[f64; N] {
117 fn into_series(self) -> Series {
119 Series::new(self.to_vec())
120 }
121}
122
123impl IntoSeries for Series {
126 fn into_series(self) -> Series {
128 self
129 }
130}
131
132impl IntoSeries for std::ops::Range<i32> {
135 fn into_series(self) -> Series {
146 Series::new(self.map(|v| v as f64).collect())
147 }
148}
149
150impl IntoSeries for Vec<i32> {
153 fn into_series(self) -> Series {
155 Series::new(self.into_iter().map(|v| v as f64).collect())
156 }
157}
158
159impl IntoSeries for &[i32] {
160 fn into_series(self) -> Series {
162 Series::new(self.iter().map(|&v| v as f64).collect())
163 }
164}
165
166impl IntoSeries for Vec<f32> {
169 fn into_series(self) -> Series {
171 Series::new(self.into_iter().map(|v| v as f64).collect())
172 }
173}
174
175impl IntoSeries for &[f32] {
176 fn into_series(self) -> Series {
178 Series::new(self.iter().map(|&v| v as f64).collect())
179 }
180}
181
182#[derive(Debug, Clone)]
192pub struct Categories {
193 pub labels: Vec<String>,
195}
196
197impl Categories {
198 pub fn new(labels: Vec<String>) -> Self {
200 Self { labels }
201 }
202
203 pub fn len(&self) -> usize {
205 self.labels.len()
206 }
207
208 pub fn is_empty(&self) -> bool {
210 self.labels.is_empty()
211 }
212}
213
214pub trait IntoCategories {
220 fn into_categories(self) -> Categories;
222}
223
224impl IntoCategories for &[&str] {
225 fn into_categories(self) -> Categories {
227 Categories::new(self.iter().map(|s| (*s).to_owned()).collect())
228 }
229}
230
231impl IntoCategories for Vec<String> {
232 fn into_categories(self) -> Categories {
234 Categories::new(self)
235 }
236}
237
238impl IntoCategories for &[String] {
239 fn into_categories(self) -> Categories {
241 Categories::new(self.to_vec())
242 }
243}
244
245impl IntoCategories for Vec<&str> {
246 fn into_categories(self) -> Categories {
248 Categories::new(self.into_iter().map(|s| s.to_owned()).collect())
249 }
250}
251
252#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn series_from_vec_f64() {
262 let s = vec![1.0, 2.0, 3.0].into_series();
263 assert_eq!(s.len(), 3);
264 assert_eq!(s.data, vec![1.0, 2.0, 3.0]);
265 }
266
267 #[test]
268 fn series_from_slice_f64() {
269 let data: &[f64] = &[4.0, 5.0];
270 let s = data.into_series();
271 assert_eq!(s.data, vec![4.0, 5.0]);
272 }
273
274 #[test]
275 fn series_from_vec_ref() {
276 let v = vec![1.0, 2.0];
277 let s = (&v).into_series();
278 assert_eq!(s.data, vec![1.0, 2.0]);
279 }
280
281 #[test]
282 fn series_from_array() {
283 let s = [10.0, 20.0, 30.0].into_series();
284 assert_eq!(s.data, vec![10.0, 20.0, 30.0]);
285 }
286
287 #[test]
288 fn series_from_array_ref() {
289 let arr = [7.0, 8.0];
290 let s = (&arr).into_series();
291 assert_eq!(s.data, vec![7.0, 8.0]);
292 }
293
294 #[test]
295 fn series_identity() {
296 let original = Series::new(vec![1.0]);
297 let s = original.into_series();
298 assert_eq!(s.data, vec![1.0]);
299 }
300
301 #[test]
302 fn series_from_range() {
303 let s = (0..4).into_series();
304 assert_eq!(s.data, vec![0.0, 1.0, 2.0, 3.0]);
305 }
306
307 #[test]
308 fn series_from_vec_i32() {
309 let s = vec![1i32, 2, 3].into_series();
310 assert_eq!(s.data, vec![1.0, 2.0, 3.0]);
311 }
312
313 #[test]
314 fn series_from_slice_i32() {
315 let data: &[i32] = &[10, 20];
316 let s = data.into_series();
317 assert_eq!(s.data, vec![10.0, 20.0]);
318 }
319
320 #[test]
321 fn series_from_vec_f32() {
322 let s = vec![1.5f32, 2.5].into_series();
323 assert_eq!(s.data, vec![1.5f64, 2.5]);
324 }
325
326 #[test]
327 fn series_from_slice_f32() {
328 let data: &[f32] = &[0.1, 0.2];
329 let s = data.into_series();
330 assert_eq!(s.len(), 2);
331 }
332
333 #[test]
334 fn series_empty() {
335 let s = Series::new(vec![]);
336 assert!(s.is_empty());
337 assert_eq!(s.min(), None);
338 assert_eq!(s.max(), None);
339 assert_eq!(s.bounds(), None);
340 }
341
342 #[test]
343 fn series_min_max_bounds() {
344 let s = vec![3.0, 1.0, 4.0, 1.5, 9.0].into_series();
345 assert_eq!(s.min(), Some(1.0));
346 assert_eq!(s.max(), Some(9.0));
347 assert_eq!(s.bounds(), Some((1.0, 9.0)));
348 }
349
350 #[test]
351 fn series_min_max_ignores_nan() {
352 let s = vec![f64::NAN, 2.0, f64::INFINITY, 1.0, f64::NEG_INFINITY].into_series();
353 assert_eq!(s.min(), Some(1.0));
354 assert_eq!(s.max(), Some(2.0));
355 }
356
357 #[test]
358 fn categories_from_str_slice() {
359 let cats: &[&str] = &["a", "b", "c"];
360 let c = cats.into_categories();
361 assert_eq!(c.labels, vec!["a", "b", "c"]);
362 }
363
364 #[test]
365 fn categories_from_vec_string() {
366 let c = vec!["x".to_string(), "y".to_string()].into_categories();
367 assert_eq!(c.labels, vec!["x", "y"]);
368 }
369
370 #[test]
371 fn categories_from_string_slice() {
372 let v = vec!["p".to_string(), "q".to_string()];
373 let c = v.as_slice().into_categories();
374 assert_eq!(c.labels, vec!["p", "q"]);
375 }
376
377 #[test]
378 fn categories_from_vec_str_ref() {
379 let c = vec!["foo", "bar"].into_categories();
380 assert_eq!(c.labels, vec!["foo", "bar"]);
381 assert_eq!(c.len(), 2);
382 assert!(!c.is_empty());
383 }
384}