use pandrs::error::Result;
use pandrs::series::{WindowExt, WindowOps};
use pandrs::{DataFrame, Series};
#[test]
#[allow(clippy::result_large_err)]
fn test_series_rolling_operations() -> Result<()> {
let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let rolling_mean = series.rolling(3)?.mean()?;
assert_eq!(rolling_mean.len(), 10);
assert!(rolling_mean.values()[0].is_nan());
assert!(rolling_mean.values()[1].is_nan());
assert!((rolling_mean.values()[2] - 2.0).abs() < 1e-10);
assert!((rolling_mean.values()[3] - 3.0).abs() < 1e-10);
assert!((rolling_mean.values()[4] - 4.0).abs() < 1e-10);
let rolling_sum = series.rolling(3)?.sum()?;
assert!(rolling_sum.values()[0].is_nan());
assert!(rolling_sum.values()[1].is_nan());
assert!((rolling_sum.values()[2] - 6.0).abs() < 1e-10);
assert!((rolling_sum.values()[3] - 9.0).abs() < 1e-10);
let rolling_min = series.rolling(3)?.min()?;
let rolling_max = series.rolling(3)?.max()?;
assert!((rolling_min.values()[2] - 1.0).abs() < 1e-10);
assert!((rolling_max.values()[2] - 3.0).abs() < 1e-10);
assert!((rolling_min.values()[3] - 2.0).abs() < 1e-10);
assert!((rolling_max.values()[3] - 4.0).abs() < 1e-10);
let rolling_std = series.rolling(3)?.std(1)?;
assert!(rolling_std.values()[0].is_nan());
assert!(rolling_std.values()[1].is_nan());
assert!((rolling_std.values()[2] - 1.0).abs() < 1e-10);
let rolling_median = series.rolling(3)?.median()?;
assert!((rolling_median.values()[2] - 2.0).abs() < 1e-10);
assert!((rolling_median.values()[3] - 3.0).abs() < 1e-10);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_series_expanding_operations() -> Result<()> {
let data = [10.0, 20.0, 30.0, 40.0, 50.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let expanding_mean = series.expanding(2)?.mean()?;
assert_eq!(expanding_mean.len(), 5);
assert!(expanding_mean.values()[0].is_nan());
assert!((expanding_mean.values()[1] - 15.0).abs() < 1e-10);
assert!((expanding_mean.values()[2] - 20.0).abs() < 1e-10); assert!((expanding_mean.values()[3] - 25.0).abs() < 1e-10);
let expanding_sum = series.expanding(2)?.sum()?;
assert!(expanding_sum.values()[0].is_nan());
assert!((expanding_sum.values()[1] - 30.0).abs() < 1e-10);
assert!((expanding_sum.values()[2] - 60.0).abs() < 1e-10);
assert!((expanding_sum.values()[3] - 100.0).abs() < 1e-10);
let expanding_min = series.expanding(1)?.min()?;
let expanding_max = series.expanding(1)?.max()?;
assert!((expanding_min.values()[0] - 10.0).abs() < 1e-10);
assert!((expanding_min.values()[1] - 10.0).abs() < 1e-10);
assert!((expanding_min.values()[2] - 10.0).abs() < 1e-10);
assert!((expanding_max.values()[0] - 10.0).abs() < 1e-10);
assert!((expanding_max.values()[1] - 20.0).abs() < 1e-10);
assert!((expanding_max.values()[2] - 30.0).abs() < 1e-10);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_series_ewm_operations() -> Result<()> {
let data = [10.0, 20.0, 30.0, 40.0, 50.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let ewm_mean = series.ewm().alpha(0.5)?.mean()?;
assert_eq!(ewm_mean.len(), 5);
assert!((ewm_mean.values()[0] - 10.0).abs() < 1e-10);
assert!((ewm_mean.values()[1] - 15.0).abs() < 1e-10);
assert!((ewm_mean.values()[2] - 22.5).abs() < 1e-10);
let ewm_span = series.ewm().span(3).mean()?;
assert_eq!(ewm_span.len(), 5);
assert!((ewm_span.values()[0] - 10.0).abs() < 1e-10);
let ewm_std = series.ewm().alpha(0.5)?.std(1)?;
assert_eq!(ewm_std.len(), 5);
assert!(ewm_std.values()[0].is_nan());
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_rolling_custom_functions() -> Result<()> {
let data = [1.0, 2.0, 3.0, 4.0, 5.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let rolling_range = series.rolling(3)?.apply(|window| {
let min_val = window.iter().fold(f64::INFINITY, |a, &b| a.min(b));
let max_val = window.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
max_val - min_val
})?;
assert_eq!(rolling_range.len(), 3); assert!((rolling_range.values()[0] - 2.0).abs() < 1e-10); assert!((rolling_range.values()[1] - 2.0).abs() < 1e-10); assert!((rolling_range.values()[2] - 2.0).abs() < 1e-10);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_rolling_quantiles() -> Result<()> {
let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let rolling_median = series.rolling(3)?.quantile(0.5)?;
assert!((rolling_median.values()[2] - 2.0).abs() < 1e-10); assert!((rolling_median.values()[3] - 3.0).abs() < 1e-10);
let rolling_q75 = series.rolling(3)?.quantile(0.75)?;
assert!((rolling_q75.values()[2] - 3.0).abs() < 1e-10); assert!((rolling_q75.values()[3] - 4.0).abs() < 1e-10);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_dataframe_rolling_operations() -> Result<()> {
let mut df = DataFrame::new();
let values = ["10", "20", "30", "40", "50"];
let dates = vec![
"2024-01-01",
"2024-01-02",
"2024-01-03",
"2024-01-04",
"2024-01-05",
];
let date_series = Series::new(
dates.into_iter().map(|s| s.to_string()).collect(),
Some("Date".to_string()),
)?;
let value_series = Series::new(
values.into_iter().map(|s| s.to_string()).collect(),
Some("Value".to_string()),
)?;
df.add_column("Date".to_string(), date_series)?;
df.add_column("Value".to_string(), value_series)?;
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_window_edge_cases() -> Result<()> {
let data = [1.0, 2.0, 3.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
assert!(series.rolling(0).is_err());
let rolling = series.rolling(2)?;
assert!(rolling.quantile(1.5).is_err());
assert!(rolling.quantile(-0.1).is_err());
assert!(series.ewm().alpha(0.0).is_err());
assert!(series.ewm().alpha(1.5).is_err());
let empty_series: Series<f64> = Series::new(vec![], Some("empty".to_string()))?;
let rolling_result = empty_series.rolling(3)?.mean()?;
assert_eq!(rolling_result.len(), 0);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_rolling_min_periods() -> Result<()> {
let data = [1.0, 2.0, 3.0, 4.0, 5.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let rolling_min_periods = series.rolling(5)?.min_periods(3).mean()?;
assert_eq!(rolling_min_periods.len(), 5);
assert!(rolling_min_periods.values()[0].is_nan());
assert!(rolling_min_periods.values()[1].is_nan());
assert!((rolling_min_periods.values()[2] - 2.0).abs() < 1e-10);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_centered_rolling() -> Result<()> {
let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let rolling_centered = series.rolling(3)?.center(true).mean()?;
assert_eq!(rolling_centered.len(), 7);
assert!(!rolling_centered.values().iter().all(|&x| x.is_nan()));
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_window_count_operation() -> Result<()> {
let data = [1.0, 2.0, 3.0, 4.0, 5.0];
let series = Series::new(data.to_vec(), Some("test".to_string()))?;
let rolling_count = series.rolling(3)?.count()?;
assert_eq!(rolling_count.len(), 5);
assert_eq!(rolling_count.values()[2], 3);
assert_eq!(rolling_count.values()[3], 3);
assert_eq!(rolling_count.values()[4], 3);
Ok(())
}
#[test]
#[allow(clippy::result_large_err)]
fn test_large_dataset_performance() -> Result<()> {
let large_data: Vec<f64> = (0..1000).map(|i| i as f64).collect();
let series = Series::new(large_data, Some("large".to_string()))?;
let start = std::time::Instant::now();
let _rolling_mean = series.rolling(50)?.mean()?;
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"Rolling operation took too long: {duration:?}"
);
let start = std::time::Instant::now();
let _expanding_mean = series.expanding(10)?.mean()?;
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"Expanding operation took too long: {duration:?}"
);
let start = std::time::Instant::now();
let _ewm_mean = series.ewm().span(20).mean()?;
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"EWM operation took too long: {duration:?}"
);
Ok(())
}