use std::sync::Arc;
use crate::column::Column;
use crate::dtype::DType;
use crate::error::{Result, VolasError};
use crate::index::Index;
#[derive(Clone, Debug)]
pub struct Series {
pub name: Option<String>,
pub data: Column,
pub index: Arc<Index>,
}
impl Series {
pub fn new(name: Option<String>, data: Column, index: Arc<Index>) -> Self {
debug_assert_eq!(
data.len(),
index.len(),
"Series data length != index length"
);
Series { name, data, index }
}
pub fn from_f64(name: Option<String>, values: Vec<f64>) -> Self {
let n = values.len();
Series::new(name, Column::f64(values), Arc::new(Index::range(n)))
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn dtype(&self) -> DType {
self.data.dtype()
}
pub fn filter_mask(&self, mask: &[bool]) -> Result<Series> {
if mask.len() != self.len() {
return Err(VolasError::Shape(format!(
"boolean mask length {} != series length {}",
mask.len(),
self.len()
)));
}
let idx: Vec<usize> = mask
.iter()
.enumerate()
.filter_map(|(i, &b)| if b { Some(i) } else { None })
.collect();
Ok(Series::new(
self.name.clone(),
self.data.take(&idx),
Arc::new(self.index.take(&idx)),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_f64_has_range_index_and_query_methods() {
let s = Series::from_f64(Some("x".into()), vec![1.0, 2.0, 3.0]);
assert_eq!(s.len(), 3);
assert!(!s.is_empty());
assert_eq!(s.dtype(), DType::F64);
assert_eq!(s.name.as_deref(), Some("x"));
assert_eq!(s.index.len(), 3);
}
#[test]
fn new_preserves_parts_and_empty_is_empty() {
let s = Series::new(None, Column::i64(vec![7, 8]), Arc::new(Index::range(2)));
assert_eq!(s.dtype(), DType::I64);
assert_eq!(s.len(), 2);
assert!(s.name.is_none());
let empty = Series::from_f64(None, vec![]);
assert!(empty.is_empty());
assert_eq!(empty.len(), 0);
}
#[test]
fn filter_mask_keeps_true_rows_and_labels() {
let s = Series::new(
Some("v".into()),
Column::f64(vec![10.0, 20.0, 30.0]),
Arc::new(Index::range(3)),
);
let sub = s.filter_mask(&[false, true, true]).unwrap();
assert_eq!(sub.data.to_f64_vec(), vec![20.0, 30.0]);
assert_eq!(sub.index.label_at(0), Index::range(3).label_at(1));
assert!(s.filter_mask(&[true, false]).is_err()); }
}