use crate::core::error::{Error, Result};
use crate::dataframe::base::DataFrame;
use crate::series::{Series, WindowExt, WindowOps};
pub trait DataFrameWindowExt {
fn rolling(
&self,
window_size: usize,
column_name: &str,
operation: &str,
new_column_name: Option<&str>,
) -> Result<DataFrame>;
fn expanding(
&self,
min_periods: usize,
column_name: &str,
operation: &str,
new_column_name: Option<&str>,
) -> Result<DataFrame>;
fn ewm(
&self,
column_name: &str,
operation: &str,
span: Option<usize>,
alpha: Option<f64>,
new_column_name: Option<&str>,
) -> Result<DataFrame>;
}
impl DataFrameWindowExt for DataFrame {
fn rolling(
&self,
window_size: usize,
column_name: &str,
operation: &str,
new_column_name: Option<&str>,
) -> Result<DataFrame> {
let column = self.get_column_as_f64_legacy(column_name)?;
let rolling = column.rolling(window_size)?;
let result_series = match operation.to_lowercase().as_str() {
"mean" => rolling.mean()?,
"sum" => rolling.sum()?,
"std" => rolling.std(1)?,
"var" => rolling.var(1)?,
"min" => rolling.min()?,
"max" => rolling.max()?,
"median" => rolling.median()?,
_ => {
return Err(Error::InvalidValue(format!(
"Unsupported rolling operation: {}",
operation
)))
}
};
let mut new_df = self.clone();
let default_name = format!("{}_{}", column_name, operation);
let result_column_name = new_column_name.unwrap_or(&default_name);
new_df.add_column(
result_column_name.to_string(),
result_series.to_string_series()?,
)?;
Ok(new_df)
}
fn expanding(
&self,
min_periods: usize,
column_name: &str,
operation: &str,
new_column_name: Option<&str>,
) -> Result<DataFrame> {
let column = self.get_column_as_f64_legacy(column_name)?;
let expanding = column.expanding(min_periods)?;
let result_series = match operation.to_lowercase().as_str() {
"mean" => expanding.mean()?,
"sum" => expanding.sum()?,
"std" => expanding.std(1)?,
"var" => expanding.var(1)?,
"min" => expanding.min()?,
"max" => expanding.max()?,
"median" => expanding.median()?,
_ => {
return Err(Error::InvalidValue(format!(
"Unsupported expanding operation: {}",
operation
)))
}
};
let mut new_df = self.clone();
let default_name = format!("{}_{}", column_name, operation);
let result_column_name = new_column_name.unwrap_or(&default_name);
new_df.add_column(
result_column_name.to_string(),
result_series.to_string_series()?,
)?;
Ok(new_df)
}
fn ewm(
&self,
column_name: &str,
operation: &str,
span: Option<usize>,
alpha: Option<f64>,
new_column_name: Option<&str>,
) -> Result<DataFrame> {
let column = self.get_column_as_f64_legacy(column_name)?;
let mut ewm = column.ewm();
if let Some(span_val) = span {
ewm = ewm.span(span_val);
} else if let Some(alpha_val) = alpha {
ewm = ewm.alpha(alpha_val)?;
} else {
return Err(Error::InvalidValue(
"Must specify either span or alpha for EWM".to_string(),
));
}
let result_series = match operation.to_lowercase().as_str() {
"mean" => ewm.mean()?,
"std" => ewm.std(1)?,
"var" => ewm.var(1)?,
_ => {
return Err(Error::InvalidValue(format!(
"Unsupported EWM operation: {}",
operation
)))
}
};
let mut new_df = self.clone();
let default_name = format!("{}_{}", column_name, operation);
let result_column_name = new_column_name.unwrap_or(&default_name);
new_df.add_column(
result_column_name.to_string(),
result_series.to_string_series()?,
)?;
Ok(new_df)
}
}
impl DataFrame {
fn get_column_as_f64_legacy(&self, column_name: &str) -> Result<Series<f64>> {
if let Ok(string_series) = self.get_column::<String>(column_name) {
let mut f64_values = Vec::new();
for value in string_series.values() {
match value.parse::<f64>() {
Ok(val) => f64_values.push(val),
Err(_) => {
return Err(Error::InvalidValue(format!(
"Cannot convert column '{}' to numeric",
column_name
)))
}
}
}
return Series::new(f64_values, Some(column_name.to_string()));
}
Err(Error::ColumnNotFound(column_name.to_string()))
}
}