use chrono::NaiveDate;
use pandrs::error::{Error, Result};
use pandrs::temporal::{date_range, Frequency, TimeSeries};
use pandrs::NA;
use std::str::FromStr;
#[allow(clippy::result_large_err)]
#[allow(clippy::result_large_err)]
fn main() -> Result<()> {
println!("=== Example of Window Operations ===\n");
let start_date = NaiveDate::from_str("2023-01-01").map_err(|e| Error::Format(e.to_string()))?;
let end_date = NaiveDate::from_str("2023-01-20").map_err(|e| Error::Format(e.to_string()))?;
let dates = date_range(start_date, end_date, Frequency::Daily, true)?;
let mut values = Vec::with_capacity(dates.len());
for i in 0..dates.len() {
let value = 100.0 + i as f64 * 2.0 + (i as f64 * 0.5).sin() * 5.0;
if i % 7 == 0 {
values.push(NA::NA);
} else {
values.push(NA::Value(value));
}
}
let time_series = TimeSeries::new(values, dates, Some("sample_data".to_string()))?;
println!("=== Original Data ===");
println!("Date\t\tValue");
for i in 0..time_series.len() {
let date = time_series.timestamps()[i];
let value = match time_series.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
println!("{date}\t{value}");
}
println!("\n=== Fixed-length Window Operations (Window Size: 3) ===");
let window_size = 3;
let rolling_mean = time_series.rolling(window_size)?.mean()?;
let rolling_sum = time_series.rolling(window_size)?.sum()?;
let rolling_std = time_series.rolling(window_size)?.std(1)?;
let rolling_min = time_series.rolling(window_size)?.min()?;
let rolling_max = time_series.rolling(window_size)?.max()?;
println!(
"Date\t\tOriginal Data\tMoving Avg\tMoving Sum\tMoving Std Dev\tMoving Min\tMoving Max"
);
for i in 0..time_series.len() {
let date = time_series.timestamps()[i];
let original = match time_series.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let mean = match rolling_mean.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let sum = match rolling_sum.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let std = match rolling_std.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let min = match rolling_min.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let max = match rolling_max.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
println!("{date}\t{original}\t{mean}\t{sum}\t{std}\\t{min}\t{max}");
}
println!("\n=== Expanding Window Operations (Minimum Periods: 3) ===");
let min_periods = 3; let expanding_mean = time_series.expanding(min_periods)?.mean()?;
let expanding_sum = time_series.expanding(min_periods)?.sum()?;
println!("Date\t\tOriginal Data\tExpanding Avg\tExpanding Sum");
for i in 0..time_series.len() {
let date = time_series.timestamps()[i];
let original = match time_series.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let exp_mean = match expanding_mean.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let exp_sum = match expanding_sum.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
println!("{date}\t{original}\t{exp_mean}\t{exp_sum}");
}
println!("\n=== Exponentially Weighted Moving Operations (span: 5) ===");
let span = 5; let ewm_mean = time_series.ewm(Some(span), None, false)?.mean()?;
let ewm_std = time_series.ewm(Some(span), None, false)?.std(1)?;
let alpha = 0.3; let ewm_mean_alpha = time_series.ewm(None, Some(alpha), false)?.mean()?;
println!("Date\t\tOriginal Data\tEWM Avg (span=5)\tEWM Std Dev\tEWM Avg (alpha=0.3)");
for i in 0..time_series.len() {
let date = time_series.timestamps()[i];
let original = match time_series.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let ewm = match ewm_mean.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let ewm_s = match ewm_std.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let ewm_a = match ewm_mean_alpha.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
println!("{date}\t{original}\\t{ewm}\\t{ewm_s}\\t{ewm_a}");
}
println!("\n=== Example of Custom Aggregation Function (Median) ===");
let median = |values: &[f64]| -> f64 {
let mut sorted = values.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mid = sorted.len() / 2;
if sorted.len() % 2 == 0 {
(sorted[mid - 1] + sorted[mid]) / 2.0
} else {
sorted[mid]
}
};
let rolling_median = time_series
.rolling(window_size)?
.aggregate(median, Some(1))?;
println!("Date\t\tOriginal Data\tMoving Median");
for i in 0..time_series.len() {
let date = time_series.timestamps()[i];
let original = match time_series.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
let med = match rolling_median.values()[i] {
NA::Value(v) => format!("{v:.2}"),
NA::NA => "NA".to_string(),
};
println!("{date}\t{original}\t{med}");
}
println!("\n=== Example of Window Operations Complete ===");
Ok(())
}