use std::fmt::Debug;
use crate::core::error::Result;
#[deprecated(
since = "0.1.0",
note = "Use new Series implementation in crate::series::base"
)]
pub use crate::series::Series as LegacySeries;
#[derive(Debug, Clone)]
pub struct Series<T>
where
T: Debug + Clone,
{
values: Vec<T>,
name: Option<String>,
}
impl<T> Series<T>
where
T: Debug + Clone,
{
pub fn new(data: Vec<T>, name: Option<String>) -> Result<Self> {
Ok(Self { values: data, name })
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn get(&self, index: usize) -> Option<&T> {
self.values.get(index)
}
pub fn values(&self) -> &[T] {
&self.values
}
pub fn to_vec(&self) -> Vec<T> {
self.values.clone()
}
pub fn name(&self) -> Option<&String> {
self.name.as_ref()
}
pub fn set_name(&mut self, name: String) {
self.name = Some(name);
}
pub fn with_name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn as_f64(&self) -> Result<Vec<f64>>
where
T: Into<f64> + Copy,
{
let mut result = Vec::with_capacity(self.values.len());
for value in &self.values {
result.push((*value).into());
}
Ok(result)
}
pub fn to_string_series(&self) -> Result<Series<String>>
where
T: std::fmt::Display,
{
let string_values: Vec<String> = self.values.iter().map(|v| v.to_string()).collect();
Series::new(string_values, self.name.clone())
}
pub fn shift(&self, periods: i64) -> Result<crate::series::NASeries<T>> {
let len = self.values.len();
let abs = periods.unsigned_abs().min(len as u64) as usize;
let mut shifted: Vec<crate::na::NA<T>> = Vec::with_capacity(len);
if periods >= 0 {
for _ in 0..abs {
shifted.push(crate::na::NA::NA);
}
for v in &self.values[..len - abs] {
shifted.push(crate::na::NA::Value(v.clone()));
}
} else {
for v in &self.values[abs..] {
shifted.push(crate::na::NA::Value(v.clone()));
}
for _ in 0..abs {
shifted.push(crate::na::NA::NA);
}
}
crate::series::NASeries::new(shifted, self.name.clone())
}
}
impl Series<i32> {
pub fn sum(&self) -> i64 {
self.values.iter().map(|&v| v as i64).sum()
}
pub fn mean(&self) -> Result<f64> {
if self.is_empty() {
return Err(crate::core::error::Error::EmptySeries);
}
Ok(self.sum() as f64 / self.len() as f64)
}
pub fn min(&self) -> Result<i32> {
self.values
.iter()
.min()
.cloned()
.ok_or_else(|| crate::core::error::Error::EmptySeries)
}
pub fn max(&self) -> Result<i32> {
self.values
.iter()
.max()
.cloned()
.ok_or_else(|| crate::core::error::Error::EmptySeries)
}
}
impl Series<i64> {
pub fn sum(&self) -> i64 {
self.values.iter().sum()
}
pub fn mean(&self) -> Result<f64> {
if self.is_empty() {
return Err(crate::core::error::Error::EmptySeries);
}
Ok(self.sum() as f64 / self.len() as f64)
}
pub fn min(&self) -> Result<i64> {
self.values
.iter()
.min()
.cloned()
.ok_or_else(|| crate::core::error::Error::EmptySeries)
}
pub fn max(&self) -> Result<i64> {
self.values
.iter()
.max()
.cloned()
.ok_or_else(|| crate::core::error::Error::EmptySeries)
}
}
impl Series<f64> {
pub fn sum(&self) -> f64 {
self.values.iter().copied().filter(|v| !v.is_nan()).sum()
}
pub fn mean(&self) -> Result<f64> {
let (sum, count) = self
.values
.iter()
.copied()
.filter(|v| !v.is_nan())
.fold((0.0_f64, 0usize), |(s, c), v| (s + v, c + 1));
if count == 0 {
return Err(crate::core::error::Error::EmptySeries);
}
Ok(sum / count as f64)
}
pub fn min(&self) -> Result<f64> {
self.values
.iter()
.copied()
.filter(|v| !v.is_nan())
.fold(None, |acc: Option<f64>, v| {
Some(acc.map_or(v, |a| a.min(v)))
})
.ok_or_else(|| crate::core::error::Error::EmptySeries)
}
pub fn max(&self) -> Result<f64> {
self.values
.iter()
.copied()
.filter(|v| !v.is_nan())
.fold(None, |acc: Option<f64>, v| {
Some(acc.map_or(v, |a| a.max(v)))
})
.ok_or_else(|| crate::core::error::Error::EmptySeries)
}
}
impl Series<String> {
pub fn str(&self) -> Result<crate::series::string_accessor::StringAccessor> {
crate::series::string_accessor::StringAccessor::new(self.clone()).map_err(|e| {
crate::core::error::Error::Type(format!("Failed to create string accessor: {:?}", e))
})
}
}
impl Series<chrono::NaiveDateTime> {
pub fn dt(&self) -> Result<crate::series::datetime_accessor::DateTimeAccessor> {
crate::series::datetime_accessor::DateTimeAccessor::new(self.clone()).map_err(|e| {
crate::core::error::Error::Type(format!("Failed to create datetime accessor: {:?}", e))
})
}
}
impl Series<chrono::DateTime<chrono::Utc>> {
pub fn dt_tz(&self) -> Result<crate::series::datetime_accessor::DateTimeAccessorTz> {
crate::series::datetime_accessor::DateTimeAccessorTz::new(self.clone()).map_err(|e| {
crate::core::error::Error::Type(format!("Failed to create datetime accessor: {:?}", e))
})
}
}
impl<T> std::ops::Index<usize> for Series<T>
where
T: Debug + Clone,
{
type Output = T;
fn index(&self, i: usize) -> &T {
&self.values[i]
}
}
impl<'a, T> IntoIterator for &'a Series<T>
where
T: Debug + Clone,
{
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.values.iter()
}
}
impl<T> FromIterator<T> for Series<T>
where
T: Debug + Clone,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let values: Vec<T> = iter.into_iter().collect();
Series::new(values, None).unwrap_or_else(|_| Series {
values: Vec::new(),
name: None,
})
}
}
impl<T> Extend<T> for Series<T>
where
T: Debug + Clone,
{
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.values.extend(iter);
}
}
impl<T> std::fmt::Display for Series<T>
where
T: Debug + Clone + std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = self.name.as_deref().unwrap_or("unnamed");
write!(f, "Series[{}](", name)?;
for (i, v) in self.values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, ")")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_series_index_trait() {
let s = Series::new(vec![10i64, 20, 30], Some("nums".to_string())).unwrap();
assert_eq!(s[0], 10);
assert_eq!(s[2], 30);
}
#[test]
fn test_series_into_iterator() {
let s = Series::new(vec![1.0f64, 2.0, 3.0], Some("vals".to_string())).unwrap();
let sum: f64 = (&s).into_iter().copied().sum();
assert!((sum - 6.0).abs() < 1e-10);
}
#[test]
fn test_series_collect() {
let s = Series::new(vec![1i64, 2, 3], Some("x".to_string())).unwrap();
let doubled: Vec<i64> = (&s).into_iter().map(|&v| v * 2).collect();
assert_eq!(doubled, vec![2, 4, 6]);
}
#[test]
fn test_series_from_iterator() {
let v = vec![4i64, 5, 6];
let s: Series<i64> = v.into_iter().collect();
assert_eq!(s.len(), 3);
assert_eq!(s[0], 4);
}
#[test]
fn test_series_extend() {
let mut s = Series::new(vec![1i64, 2], None).unwrap();
s.extend(vec![3i64, 4]);
assert_eq!(s.len(), 4);
assert_eq!(s[3], 4);
}
#[test]
fn test_series_display() {
let s = Series::new(vec![1i64, 2, 3], Some("nums".to_string())).unwrap();
let disp = s.to_string();
assert!(disp.contains("nums"), "Display missing name: {}", disp);
assert!(disp.contains("1"), "Display missing value: {}", disp);
}
}