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}