1use crate::dtype::{DataType, DataTypeTrait};
6use crate::error::{AxionError, CastError, AxionResult};
7use super::interface::SeriesTrait;
8use super::ops::{SeriesArithScalar, SeriesCompareScalar, SeriesCompare, SeriesCompareSeries, SeriesArithSeries};
9use super::string::StringAccessor;
10
11use std::cmp::Ordering;
12use std::fmt::{self, Debug, Display};
13use std::any::Any;
14use num_traits::{Float, Zero, ToPrimitive};
15use std::iter::{FromIterator, Sum};
16use std::ops::{Add, Sub, Mul, Div, Rem, Deref};
17
18use rayon::prelude::*;
19
20#[derive(Debug, Clone, Copy, Default)]
22pub struct SeriesFlags {
23 is_sorted_ascending: bool,
25 is_sorted_descending: bool,
27}
28
29impl SeriesFlags {
30 pub fn is_sorted_ascending(&self) -> bool {
32 self.is_sorted_ascending
33 }
34
35 pub fn is_sorted_descending(&self) -> bool {
37 self.is_sorted_descending
38 }
39
40 pub fn is_sorted(&self) -> bool {
42 self.is_sorted_ascending || self.is_sorted_descending
43 }
44
45 fn set_sorted(&mut self, ascending: bool, descending: bool) {
47 self.is_sorted_ascending = ascending;
48 self.is_sorted_descending = descending;
49 }
50
51 fn clear_sorted(&mut self) {
53 self.is_sorted_ascending = false;
54 self.is_sorted_descending = false;
55 }
56}
57
58pub trait IntoSeriesData<T>
60where
61 T: DataTypeTrait + Clone + Debug,
62 Self: Sized,
63{
64 fn into_series_data(self) -> (Vec<T>, DataType);
66}
67
68impl<T> IntoSeriesData<T> for Vec<T>
70where
71 T: DataTypeTrait + Clone + Debug,
72{
73 fn into_series_data(self) -> (Vec<T>, DataType) {
74 let dtype = T::DTYPE;
75 (self, dtype)
76 }
77}
78
79impl<T> IntoSeriesData<T> for &[T]
81where
82 T: DataTypeTrait + Clone + Debug,
83{
84 fn into_series_data(self) -> (Vec<T>, DataType) {
85 let dtype = T::DTYPE;
86 (self.to_vec(), dtype)
87 }
88}
89
90impl<T, const N: usize> IntoSeriesData<T> for &[T; N]
92where
93 T: DataTypeTrait + Clone + Debug,
94{
95 fn into_series_data(self) -> (Vec<T>, DataType) {
96 let dtype = T::DTYPE;
97 (self.to_vec(), dtype)
98 }
99}
100
101impl<T, const N: usize> IntoSeriesData<T> for [T; N]
103where
104 T: DataTypeTrait + Clone + Debug,
105{
106 fn into_series_data(self) -> (Vec<T>, DataType) {
107 let dtype = T::DTYPE;
108 (self.to_vec(), dtype)
109 }
110}
111
112impl<'a, const N: usize> IntoSeriesData<String> for &'a [&'a str; N] {
114 fn into_series_data(self) -> (Vec<String>, DataType) {
115 let data = self.iter().map(|s| s.to_string()).collect::<Vec<String>>();
116 (data, DataType::String)
117 }
118}
119
120impl IntoSeriesData<String> for Vec<&str> {
122 fn into_series_data(self) -> (Vec<String>, DataType) {
123 let data = self.into_iter().map(|s| s.to_string()).collect::<Vec<String>>();
124 (data, DataType::String)
125 }
126}
127
128impl<'a> IntoSeriesData<String> for &'a [&'a str] {
130 fn into_series_data(self) -> (Vec<String>, DataType) {
131 let data = self.iter().map(|s| s.to_string()).collect::<Vec<String>>();
132 (data, DataType::String)
133 }
134}
135
136pub struct Series<T>
141{
142 name: String,
144 dtype: DataType,
146 pub data: Vec<Option<T>>,
148 flags: SeriesFlags,
150}
151
152impl<T: Clone> Clone for Series<T> {
154 fn clone(&self) -> Self {
155 Series {
156 name: self.name.clone(),
157 dtype: self.dtype.clone(),
158 data: self.data.clone(),
159 flags: self.flags,
160 }
161 }
162}
163
164impl<T> Debug for Series<T>
166where
167 T: Debug,
168{
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 f.debug_struct("Series")
171 .field("name", &self.name)
172 .field("dtype", &self.dtype)
173 .field("len", &self.len())
174 .field("flags", &self.flags)
175 .finish()
176 }
177}
178
179impl<T> Display for Series<T>
181where
182 T: Display,
183{
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 write!(f, "[")?;
186
187 let mut first = true;
188 for opt_val in &self.data {
189 if !first {
190 write!(f, ", ")?;
191 }
192 match opt_val {
193 Some(value) => write!(f, "{}", value)?,
194 None => write!(f, "null")?,
195 }
196 first = false;
197 }
198
199 write!(f, "]")
200 }
201}
202
203impl<T> Default for Series<T> {
205 fn default() -> Self {
206 Series {
207 name: String::new(),
208 dtype: DataType::Null,
209 data: Vec::new(),
210 flags: SeriesFlags::default(),
211 }
212 }
213}
214
215impl<T> Deref for Series<T> {
217 type Target = Vec<Option<T>>;
218
219 fn deref(&self) -> &Self::Target {
220 &self.data
221 }
222}
223
224impl<T> Series<T> {
225 pub fn new<D>(name: String, data_source: D) -> Self
227 where
228 T: DataTypeTrait + Clone + Debug + Send + Sync + 'static,
229 D: IntoSeriesData<T>,
230 {
231 let (data, dtype) = data_source.into_series_data();
232 let data_opts = data.into_iter().map(Some).collect();
233 Series { name, dtype, data: data_opts, flags: SeriesFlags::default() }
234 }
235
236 pub fn new_empty(name: String, dtype: DataType) -> Self {
238 Series { name, dtype, data: Vec::new(), flags: SeriesFlags::default() }
239 }
240
241 pub fn new_from_options(name: String, data: Vec<Option<T>>) -> Self
243 where
244 T: DataTypeTrait,
245 {
246 let dtype = T::DTYPE;
247 Series { name, dtype, data, flags: SeriesFlags::default() }
248 }
249
250 pub fn clear(&mut self) {
252 self.data.clear();
253 self.dtype = DataType::Null;
254 self.flags = SeriesFlags::default();
255 }
256
257 pub fn push(&mut self, value: Option<T>) where T: DataTypeTrait {
259 if self.dtype == DataType::Null {
260 if let Some(ref v) = value {
261 self.dtype = v.as_dtype();
262 }
263 }
264 self.flags.clear_sorted();
265 self.data.push(value);
266 }
267
268 pub fn name(&self) -> &str {
272 &self.name
273 }
274
275 pub fn dtype(&self) -> DataType {
277 self.dtype.clone()
278 }
279
280 pub fn len(&self) -> usize {
282 self.data.len()
283 }
284
285 pub fn is_empty(&self) -> bool {
287 self.data.is_empty()
288 }
289
290 pub fn get(&self, index: usize) -> Option<&T> {
292 self.data.get(index).and_then(|opt_val| opt_val.as_ref())
293 }
294
295 pub fn iter(&self) -> impl Iterator<Item = Option<&T>> + '_ {
297 self.data.iter().map(|opt_val| opt_val.as_ref())
298 }
299
300 pub fn iter_valid(&self) -> impl Iterator<Item = &T> + '_ {
302 self.data.iter().filter_map(|opt_val| opt_val.as_ref())
303 }
304
305 pub fn iter_valid_owned(&self) -> impl Iterator<Item = T> + '_ where T: Clone {
307 self.data.iter().filter_map(|opt_val| opt_val.clone())
308 }
309
310 pub fn data_internal(&self) -> &Vec<Option<T>> {
312 &self.data
313 }
314
315 pub fn take_inner(self) -> Vec<Option<T>> {
317 self.data
318 }
319
320 pub fn rename(&mut self, name: String) {
322 self.name = name;
323 }
324
325 pub fn with_name(mut self, name: String) -> Self {
327 self.name = name;
328 self
329 }
330
331 pub fn is_sorted_ascending(&self) -> bool {
335 self.flags.is_sorted_ascending()
336 }
337
338 pub fn is_sorted_descending(&self) -> bool {
340 self.flags.is_sorted_descending()
341 }
342
343 pub fn is_sorted(&self) -> bool {
345 self.flags.is_sorted()
346 }
347
348 pub fn set_sorted_flag(&mut self, ascending: bool, descending: bool) {
350 self.flags.set_sorted(ascending, descending);
351 }
352
353 pub fn get_flags(&self) -> SeriesFlags {
355 self.flags
356 }
357
358 pub fn get_opt(&self, index: usize) -> Option<Option<&T>> {
365 self.data.get(index).map(|opt_t| opt_t.as_ref())
366 }
367
368 pub fn sort(&mut self, reverse: bool)
373 where
374 T: Ord,
375 {
376 let compare_options = |a: &Option<T>, b: &Option<T>| -> Ordering {
377 match (a, b) {
378 (Some(va), Some(vb)) => va.cmp(vb),
379 (None, None) => Ordering::Equal,
380 (None, Some(_)) => Ordering::Less,
381 (Some(_), None) => Ordering::Greater,
382 }
383 };
384
385 if reverse {
386 self.data.sort_by(|a, b| compare_options(b, a));
387 self.flags.set_sorted(false, true);
388 } else {
389 self.data.sort_by(compare_options);
390 self.flags.set_sorted(true, false);
391 }
392 }
393
394 pub fn cast<NewType>(&self) -> AxionResult<Series<NewType>>
398 where
399 T: Clone + 'static,
400 NewType: DataTypeTrait + Clone + Debug + Default + 'static,
401 {
402 let target_dtype = NewType::default().as_dtype();
403
404 let mut new_data = Vec::with_capacity(self.len());
405 for opt_val in self.data.iter() {
406 match opt_val {
407 Some(val) => {
408 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f64>()
409 && std::any::TypeId::of::<NewType>() == std::any::TypeId::of::<f64>()
410 {
411 let any_ref = val as &dyn std::any::Any;
412 if let Some(float_val) = any_ref.downcast_ref::<f64>() {
413 let new_val_any = float_val as &dyn std::any::Any;
414 if let Some(new_val) = new_val_any.downcast_ref::<NewType>() {
415 new_data.push(Some(new_val.clone()));
416 } else {
417 return Err(AxionError::CastError(CastError("Failed to downcast to target type after 'as' cast".to_string())));
418 }
419 } else {
420 return Err(AxionError::CastError(CastError("Failed to downcast original value".to_string())));
421 }
422 } else {
423 return Err(AxionError::CastError(CastError(format!(
424 "Unsupported cast from {:?} to {:?}",
425 self.dtype(),
426 target_dtype
427 ))));
428 }
429 }
430 None => new_data.push(None),
431 }
432 }
433
434 Ok(Series {
435 name: self.name.clone(),
436 dtype: target_dtype,
437 data: new_data,
438 flags: SeriesFlags::default(),
439 })
440 }
441
442 pub fn sum(&self) -> Option<T>
446 where
447 T: Sum<T> + Clone + Zero,
448 {
449 let iter = self.iter_valid_owned();
450 let total: T = iter.sum();
451
452 if self.data.iter().any(|opt| opt.is_some()) {
453 Some(total)
454 } else {
455 None
456 }
457 }
458
459 pub fn min(&self) -> Option<T>
461 where
462 T: PartialOrd + Clone,
463 {
464 self.data
465 .iter()
466 .filter_map(|opt| opt.as_ref())
467 .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
468 .cloned()
469 }
470
471 pub fn max(&self) -> Option<T>
473 where
474 T: PartialOrd + Clone,
475 {
476 self.data
477 .iter()
478 .filter_map(|opt| opt.as_ref())
479 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
480 .cloned()
481 }
482
483 pub fn is_nan(&self) -> Series<bool>
487 where
488 T: Float,
489 {
490 let new_data: Vec<Option<bool>> = self.data.iter().map(|opt_val| {
491 opt_val.map(|v| v.is_nan())
492 .or(Some(false))
493 }).collect();
494 Series::new_from_options(format!("{}_is_nan", self.name), new_data)
495 }
496
497 pub fn is_not_nan(&self) -> Series<bool>
499 where
500 T: Float,
501 {
502 let new_data: Vec<Option<bool>> = self.data.iter().map(|opt_val| {
503 opt_val.map(|v| !v.is_nan())
504 .or(Some(true))
505 }).collect();
506 Series::new_from_options(format!("{}_is_not_nan", self.name), new_data)
507 }
508
509 pub fn is_infinite(&self) -> Series<bool>
511 where
512 T: Float,
513 {
514 let new_data: Vec<Option<bool>> = self.data.iter().map(|opt_val| {
515 opt_val.map(|v| v.is_infinite())
516 .or(Some(false))
517 }).collect();
518 Series::new_from_options(format!("{}_is_infinite", self.name), new_data)
519 }
520
521 pub fn map<U, F>(&self, f: F) -> Series<U>
525 where
526 T: Clone,
527 U: DataTypeTrait + Clone + Debug,
528 F: FnMut(Option<T>) -> Option<U>,
529 {
530 let new_data: Vec<Option<U>> = self.data.iter()
531 .cloned()
532 .map(f)
533 .collect();
534 Series::new_from_options(self.name.clone(), new_data)
535 }
536
537 pub fn filter(&self, mask: &Series<bool>) -> Self where T: Clone {
542 if self.len() != mask.len() {
543 panic!(
544 "Filter mask length ({}) must match Series length ({})",
545 mask.len(),
546 self.len()
547 );
548 }
549
550 let new_data: Vec<Option<T>> = self.data
551 .iter()
552 .zip(mask.data.iter())
553 .filter_map(|(data_opt, mask_opt)| {
554 match mask_opt {
555 Some(true) => Some(data_opt.clone()),
556 _ => None,
557 }
558 })
559 .collect();
560
561 Series {
562 name: self.name.clone(),
563 dtype: self.dtype.clone(),
564 data: new_data,
565 flags: SeriesFlags::default(),
566 }
567 }
568
569 pub fn equals(&self, other: &Self) -> bool
571 where
572 T: PartialEq,
573 {
574 if self.name != other.name || self.dtype != other.dtype || self.len() != other.len() {
575 return false;
576 }
577 self.data.iter().eq(other.data.iter())
578 }
579
580 pub fn equals_missing(&self, other: &Self) -> bool
582 where
583 T: PartialEq,
584 {
585 if self.name != other.name || self.dtype != other.dtype || self.len() != other.len() {
586 return false;
587 }
588 self.data.iter().eq(other.data.iter())
589 }
590
591 fn binary_op<F, U>(&self, other: &Series<T>, op: F) -> Series<U>
595 where
596 T: Clone,
597 U: DataTypeTrait + Clone + Debug,
598 F: Fn(T, T) -> U,
599 {
600 if self.len() != other.len() {
601 panic!("Cannot perform operation on Series of different lengths");
602 }
603
604 let new_data = self.data.iter().zip(other.data.iter()).map(|(opt_a, opt_b)| {
605 match (opt_a, opt_b) {
606 (Some(a), Some(b)) => Some(op(a.clone(), b.clone())),
607 _ => None,
608 }
609 }).collect();
610
611 Series::new_from_options(self.name.clone(), new_data)
612 }
613
614 fn scalar_op<F, U>(&self, scalar: T, op: F) -> Series<U>
616 where
617 T: Clone,
618 U: DataTypeTrait + Clone + Debug,
619 F: Fn(T, T) -> U,
620 {
621 let new_data = self.data.iter().map(|opt_a| {
622 opt_a.as_ref().map(|a| op(a.clone(), scalar.clone()))
623 }).collect();
624 Series::new_from_options(self.name.clone(), new_data)
625 }
626
627 pub fn mean(&self) -> Option<f64>
629 where
630 T: Clone + Zero + ToPrimitive,
631 {
632 let mut sum = 0.0f64;
633 let mut count = 0usize;
634
635 for val in self.data.iter().flatten() {
636 if let Some(float_val) = val.to_f64() {
637 sum += float_val;
638 count += 1;
639 }
640 }
641
642 if count > 0 {
643 Some(sum / count as f64)
644 } else {
645 None
646 }
647 }
648
649 pub fn apply<F, U>(&self, func: F) -> Series<U>
653 where
654 U: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
655 F: Fn(Option<&T>) -> Option<U>,
656 {
657 let new_data: Vec<Option<U>> = self.data.iter()
658 .map(|opt_ref_t| func(opt_ref_t.as_ref()))
659 .collect();
660
661 if self.is_empty() {
662 let dtype = U::DTYPE;
663 Series::new_empty(self.name.clone(), dtype)
664 } else {
665 Series::new_from_options(self.name.clone(), new_data)
666 }
667 }
668
669 pub fn par_apply<F, U>(&self, func: F) -> Series<U>
673 where
674 U: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
675 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
676 F: Fn(Option<&T>) -> Option<U> + Send + Sync,
677 {
678 if self.is_empty() {
679 return Series::new_empty(self.name.clone(), U::DTYPE);
680 }
681
682 let new_data: Vec<Option<U>> = self.data
683 .par_iter()
684 .map(|opt_val| func(opt_val.as_ref()))
685 .collect();
686
687 Series::new_from_options(self.name.clone(), new_data)
688 }
689}
690
691impl Series<String> {
694 pub fn str(&self) -> StringAccessor<'_> {
696 StringAccessor::new(self)
697 }
698}
699
700impl<T> Add<&Series<T>> for &Series<T>
703where
704 T: DataTypeTrait + Clone + Debug + Add<Output = T>,
705{
706 type Output = Series<T>;
707
708 fn add(self, rhs: &Series<T>) -> Self::Output {
709 self.binary_op(rhs, |a, b| a + b)
710 }
711}
712
713impl<T> Add<T> for &Series<T>
714where
715 T: DataTypeTrait + Clone + Debug + Add<Output = T>,
716{
717 type Output = Series<T>;
718
719 fn add(self, rhs: T) -> Self::Output {
720 self.scalar_op(rhs, |a, b| a + b)
721 }
722}
723
724impl<T> Sub<&Series<T>> for &Series<T>
725where
726 T: DataTypeTrait + Clone + Debug + Sub<Output = T>,
727{
728 type Output = Series<T>;
729
730 fn sub(self, rhs: &Series<T>) -> Self::Output {
731 self.binary_op(rhs, |a, b| a - b)
732 }
733}
734
735impl<T> Sub<T> for &Series<T>
736where
737 T: DataTypeTrait + Clone + Debug + Sub<Output = T>,
738{
739 type Output = Series<T>;
740
741 fn sub(self, rhs: T) -> Self::Output {
742 self.scalar_op(rhs, |a, b| a - b)
743 }
744}
745
746impl<T> Mul<&Series<T>> for &Series<T>
747where
748 T: DataTypeTrait + Clone + Debug + Mul<Output = T>,
749{
750 type Output = Series<T>;
751
752 fn mul(self, rhs: &Series<T>) -> Self::Output {
753 self.binary_op(rhs, |a, b| a * b)
754 }
755}
756
757impl<T> Mul<T> for &Series<T>
758where
759 T: DataTypeTrait + Clone + Debug + Mul<Output = T>,
760{
761 type Output = Series<T>;
762
763 fn mul(self, rhs: T) -> Self::Output {
764 self.scalar_op(rhs, |a, b| a * b)
765 }
766}
767
768impl<T> Div<&Series<T>> for &Series<T>
769where
770 T: DataTypeTrait + Clone + Debug + Div<Output = T> + Zero,
771{
772 type Output = Series<T>;
773
774 fn div(self, rhs: &Series<T>) -> Self::Output {
775 if self.len() != rhs.len() {
776 panic!("Cannot perform operation on Series of different lengths");
777 }
778 let new_data = self.data.iter().zip(rhs.data.iter()).map(|(opt_a, opt_b)| {
779 match (opt_a, opt_b) {
780 (Some(a), Some(b)) => {
781 if b.is_zero() { None }
782 else { Some(a.clone() / b.clone()) }
783 },
784 _ => None,
785 }
786 }).collect();
787 Series::new_from_options(self.name.clone(), new_data)
788 }
789}
790
791impl<T> Div<T> for &Series<T>
792where
793 T: DataTypeTrait + Clone + Debug + Div<Output = T> + Zero,
794{
795 type Output = Series<T>;
796
797 fn div(self, rhs: T) -> Self::Output {
798 if rhs.is_zero() {
799 let new_data = vec![None; self.len()];
800 Series::new_from_options(self.name.clone(), new_data)
801 } else {
802 self.scalar_op(rhs, |a, b| a / b)
803 }
804 }
805}
806
807impl<T> Rem<&Series<T>> for &Series<T>
808where
809 T: DataTypeTrait + Clone + Debug + Rem<Output = T> + Zero,
810{
811 type Output = Series<T>;
812
813 fn rem(self, rhs: &Series<T>) -> Self::Output {
814 if self.len() != rhs.len() {
815 panic!("Cannot perform operation on Series of different lengths");
816 }
817 let new_data = self.data.iter().zip(rhs.data.iter()).map(|(opt_a, opt_b)| {
818 match (opt_a, opt_b) {
819 (Some(a), Some(b)) => {
820 if b.is_zero() { None }
821 else { Some(a.clone() % b.clone()) }
822 },
823 _ => None,
824 }
825 }).collect();
826 Series::new_from_options(self.name.clone(), new_data)
827 }
828}
829
830impl<T> Rem<T> for &Series<T>
831where
832 T: DataTypeTrait + Clone + Debug + Rem<Output = T> + Zero,
833{
834 type Output = Series<T>;
835
836 fn rem(self, rhs: T) -> Self::Output {
837 if rhs.is_zero() {
838 let new_data = vec![None; self.len()];
839 Series::new_from_options(self.name.clone(), new_data)
840 } else {
841 self.scalar_op(rhs, |a, b| a % b)
842 }
843 }
844}
845
846impl<T> FromIterator<T> for Series<T>
849where
850 T: DataTypeTrait + Clone + Debug,
851{
852 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
853 let data: Vec<T> = iter.into_iter().collect();
854 let (data_vec, dtype) = data.into_series_data();
855 let data_opts = data_vec.into_iter().map(Some).collect();
856 Series {
857 name: String::new(),
858 dtype,
859 data: data_opts,
860 flags: SeriesFlags::default(),
861 }
862 }
863}
864
865impl<T> FromIterator<Option<T>> for Series<T>
866where
867 T: DataTypeTrait + Clone + Debug,
868{
869 fn from_iter<I: IntoIterator<Item = Option<T>>>(iter: I) -> Self {
870 let data: Vec<Option<T>> = iter.into_iter().collect();
871 Series::new_from_options(String::new(), data)
872 }
873}
874
875impl<T> From<(String, Vec<T>)> for Series<T>
878where
879 T: DataTypeTrait + Clone + Debug,
880{
881 fn from(tuple: (String, Vec<T>)) -> Self {
882 let (name, data) = tuple;
883 let dtype = data.first().map_or(DataType::Null, |v| v.as_dtype());
884 let data_opts = data.into_iter().map(Some).collect();
885 Series {
886 name,
887 dtype,
888 data: data_opts,
889 flags: SeriesFlags::default(),
890 }
891 }
892}
893
894impl<T> From<(String, Vec<Option<T>>)> for Series<T>
895where
896 T: DataTypeTrait + Clone + Debug,
897{
898 fn from(tuple: (String, Vec<Option<T>>)) -> Self {
899 let (name, data) = tuple;
900 let dtype = data
901 .iter()
902 .find_map(|opt_val| opt_val.as_ref().map(|v| v.as_dtype()))
903 .unwrap_or(DataType::Null);
904
905 Series {
906 name,
907 dtype,
908 data,
909 flags: SeriesFlags::default(),
910 }
911 }
912}
913
914impl<'a, T> From<(String, &'a [T])> for Series<T>
915where
916 T: DataTypeTrait + Clone + Debug,
917{
918 fn from(tuple: (String, &'a [T])) -> Self {
919 let (name, data_slice) = tuple;
920 let dtype = data_slice.first().map_or(DataType::Null, |v| v.as_dtype());
921 let data_opts = data_slice.iter().cloned().map(Some).collect();
922 Series {
923 name,
924 dtype,
925 data: data_opts,
926 flags: SeriesFlags::default(),
927 }
928 }
929}
930
931impl<T, const N: usize> From<(String, [T; N])> for Series<T>
932where
933 T: DataTypeTrait + Clone + Debug,
934{
935 fn from(tuple: (String, [T; N])) -> Self {
936 let (name, data_array) = tuple;
937 let dtype = data_array.first().map_or(DataType::Null, |v| v.as_dtype());
938 let data_opts = Vec::from(data_array).into_iter().map(Some).collect();
939 Series {
940 name,
941 dtype,
942 data: data_opts,
943 flags: SeriesFlags::default(),
944 }
945 }
946}
947
948impl<'a, T> IntoIterator for &'a Series<T>
951where
952 T: DataTypeTrait + Clone + Debug,
953{
954 type Item = Option<&'a T>;
955 type IntoIter = std::iter::Map<std::slice::Iter<'a, Option<T>>, fn(&'a Option<T>) -> Option<&'a T>>;
956
957 fn into_iter(self) -> Self::IntoIter {
958 self.data.iter().map(|opt_val| opt_val.as_ref())
959 }
960}
961
962impl Series<bool> {
965 pub fn all(&self) -> bool {
967 self.data
968 .iter()
969 .all(|opt_val| matches!(opt_val, Some(true)))
970 }
971
972 pub fn any(&self) -> bool {
974 self.data
975 .iter()
976 .any(|opt_val| matches!(opt_val, Some(true)))
977 }
978}
979
980impl<T> SeriesTrait for Series<T>
983where
984 T: DataTypeTrait + Display + Debug + Send + Sync + Clone + PartialEq + PartialOrd + 'static,
985{
986 fn name(&self) -> &str {
987 self.name()
988 }
989
990 fn dtype(&self) -> DataType {
991 self.dtype()
992 }
993
994 fn len(&self) -> usize {
995 self.len()
996 }
997
998 fn as_any(&self) -> &dyn Any {
999 self
1000 }
1001
1002 fn as_any_mut(&mut self) -> &mut dyn Any {
1003 self
1004 }
1005
1006 fn get_str(&self, index: usize) -> Option<String> {
1007 self.data.get(index).map(|opt_val| {
1008 match opt_val {
1009 Some(value) => value.to_string(),
1010 None => "null".to_string(),
1011 }
1012 })
1013 }
1014
1015 fn is_null_at(&self, index: usize) -> bool {
1016 self.data.get(index).map_or(true, |opt_val| opt_val.is_none())
1017 }
1018
1019 fn clone_box(&self) -> Box<dyn SeriesTrait> {
1020 Box::new(self.clone())
1021 }
1022
1023 fn slice(&self, offset: usize, length: usize) -> Box<dyn SeriesTrait> {
1024 let start = std::cmp::min(offset, self.len());
1025 let end = std::cmp::min(start + length, self.len());
1026 let sliced_data = self.data[start..end].to_vec();
1027 let new_series = Series::new_from_options(self.name.clone(), sliced_data);
1028 Box::new(new_series)
1029 }
1030
1031 fn filter(&self, mask: &Series<bool>) -> AxionResult<Box<dyn SeriesTrait>> {
1032 let mut new_data = Vec::with_capacity(self.len());
1033
1034 for (opt_val, opt_mask_val) in self.data.iter().zip(mask.data_internal().iter()) {
1035 if let Some(true) = opt_mask_val {
1036 new_data.push(opt_val.clone());
1037 }
1038 }
1039
1040 let new_series = Series::new_from_options(self.name.clone(), new_data);
1041 Ok(Box::new(new_series))
1042 }
1043
1044 fn take_indices(&self, indices: &[usize]) -> AxionResult<Box<dyn SeriesTrait>> {
1045 let mut new_data = Vec::with_capacity(indices.len());
1046
1047 for &idx in indices {
1048 new_data.push(self.data.get(idx).cloned().ok_or_else(|| AxionError::IndexOutOfBounds(idx, self.len()))?);
1049 }
1050
1051 let new_series = Series::new_from_options(self.name.clone(), new_data);
1052 Ok(Box::new(new_series))
1053 }
1054
1055 fn take_indices_option(&self, indices: &[Option<usize>]) -> AxionResult<Box<dyn SeriesTrait>> {
1056 let mut new_data = Vec::with_capacity(indices.len());
1057 for opt_idx in indices {
1058 match opt_idx {
1059 Some(idx) => {
1060 let opt_val = self.data.get(*idx)
1061 .ok_or_else(|| AxionError::IndexOutOfBounds(*idx, self.len()))?
1062 .clone();
1063 new_data.push(opt_val);
1064 }
1065 None => {
1066 new_data.push(None);
1067 }
1068 }
1069 }
1070 Ok(Box::new(Series::new_from_options(self.name.clone(), new_data)))
1071 }
1072
1073 fn rename(&mut self, new_name: &str){
1074 self.name = new_name.to_string();
1075 }
1076
1077 fn series_equal(&self, other: &dyn SeriesTrait) -> bool {
1078 if self.dtype() != other.dtype() {
1079 return false;
1080 }
1081 if let Some(other_series) = other.as_any().downcast_ref::<Series<T>>() {
1082 if T::DTYPE == DataType::Float32 || T::DTYPE == DataType::Float64 {
1083 self.data.iter().zip(other_series.data.iter()).all(|(a, b)| {
1084 match (a, b) {
1085 (Some(val_a), Some(val_b)) => {
1086 let f_a = unsafe { *(val_a as *const T as *const f64) };
1087 let f_b = unsafe { *(val_b as *const T as *const f64) };
1088 (f_a.is_nan() && f_b.is_nan()) || (f_a == f_b)
1089 }
1090 (None, None) => true,
1091 _ => false,
1092 }
1093 })
1094 } else {
1095 self.data == other_series.data
1096 }
1097 } else {
1098 false
1099 }
1100 }
1101
1102 fn compare_row(&self, a_idx: usize, b_idx: usize) -> Ordering {
1103 let a_opt = self.data.get(a_idx);
1104 let b_opt = self.data.get(b_idx);
1105
1106 match (a_opt, b_opt) {
1107 (Some(Some(a_val)), Some(Some(b_val))) => {
1108 match a_val.partial_cmp(b_val) {
1109 Some(order) => order,
1110 None => Ordering::Equal,
1111 }
1112 }
1113 (Some(Some(_)), Some(None)) => Ordering::Less,
1114 (Some(None), Some(Some(_))) => Ordering::Greater,
1115 (Some(None), Some(None)) => Ordering::Equal,
1116 (None, None) => Ordering::Equal,
1117 (None, Some(_)) => Ordering::Greater,
1118 (Some(_), None) => Ordering::Less,
1119 }
1120 }
1121
1122 fn get_as_f64(&self, index: usize) -> AxionResult<Option<f64>> {
1123 match self.data.get(index) {
1124 Some(Some(val)) => {
1125 let any_val = val as &dyn Any;
1126
1127 if let Some(v) = any_val.downcast_ref::<i8>() { Ok(v.to_f64()) }
1128 else if let Some(v) = any_val.downcast_ref::<i16>() { Ok(v.to_f64()) }
1129 else if let Some(v) = any_val.downcast_ref::<i32>() { Ok(v.to_f64()) }
1130 else if let Some(v) = any_val.downcast_ref::<i64>() { Ok(v.to_f64()) }
1131 else if let Some(v) = any_val.downcast_ref::<u8>() { Ok(v.to_f64()) }
1132 else if let Some(v) = any_val.downcast_ref::<u16>() { Ok(v.to_f64()) }
1133 else if let Some(v) = any_val.downcast_ref::<u32>() { Ok(v.to_f64()) }
1134 else if let Some(v) = any_val.downcast_ref::<u64>() { Ok(v.to_f64()) }
1135 else if let Some(v) = any_val.downcast_ref::<f32>() { Ok(v.to_f64()) }
1136 else if let Some(v) = any_val.downcast_ref::<f64>() { Ok(Some(*v)) }
1137 else {
1138 Ok(None)
1139 }
1140 }
1141 Some(None) => Ok(None),
1142 None => Err(AxionError::IndexOutOfBounds(index, self.len())),
1143 }
1144 }
1145
1146 fn is_empty(&self) -> bool {
1147 self.len() == 0
1148 }
1149}
1150
1151impl Clone for Box<dyn SeriesTrait> {
1152 fn clone(&self) -> Self {
1153 self.clone_box()
1154 }
1155}
1156
1157macro_rules! impl_compare_scalar {
1161 ($method_name:ident, $op:tt) => {
1162 fn $method_name(&self, rhs: Rhs) -> AxionResult<Series<bool>> {
1163 let new_data: Vec<Option<bool>> = self.data.iter().map(|opt_val| {
1164 opt_val.as_ref().map(|val| val $op &rhs)
1165 }).collect();
1166 Ok(Series::new_from_options(format!("{}_{}", self.name, stringify!($method_name)), new_data))
1167 }
1168 };
1169}
1170
1171impl<T, Rhs> SeriesCompareScalar<Rhs> for Series<T>
1172where
1173 T: DataTypeTrait + Clone + PartialOrd<Rhs> + PartialEq<Rhs>,
1174 Rhs: Clone + PartialOrd + PartialEq,
1175{
1176 impl_compare_scalar!(gt_scalar, >);
1177 impl_compare_scalar!(lt_scalar, <);
1178 impl_compare_scalar!(eq_scalar, ==);
1179 impl_compare_scalar!(neq_scalar, !=);
1180 impl_compare_scalar!(gte_scalar, >=);
1181 impl_compare_scalar!(lte_scalar, <=);
1182}
1183
1184macro_rules! impl_arith_scalar {
1186 ($method_name:ident, $op_trait:ident, $op_method:ident, $op_symbol:tt, $output_assoc_type:ident) => {
1187 fn $method_name(&self, rhs: Rhs) -> AxionResult<Series<Self::$output_assoc_type>> {
1188 let new_data: Vec<Option<Self::$output_assoc_type>> = self.data.iter().map(|opt_val| {
1189 opt_val.as_ref().map(|val| val.clone().$op_method(rhs.clone()))
1190 }).collect();
1191 Ok(Series::new_from_options(format!("{}_{}", self.name, stringify!($method_name)), new_data))
1192 }
1193 };
1194 ($method_name:ident, $op_trait:ident, $op_method:ident, $op_symbol:tt, $output_assoc_type:ident, ZeroCheck) => {
1195 fn $method_name(&self, rhs: Rhs) -> AxionResult<Series<Self::$output_assoc_type>> {
1196 if rhs.is_zero() {
1197 let new_data = vec![None; self.len()];
1198 Ok(Series::new_from_options(format!("{}_{}_by_zero", self.name, stringify!($method_name)), new_data))
1199 } else {
1200 let new_data: Vec<Option<Self::$output_assoc_type>> = self.data.iter().map(|opt_val| {
1201 opt_val.as_ref().map(|val| val.clone().$op_method(rhs.clone()))
1202 }).collect();
1203 Ok(Series::new_from_options(format!("{}_{}", self.name, stringify!($method_name)), new_data))
1204 }
1205 }
1206 };
1207}
1208
1209impl<T, Rhs> SeriesArithScalar<Rhs> for Series<T>
1210where
1211 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1212 Rhs: Clone + Zero,
1213 T: Add<Rhs> + Sub<Rhs> + Mul<Rhs> + Div<Rhs> + Rem<Rhs>,
1214 <T as Add<Rhs>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1215 <T as Sub<Rhs>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1216 <T as Mul<Rhs>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1217 <T as Div<Rhs>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1218 <T as Rem<Rhs>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1219{
1220 type AddOutput = <T as Add<Rhs>>::Output;
1221 type SubOutput = <T as Sub<Rhs>>::Output;
1222 type MulOutput = <T as Mul<Rhs>>::Output;
1223 type DivOutput = <T as Div<Rhs>>::Output;
1224 type RemOutput = <T as Rem<Rhs>>::Output;
1225
1226 impl_arith_scalar!(add_scalar, Add, add, +, AddOutput);
1227 impl_arith_scalar!(sub_scalar, Sub, sub, -, SubOutput);
1228 impl_arith_scalar!(mul_scalar, Mul, mul, *, MulOutput);
1229 impl_arith_scalar!(div_scalar, Div, div, /, DivOutput, ZeroCheck);
1230 impl_arith_scalar!(rem_scalar, Rem, rem, %, RemOutput, ZeroCheck);
1231}
1232
1233macro_rules! impl_compare_series {
1235 ($method_name:ident, $op:tt) => {
1236 fn $method_name(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1237 if self.len() != rhs.len() {
1238 return Err(AxionError::MismatchedLengths {
1239 expected: self.len(),
1240 found: rhs.len(),
1241 name: rhs.name().to_string(),
1242 });
1243 }
1244
1245 let new_data: Vec<Option<bool>> = self.data.iter()
1246 .zip(rhs.data.iter())
1247 .map(|(opt_left, opt_right)| {
1248 match (opt_left.as_ref(), opt_right.as_ref()) {
1249 (Some(left_val), Some(right_val)) => Some(left_val $op right_val),
1250 _ => None,
1251 }
1252 })
1253 .collect();
1254
1255 Ok(Series::new_from_options(format!("{}_{}", self.name, stringify!($method_name)), new_data))
1256 }
1257 };
1258}
1259
1260impl<T> SeriesCompareSeries<&Series<T>> for Series<T>
1261where
1262 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1263 T: PartialOrd + PartialEq,
1264{
1265 impl_compare_series!(gt_series, >);
1266 impl_compare_series!(lt_series, <);
1267 impl_compare_series!(eq_series, ==);
1268 impl_compare_series!(neq_series, !=);
1269 impl_compare_series!(gte_series, >=);
1270 impl_compare_series!(lte_series, <=);
1271}
1272
1273pub trait IntoSeriesBox<T>
1276where
1277 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1278 Self: Sized,
1279{
1280 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>>;
1281}
1282
1283impl<T> IntoSeriesBox<T> for Vec<T>
1284where
1285 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + PartialEq + PartialOrd + 'static,
1286{
1287 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1288 Ok(Box::new(Series::new(name, self)))
1289 }
1290}
1291
1292impl<T> IntoSeriesBox<T> for Vec<Option<T>>
1293where
1294 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + PartialEq + PartialOrd + 'static,
1295{
1296 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1297 Ok(Box::new(Series::new_from_options(name, self)))
1298 }
1299}
1300
1301impl<T> IntoSeriesBox<T> for &[T]
1302where
1303 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + PartialEq + PartialOrd + 'static,
1304{
1305 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1306 Ok(Box::new(Series::new(name, self.to_vec())))
1307 }
1308}
1309
1310impl<T> IntoSeriesBox<T> for &[Option<T>]
1311where
1312 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + PartialEq + PartialOrd + 'static,
1313{
1314 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1315 Ok(Box::new(Series::new_from_options(name, self.to_vec())))
1316 }
1317}
1318
1319impl<'a> IntoSeriesBox<String> for &'a [&'a str]
1320{
1321 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1322 let string_vec: Vec<String> = self.iter().map(|&s| s.to_string()).collect();
1323 string_vec.into_series_box(name)
1324 }
1325}
1326
1327impl<'a, const N: usize> IntoSeriesBox<String> for &'a [&'a str; N]
1328{
1329 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1330 let string_vec: Vec<String> = self.iter().map(|&s| s.to_string()).collect();
1331 string_vec.into_series_box(name)
1332 }
1333}
1334
1335impl<T, const N: usize> IntoSeriesBox<T> for &[T; N]
1336where
1337 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + PartialEq + PartialOrd + 'static,
1338{
1339 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1340 Ok(Box::new(Series::new(name, self.to_vec())))
1341 }
1342}
1343
1344impl IntoSeriesBox<String> for Vec<&str>
1345{
1346 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1347 let string_vec: Vec<String> = self.into_iter().map(|s| s.to_string()).collect();
1348 string_vec.into_series_box(name)
1349 }
1350}
1351
1352impl IntoSeriesBox<String> for Vec<Option<&str>>
1353{
1354 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1355 let string_opt_vec: Vec<Option<String>> = self
1356 .into_iter()
1357 .map(|opt_s| opt_s.map(|s| s.to_string()))
1358 .collect();
1359 string_opt_vec.into_series_box(name)
1360 }
1361}
1362
1363impl<T, const N: usize> IntoSeriesBox<T> for [T; N]
1364where
1365 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + PartialEq + PartialOrd + 'static,
1366{
1367 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1368 Ok(Box::new(Series::new(name, self.to_vec())))
1369 }
1370}
1371
1372impl<const N: usize> IntoSeriesBox<String> for [&str; N]
1373{
1374 fn into_series_box(self, name: String) -> AxionResult<Box<dyn SeriesTrait>> {
1375 let string_vec: Vec<String> = self.iter().map(|&s| s.to_string()).collect();
1376 string_vec.into_series_box(name)
1377 }
1378}
1379
1380impl<T, RhsScalar> SeriesCompare<RhsScalar> for Series<T>
1383where
1384 Self: SeriesCompareScalar<RhsScalar>,
1385 RhsScalar: Clone + PartialOrd + PartialEq,
1386 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1387{
1388 #[inline]
1389 fn gt(&self, rhs: RhsScalar) -> AxionResult<Series<bool>> {
1390 SeriesCompareScalar::gt_scalar(self, rhs)
1391 }
1392 #[inline]
1393 fn lt(&self, rhs: RhsScalar) -> AxionResult<Series<bool>> {
1394 SeriesCompareScalar::lt_scalar(self, rhs)
1395 }
1396 #[inline]
1397 fn eq(&self, rhs: RhsScalar) -> AxionResult<Series<bool>> {
1398 SeriesCompareScalar::eq_scalar(self, rhs)
1399 }
1400 #[inline]
1401 fn neq(&self, rhs: RhsScalar) -> AxionResult<Series<bool>> {
1402 SeriesCompareScalar::neq_scalar(self, rhs)
1403 }
1404 #[inline]
1405 fn gte(&self, rhs: RhsScalar) -> AxionResult<Series<bool>> {
1406 SeriesCompareScalar::gte_scalar(self, rhs)
1407 }
1408 #[inline]
1409 fn lte(&self, rhs: RhsScalar) -> AxionResult<Series<bool>> {
1410 SeriesCompareScalar::lte_scalar(self, rhs)
1411 }
1412}
1413
1414impl<T> SeriesCompare<&Series<T>> for Series<T>
1415where
1416 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1417 for<'a> Self: SeriesCompareSeries<&'a Series<T>>,
1418{
1419 #[inline]
1420 fn gt(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1421 SeriesCompareSeries::gt_series(self, rhs)
1422 }
1423 #[inline]
1424 fn lt(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1425 SeriesCompareSeries::lt_series(self, rhs)
1426 }
1427 #[inline]
1428 fn eq(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1429 SeriesCompareSeries::eq_series(self, rhs)
1430 }
1431 #[inline]
1432 fn neq(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1433 SeriesCompareSeries::neq_series(self, rhs)
1434 }
1435 #[inline]
1436 fn gte(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1437 SeriesCompareSeries::gte_series(self, rhs)
1438 }
1439 #[inline]
1440 fn lte(&self, rhs: &Series<T>) -> AxionResult<Series<bool>> {
1441 SeriesCompareSeries::lte_series(self, rhs)
1442 }
1443}
1444
1445macro_rules! impl_arith_series {
1447 ($method_name:ident, $op_trait:ident, $op_method:ident, $op_symbol:tt, $output_assoc_type:ident) => {
1449 fn $method_name(&self, rhs: &Series<T>) -> AxionResult<Series<Self::$output_assoc_type>> {
1450
1451 if self.len() != rhs.len() {
1452 return Err(AxionError::MismatchedLengths {
1453 expected: self.len(),
1454 found: rhs.len(),
1455 name: rhs.name().to_string(), });
1457 }
1458
1459
1460 let new_data: Vec<Option<Self::$output_assoc_type>> = self.data.iter()
1461 .zip(rhs.data.iter())
1462 .map(|(opt_left, opt_right)| {
1463 match (opt_left.as_ref(), opt_right.as_ref()) {
1465 (Some(left), Some(right)) => {
1466 Some(left.clone().$op_method(right.clone()))
1469 }
1470 _ => None, }
1472 })
1473 .collect();
1474
1475 Ok(Series::new_from_options(format!("{}_{}_{}", self.name, stringify!($method_name), rhs.name), new_data))
1476 }
1477 };
1478}
1479
1480
1481impl<T> SeriesArithSeries<&Series<T>> for Series<T>
1483where
1484 T: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1485 T: Add<T> + Sub<T> + Mul<T> + Div<T> + Rem<T>,
1486 <T as Add<T>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1487 <T as Sub<T>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1488 <T as Mul<T>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1489 <T as Div<T>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1490 <T as Rem<T>>::Output: DataTypeTrait + Clone + Debug + Display + Send + Sync + 'static,
1491{
1492 type AddOutput = <T as Add<T>>::Output;
1494 type SubOutput = <T as Sub<T>>::Output;
1495 type MulOutput = <T as Mul<T>>::Output;
1496 type DivOutput = <T as Div<T>>::Output;
1497 type RemOutput = <T as Rem<T>>::Output;
1498
1499 impl_arith_series!(add_series, Add, add, +, AddOutput);
1501 impl_arith_series!(sub_series, Sub, sub, -, SubOutput);
1502 impl_arith_series!(mul_series, Mul, mul, *, MulOutput);
1503 impl_arith_series!(div_series, Div, div, /, DivOutput);
1504 impl_arith_series!(rem_series, Rem, rem, %, RemOutput);
1505}
1506
1507impl<T: DataTypeTrait + Clone + 'static> Series<T> {
1508 pub fn is_null(&self) -> Series<bool> {
1510 let data: Vec<Option<bool>> = self.data.iter().map(|v| Some(v.is_none())).collect();
1511 Series::new_from_options(format!("{}_is_null", self.name), data)
1512 }
1513
1514 pub fn not_null(&self) -> Series<bool> {
1516 let data: Vec<Option<bool>> = self.data.iter().map(|v| Some(v.is_some())).collect();
1517 Series::new_from_options(format!("{}_not_null", self.name), data)
1518 }
1519
1520 pub fn fill_null(&self, value: T) -> Series<T> {
1522 let data: Vec<Option<T>> = self.data.iter().map(|v| v.clone().or(Some(value.clone()))).collect();
1523 Series::new_from_options(self.name.clone(), data)
1524 }
1525}