blackjack/series/mod.rs
1//! Series represents a single column within a `DataFrame`
2//!
3//! ## Example use:
4//!
5//! ```
6//! use blackjack::prelude::*;
7//!
8//! let mut series = Series::arange(0, 5);
9//!
10//! // Index and change elements
11//! series[0] = 1;
12//! series[1] = 0;
13//!
14//! assert_eq!(series[0], 1);
15//! assert_eq!(series.sum(), 10);
16//! assert_eq!(series.len(), 5);
17//! ```
18
19use std::convert::From;
20use std::fmt;
21use std::iter::{FromIterator, Sum};
22use std::marker::{Send, Sync};
23use std::ops::{Index, IndexMut, Range};
24use std::str::FromStr;
25use std::vec::IntoIter;
26
27use serde::{Deserialize, Serialize};
28use itertools::Itertools;
29
30use num::*;
31use rayon::prelude::*;
32use stats;
33
34pub mod overloaders;
35pub mod rolling;
36pub mod series_groupby;
37pub mod variants;
38
39pub use self::rolling::*;
40pub use self::series_groupby::*;
41pub use self::variants::*;
42
43use crate::funcs;
44use crate::prelude::*;
45
46// Allow series.into_iter()
47impl_series_into_iter!(String);
48impl_series_into_iter!(f64);
49impl_series_into_iter!(i64);
50impl_series_into_iter!(f32);
51impl_series_into_iter!(i32);
52
53/// Series struct for containing underlying Array and other meta data.
54#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, PartialOrd)]
55pub struct Series<T>
56where
57 T: BlackJackData,
58{
59 /// Name of the series, if added to a dataframe without a name, it will be assigned
60 /// a default name equalling the count of columns in the dataframe.
61 pub name: Option<String>,
62
63 /// The underlying values of the Series
64 pub values: Vec<T>,
65
66 dtype: Option<DType>,
67}
68
69impl<I> Default for Series<I>
70where
71 I: PartialOrd + PartialEq + BlackJackData,
72{
73 fn default() -> Self {
74 Series::from_vec(vec![])
75 }
76}
77
78/// Constructor methods for `Series<T>`
79impl<T> Series<T>
80where
81 T: BlackJackData,
82{
83 /// Create a new Series struct from an integer range with one step increments.
84 ///
85 /// ## Example
86 /// ```
87 /// use blackjack::prelude::*;
88 ///
89 /// let series: Series<i32> = Series::arange(0, 10);
90 /// ```
91 pub fn arange(start: T, stop: T) -> Self
92 where
93 T: Integer + BlackJackData + ToPrimitive,
94 Range<T>: Iterator,
95 Vec<T>: FromIterator<<Range<T> as Iterator>::Item>,
96 {
97 let dtype = Some(start.dtype());
98 let values: Vec<T> = (start..stop).collect();
99 Series {
100 name: None,
101 dtype,
102 values,
103 }
104 }
105
106 /// Drop positions of the Series
107 pub fn drop_positions<I>(&mut self, positions: I) -> ()
108 where
109 I: IntoIterator<Item = usize>,
110 {
111 // TODO: refactor to avoid duplicating data
112
113 let positions = positions.into_iter().collect::<Vec<usize>>();
114
115 // Create a collection of the new values and indexes
116 self.values = self
117 .values
118 .iter()
119 .enumerate()
120 .filter_map(|(position, val)| {
121 if positions.contains(&position) {
122 None
123 } else {
124 Some(val.clone())
125 }
126 })
127 .collect::<Vec<T>>();
128 }
129
130 /// Fetch values from the series by matching index _positions_, _not_ by index value.
131 ///
132 /// _No data copies are made_, and currently this is _not_ done in parallel. As by currently
133 /// single threaded exceeds parallel execution up to ~10m elements. As the _majority_ of use cases
134 /// have less than this amount, we've opted for single threading. If you need concurrent execution,
135 /// please file an issue at our github. :-)
136 ///
137 /// ## Example
138 /// ```
139 /// use blackjack::prelude::*;
140 ///
141 /// let mut series = Series::arange(0, 10000); // Index values end up being 0-10000 by default here
142 ///
143 /// let vals = series.iloc(&vec![250, 500, 1000, 2000, 4000, 5000]); // ~300ns, ~28x faster than Pandas
144 /// assert_eq!(vals, vec![&250, &500, &1000, &2000, &4000, &5000]);
145 /// ```
146 pub fn iloc<'b, I>(&self, idx_vals: I) -> Vec<&T>
147 where
148 I: IntoIterator<Item = &'b usize>,
149 {
150 idx_vals
151 .into_iter()
152 .map(|idx_val| &self.values[*idx_val])
153 .collect::<Vec<&T>>()
154 }
155
156 /// Calculate a predefined rolling aggregation
157 ///
158 /// See [`Rolling`] for additional functionality.
159 ///
160 /// ## Example
161 /// ```
162 /// use blackjack::prelude::*;
163 /// use float_cmp::ApproxEq;
164 ///
165 /// let series = Series::from_vec(vec![0, 1, 2, 3, 4, 5]);
166 ///
167 /// let rolled: Series<f64> = series.rolling(4).mean().unwrap();
168 /// assert_eq!(rolled.len(), 6);
169 ///
170 /// // vals in indexes 0 thru 2 should be NaN as they are inside the window
171 /// assert_eq!(rolled[0..2].iter().all(|v| v.is_nan()), true);
172 ///
173 /// assert_eq!(rolled[3], 1.5);
174 /// assert_eq!(rolled[4], 2.5);
175 /// assert_eq!(rolled[5], 3.5);
176 /// ```
177 pub fn rolling(&self, window: usize) -> Rolling<T>
178 where
179 T: Send + Sync,
180 {
181 Rolling::new(window, &self)
182 }
183
184 /// Return an iterable of booleans determining if any element is NaN
185 ///
186 /// ## Example
187 /// ```
188 /// use blackjack::prelude::*;
189 ///
190 /// let mut series = Series::from_vec(vec![0, 1, 2])
191 /// .astype::<f32>()
192 /// .unwrap();
193 ///
194 /// // No NaNs currently
195 /// assert_eq!(series.isna().collect::<Vec<bool>>(), vec![false, false, false]);
196 ///
197 /// // Insert a NaN at index zero
198 /// series[0] = num::Float::nan();
199 /// assert_eq!(series.isna().collect::<Vec<bool>>(), vec![true, false, false]);
200 /// ```
201 pub fn isna<'a>(&'a self) -> impl Iterator<Item = bool> + 'a
202 where
203 T: Float,
204 {
205 self.values.iter().map(|v| v.is_nan())
206 }
207
208 /// Determine if _all_ elements in the Series meet a given condition
209 ///
210 /// This will stop iteration after encountering the first element which breaks
211 /// the condition.
212 ///
213 /// ## Example
214 /// ```
215 /// use blackjack::prelude::*;
216 ///
217 /// let series = Series::from_vec(vec![1, 2, 3, 4]);
218 ///
219 /// assert_eq!(series.all(|x| *x > 0), true);
220 /// assert_eq!(series.all(|x| *x > 2), false);
221 /// ```
222 pub fn all<F>(&self, condition: F) -> bool
223 where
224 for<'r> F: Fn(&'r T) -> bool,
225 {
226 self.values.iter().all(condition)
227 }
228
229 /// Check if all elements with the Series are equal
230 ///
231 /// ## Example
232 /// ```
233 /// use blackjack::prelude::*;
234 ///
235 /// let series = Series::from_vec(vec![1, 1, 1, 1, 1]);
236 /// assert!(series.all_equal());
237 /// ```
238 pub fn all_equal(&self) -> bool
239 where
240 T: PartialEq,
241 {
242 self.values.iter().all_equal()
243 }
244
245 /// Determine if _any_ element in the Series meets a given condition
246 ///
247 /// This will stop iteration after encountering the first element which meets
248 /// conditions supplied.
249 pub fn any<F>(&self, condition: F) -> bool
250 where
251 for<'r> F: FnMut(&'r &T) -> bool,
252 {
253 let first_match = self.values.iter().find(condition);
254 match first_match {
255 Some(_) => true,
256 None => false,
257 }
258 }
259
260 /// Create a cartesian product of this series and another, returns a pair of
261 /// `Series` representing the cartesian product
262 ///
263 /// ## Example
264 /// ```
265 /// use blackjack::prelude::*;
266 ///
267 /// let series1 = Series::from_vec(vec![0, 1]);
268 /// let series2 = Series::from_vec(vec![1, 2]);
269 ///
270 /// let (cart_prod1, cart_prod2) = series1.cartesian_product(&series2);
271 ///
272 /// assert_eq!(cart_prod1.values, vec![0, 0, 1, 1]);
273 /// assert_eq!(cart_prod2.values, vec![1, 2, 1, 2]);
274 /// ```
275 pub fn cartesian_product<O>(&self, other: &Series<O>) -> (Series<T>, Series<O>)
276 where
277 O: BlackJackData,
278 {
279 let mut left = vec![];
280 let mut right = vec![];
281 let _ = self
282 .values
283 .clone()
284 .into_iter()
285 .cartesian_product(other.values.clone().into_iter())
286 .map(|(l, r)| {
287 left.push(l);
288 right.push(r);
289 })
290 .collect::<Vec<()>>();
291 (Series::from_vec(left), Series::from_vec(right))
292 }
293
294 /// Return the positions of where a given condition evaluates to `true`
295 ///
296 /// This is somewhat akin to the pandas `where` method.
297 ///
298 /// ## Example
299 /// ```
300 /// use blackjack::prelude::*;
301 ///
302 /// let series = Series::from_vec(vec![1, 2, 1, 2]);
303 ///
304 /// let indexes_of_ones = series.positions(|x| *x == 1).collect::<Vec<usize>>();
305 /// assert_eq!(indexes_of_ones, vec![0, 2]);
306 /// ```
307 pub fn positions<'a, F>(&'a self, condition: F) -> impl Iterator<Item = usize> + 'a
308 where
309 F: 'a + Fn(&T) -> bool,
310 {
311 self.values.iter().positions(condition)
312 }
313
314 /// Map a function over a series _in parallel_
315 /// Function takes some type `T` and returns some type `B` which
316 /// has `BlackJackData` implemented.
317 ///
318 /// ## Example
319 ///
320 /// ```
321 /// use blackjack::prelude::*;
322 ///
323 /// let series = Series::from_vec(vec![1, 1, 1, 1]);
324 ///
325 /// let new_series = series.map_par(|x| x * 2);
326 /// assert_eq!(new_series.sum(), 8);
327 /// ```
328 pub fn map_par<B, F>(self, func: F) -> Series<B>
329 where
330 B: BlackJackData,
331 F: Fn(T) -> B + Send + Sync,
332 {
333 let new_data = self.values.into_par_iter().map(func).collect();
334 Series::from_vec(new_data)
335 }
336
337 /// Map a function over a series in a single thread
338 /// Function takes some type `T` and returns some type `B` which
339 /// has `BlackJackData` implemented.
340 pub fn map<B, F>(self, func: F) -> Series<B>
341 where
342 B: BlackJackData,
343 F: Fn(T) -> B,
344 {
345 let new_data = self.values.into_iter().map(func).collect();
346 Series::from_vec(new_data)
347 }
348
349 /// Convert the series into another [`DType`] (creates a new series)
350 ///
351 /// ## Example
352 /// ```
353 /// use blackjack::prelude::*;
354 ///
355 /// let series: Series<i32> = Series::arange(0, 10);
356 /// assert_eq!(series[0].dtype(), DType::I32);
357 /// let new_series = series.astype::<f64>().unwrap();
358 /// assert_eq!(new_series[0].dtype(), DType::F64);
359 /// ```
360 pub fn astype<A>(&self) -> Result<Series<A>, &'static str>
361 where
362 A: BlackJackData + FromStr,
363 {
364 let values = self
365 .values
366 .clone()
367 .into_iter()
368 .map(|v| v.to_string())
369 .map(|v| v.parse::<A>().map_err(|_| "Cannot cast into type"))
370 .collect::<Result<Vec<A>, _>>()?;
371 let series = Series {
372 name: self.name.clone(),
373 dtype: Some(values[0].dtype()),
374 values,
375 };
376 Ok(series)
377 }
378
379 /// Convert this series into another [`DType`] (consumes current series)
380 ///
381 /// ## Example
382 /// ```
383 /// use blackjack::prelude::*;
384 ///
385 /// let series: Series<i32> = Series::arange(0, 10);
386 /// assert_eq!(series[0].dtype(), DType::I32);
387 /// let new_series = series.into_type::<f64>().unwrap();
388 /// assert_eq!(new_series[0].dtype(), DType::F64);
389 /// ```
390 pub fn into_type<A>(self) -> Result<Series<A>, &'static str>
391 where
392 A: BlackJackData + FromStr,
393 {
394 let values = self
395 .values
396 .into_iter()
397 .map(|v| v.to_string())
398 .map(|v| v.parse::<A>().map_err(|_| "Cannot cast into type"))
399 .collect::<Result<Vec<A>, _>>()?;
400 let series = Series {
401 name: self.name.clone(),
402 dtype: Some(values[0].dtype()),
403 values,
404 };
405 Ok(series)
406 }
407
408 /// Get a series of the unique elements held in this series
409 ///
410 /// ## Example
411 ///
412 /// ```
413 /// use blackjack::prelude::*;
414 ///
415 /// let series: Series<i32> = Series::from_vec(vec![1, 2, 1, 0, 1, 0, 1, 1]);
416 /// let unique: Series<i32> = series.unique();
417 /// assert_eq!(unique, Series::from_vec(vec![0, 1, 2]));
418 /// ```
419 pub fn unique(&self) -> Series<T>
420 where
421 T: PartialOrd + Copy,
422 {
423 // Cannot use `HashSet` as f32 & f64 don't implement Hash
424 let mut unique: Vec<T> = vec![];
425 let mut values = self.values.clone();
426 values.sort_by(|a, b| a.partial_cmp(b).unwrap());
427
428 for val in values {
429 if unique.len() > 0 {
430 if val == unique[unique.len() - 1] {
431 continue;
432 } else {
433 unique.push(val)
434 }
435 } else {
436 unique.push(val)
437 }
438 }
439
440 Series::from_vec(unique)
441 }
442
443 /// Create a new Series struct from a vector, where T is supported by [`BlackJackData`].
444 ///
445 /// ## Example
446 /// ```
447 /// use blackjack::prelude::*;
448 ///
449 /// let series: Series<i32> = Series::from_vec(vec![1, 2, 3]);
450 /// ```
451 pub fn from_vec(vec: Vec<T>) -> Self {
452 let dtype = if vec.len() == 0 {
453 None
454 } else {
455 Some(vec[0].dtype())
456 };
457 Series {
458 name: None,
459 dtype,
460 values: vec,
461 }
462 }
463
464 /// Convert the series to a [`Vec`]
465 ///
466 /// ## Example
467 /// ```
468 /// use blackjack::prelude::*;
469 ///
470 /// let series = Series::from_vec(vec![1_f64, 2_f64, 3_f64]);
471 ///
472 /// assert_eq!(
473 /// series.clone().into_vec(),
474 /// vec![1_f64, 2_f64, 3_f64]
475 /// );
476 /// ```
477 pub fn into_vec(self) -> Vec<T> {
478 self.values
479 }
480
481 /// Set the name of a series
482 pub fn set_name(&mut self, name: &str) -> () {
483 self.name = Some(name.to_string());
484 }
485
486 /// Get the name of the series; Series may not be assigned a string,
487 /// so an `Option` is returned.
488 ///
489 /// ## Example
490 /// ```
491 /// use blackjack::prelude::*;
492 ///
493 /// let mut series = Series::from_vec(vec![1, 2, 3]);
494 /// series.set_name("my-series");
495 ///
496 /// assert_eq!(series.name(), Some("my-series".to_string()));
497 /// ```
498 pub fn name(&self) -> Option<String> {
499 match self.name {
500 Some(ref name) => Some(name.clone()),
501 None => None,
502 }
503 }
504
505 /// Finds the returns a [`Series`] containing the mode(s) of the current
506 /// [`Series`]
507 pub fn mode(&self) -> Result<Self, BlackJackError>
508 where
509 T: BlackJackData + PartialOrd + Copy + ToPrimitive,
510 {
511 if self.len() == 0 {
512 return Err(BlackJackError::from(
513 "Cannot compute mode of an empty series!",
514 ));
515 }
516
517 let modes = stats::modes(self.values.iter().map(|v| *v));
518 let modes = Series::from_vec(modes);
519 Ok(modes)
520 }
521
522 /// Calculate the variance of the series, using either population or sample variance
523 /// > Population: `ddof` == 0_f64
524 /// > Sample: `ddof` == 1_f64
525 pub fn var(&self, ddof: f64) -> Result<f64, BlackJackError>
526 where
527 T: ToPrimitive + Num,
528 {
529 if self.len() == 0 {
530 return Err(BlackJackError::ValueError(
531 "Cannot compute variance of an empty series!".to_owned(),
532 ));
533 }
534 funcs::variance(self.values.as_slice(), ddof)
535 .ok_or_else(|| BlackJackError::from("Failed to calculate variance of series."))
536 }
537
538 /// Calculate the standard deviation of the series
539 ///
540 /// ## Example
541 /// ```
542 /// use blackjack::prelude::*;
543 /// use float_cmp::ApproxEq;
544 ///
545 /// let series = Series::arange(0, 10).astype::<f32>().unwrap();
546 ///
547 /// let std = series.std(1_f64).unwrap(); // using population ddof (sample variance == 0_f64)
548 /// assert_eq!(std, 3.0276503540974917);
549 /// ```
550 pub fn std(&self, ddof: f64) -> Result<f64, BlackJackError>
551 where
552 T: BlackJackData + ToPrimitive + Copy + Num,
553 {
554 if self.len() == 0 {
555 return Err(BlackJackError::ValueError(
556 "Cannot compute standard deviation of an empty series!".to_owned(),
557 ));
558 }
559 funcs::std(self.values.as_slice(), ddof)
560 .ok_or_else(|| BlackJackError::from("Failed to calculate stddev of series."))
561 }
562
563 /// Sum a given series, yielding the same type as the elements stored in the
564 /// series.
565 pub fn sum(&self) -> T
566 where
567 T: Num + Copy + Sum,
568 {
569 funcs::sum(self.values.as_slice())
570 }
571
572 /// Average / Mean of a given series - Requires specifying desired float
573 /// return annotation
574 ///
575 /// ## Example:
576 /// ```
577 /// use blackjack::prelude::*;
578 ///
579 /// let series = Series::arange(0, 5);
580 /// let mean = series.mean();
581 ///
582 /// match mean {
583 /// Ok(result) => {
584 /// println!("Result is: {}", &result);
585 /// assert_eq!(result, 2.0);
586 /// },
587 /// Err(err) => {
588 /// panic!("Was unable to compute mean, error: {}", err);
589 /// }
590 /// }
591 /// ```
592 pub fn mean(&self) -> Result<f64, BlackJackError>
593 where
594 T: ToPrimitive + Copy + Num + Sum,
595 {
596 funcs::mean(self.values.as_slice())
597 .ok_or_else(|| BlackJackError::from("Failed to calculate mean!"))
598 }
599
600 /// Calculate the quantile of the series
601 ///
602 /// ## Example:
603 /// ```
604 /// use blackjack::prelude::*;
605 ///
606 /// let series = Series::arange(0, 100).astype::<f32>().unwrap();
607 /// let qtl = series.quantile(0.5).unwrap(); // `49.5_f32`
608 ///
609 /// assert!(qtl < 49.51);
610 /// assert!(qtl > 49.49);
611 /// ```
612 pub fn quantile(&self, quantile: f64) -> Result<f64, BlackJackError>
613 where
614 T: ToPrimitive + BlackJackData,
615 {
616 use rgsl::statistics::quantile_from_sorted_data;
617 use std::cmp::Ordering;
618
619 let mut vec = self
620 .clone()
621 .into_vec()
622 .into_iter()
623 .map(|v| v.to_f64().unwrap())
624 .collect::<Vec<f64>>();
625
626 vec.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
627 let qtl = quantile_from_sorted_data(&vec[..], 1, vec.len(), quantile);
628 Ok(qtl)
629 }
630
631 /// Calculate the median of a series
632 pub fn median(&self) -> Result<f64, BlackJackError>
633 where
634 T: ToPrimitive + Copy + PartialOrd,
635 {
636 if self.len() == 0 {
637 return Err(BlackJackError::from(
638 "Cannot calculate median of an empty series.",
639 ));
640 }
641 stats::median(self.values.iter().map(|v| v.to_f64().unwrap())).ok_or_else(|| {
642 BlackJackError::from(
643 r#"Unable to calculate median, please create an issue!
644 as this wasn't expected to ever happen on a non-empty
645 series. :("#,
646 )
647 })
648 }
649
650 /// Find the minimum of the series. If several elements are equally minimum,
651 /// the first element is returned. If it's empty, an Error will be returned.
652 ///
653 /// ## Example
654 /// ```
655 /// use blackjack::prelude::*;
656 ///
657 /// let series: Series<i32> = Series::arange(10, 100);
658 ///
659 /// assert_eq!(series.min().unwrap(), 10);
660 /// ```
661 pub fn min(&self) -> Result<T, BlackJackError>
662 where
663 T: Num + PartialOrd + BlackJackData + Copy,
664 {
665 funcs::min(self.values.as_slice())
666 .map(|v| *v)
667 .ok_or_else(|| BlackJackError::from("Failed to calculate min of series."))
668 }
669
670 /// Exibits the same behavior and usage of [`Series::min`], only
671 /// yielding the [`Result`] of a maximum.
672 pub fn max(&self) -> Result<T, BlackJackError>
673 where
674 T: Num + PartialOrd + BlackJackData + Copy,
675 {
676 funcs::max(self.values.as_slice())
677 .map(|v| *v)
678 .ok_or_else(|| BlackJackError::from("Failed to calculate max of series."))
679 }
680
681 /// Determine the length of the Series
682 pub fn len(&self) -> usize {
683 self.values.len()
684 }
685
686 /// Determine if series is empty.
687 pub fn is_empty(&self) -> bool {
688 self.len() == 0
689 }
690
691 /// Get the dtype, returns `None` if series dtype is unknown.
692 /// in such a case, calling `.astype()` to coerce all types to a single
693 /// type is needed.
694 pub fn dtype(&self) -> Option<DType> {
695 self.dtype.clone()
696 }
697
698 /// Append a [`BlackJackData`] element to the Series
699 ///
700 /// ## Example
701 /// ```
702 /// use blackjack::prelude::*;
703 ///
704 /// let mut series = Series::from_vec(vec![0, 1, 2]);
705 /// assert_eq!(series.len(), 3);
706 ///
707 /// series.append(3);
708 /// assert_eq!(series.len(), 4);
709 /// ```
710 pub fn append<V: Into<T>>(&mut self, val: V) -> () {
711 let v = val.into();
712 self.values.push(v);
713 }
714
715 /// As boxed pointer, recoverable by `Box::from_raw(ptr)` or
716 /// `Series::from_raw(*mut Self)`
717 pub fn into_raw(self) -> *mut Self {
718 Box::into_raw(Box::new(self))
719 }
720
721 /// Create from raw pointer
722 pub fn from_raw(ptr: *mut Self) -> Self {
723 unsafe { *Box::from_raw(ptr) }
724 }
725
726 /// Group by method for grouping elements in a [`Series`]
727 /// by key.
728 ///
729 /// ## Example
730 ///
731 /// ```
732 /// use blackjack::prelude::*;
733 ///
734 /// let series = Series::from_vec(vec![1, 2, 3, 1, 2, 3]);
735 /// let keys = Series::from_vec(vec![4, 5, 6, 4, 5, 6]);
736 ///
737 /// let grouped: Series<i32> = series.groupby(&keys).sum();
738 /// assert_eq!(grouped.len(), 3);
739 ///
740 /// let mut vals = grouped.into_vec();
741 /// vals.sort();
742 /// assert_eq!(vals, vec![2, 4, 6]);
743 /// ```
744 pub fn groupby(&self, keys: &Series<T>) -> SeriesGroupBy<T>
745 where
746 T: ToPrimitive,
747 {
748 /* TODO: Revisit this to avoid the clones. Needs to keep the groups
749 in order based on key order; match pandas. ie:
750
751 >>> pd.Series([1, 2, 3, 1, 2, 3]).groupby([4, 5, 6, 4, 5, 6]).sum()
752 4 2
753 5 4
754 6 6
755 dtype: int64
756 */
757 use indexmap::IndexMap;
758
759 let values = self.values.clone();
760
761 let mut map: IndexMap<String, Vec<T>> = IndexMap::new();
762
763 // Group values by their keys
764 for (k, v) in keys.values.iter().zip(values.iter()) {
765 let key = k.to_string();
766 let mr = map.entry(key).or_insert(vec![]);
767 mr.push(v.clone());
768 }
769
770 // Create new series from the previous mapping.
771 let groups = map
772 .iter()
773 .map(|(name, values)| {
774 let mut series = Series::from_vec(values.clone());
775 series.set_name(name.as_str());
776 series
777 })
778 .collect();
779
780 SeriesGroupBy::new(groups)
781 }
782
783 /// Find the _positions_ where a condition is true
784 ///
785 /// ## Example
786 /// ```
787 /// # use blackjack::prelude::*;
788 ///
789 /// let series = Series::from(0..10);
790 /// let positions = series.find(|v| v % 2 == 0);
791 ///
792 /// assert_eq!(positions, vec![0, 2, 4, 6, 8]);
793 /// ```
794 pub fn find<F: Fn(&T) -> bool>(&self, condition: F) -> Vec<usize> {
795 self.values
796 .iter()
797 .enumerate()
798 .filter(|(_idx, val)| condition(val))
799 .map(|(idx, _val)| idx)
800 .collect()
801 }
802}
803
804// Support Series creation from Range
805impl<T> From<std::ops::Range<T>> for Series<T>
806where
807 T: BlackJackData,
808 std::ops::Range<T>: Iterator,
809 Vec<T>: FromIterator<<std::ops::Range<T> as Iterator>::Item>,
810{
811 fn from(range: std::ops::Range<T>) -> Series<T> {
812 let vec = range.collect();
813 Series::from_vec(vec)
814 }
815}
816
817// Support ref indexing
818impl<T> Index<usize> for Series<T>
819where
820 T: BlackJackData,
821{
822 type Output = T;
823 fn index(&self, idx: usize) -> &T {
824 &self.values[idx]
825 }
826}
827
828// Support ref indexing
829impl<T> Index<Range<usize>> for Series<T>
830where
831 T: BlackJackData,
832{
833 type Output = [T];
834 fn index(&self, idx: Range<usize>) -> &[T] {
835 &self.values[idx]
836 }
837}
838
839// Support ref mut indexing
840impl<T: BlackJackData> IndexMut<usize> for Series<T> {
841 fn index_mut(&mut self, idx: usize) -> &mut T {
842 &mut self.values[idx]
843 }
844}
845
846// Support Display for Series
847impl<T> fmt::Display for Series<T>
848where
849 T: BlackJackData,
850 String: From<T>,
851{
852 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
853 use prettytable::{Cell, Row, Table};
854
855 let mut table = Table::new();
856
857 // Title (column name)
858 table.add_row(Row::new(vec![Cell::new(
859 &self.name().unwrap_or("<NA>".to_string()),
860 )]));
861
862 // Build remaining values.
863 // TODO: Limit how many are actually printed.
864 let _ = self
865 .values
866 .iter()
867 .map(|v| {
868 let v: String = v.clone().into();
869 table.add_row(Row::new(vec![Cell::new(&format!("{}", v))]));
870 })
871 .collect::<Vec<()>>();
872
873 write!(f, "{}\n", table)
874 }
875}