#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
use core::cmp::Ordering::Equal;
use core::fmt::{Debug, Display, Formatter, Result};
use num_traits::Float;
use crate::algorithms::regression::PolynomialDegree;
use crate::evaluation::diagnostics::Diagnostics;
use crate::math::distance::DistanceMetric;
#[derive(Debug, Clone, PartialEq)]
pub struct LoessResult<T> {
pub x: Vec<T>,
pub dimensions: usize,
pub distance_metric: DistanceMetric<T>,
pub polynomial_degree: PolynomialDegree,
pub y: Vec<T>,
pub standard_errors: Option<Vec<T>>,
pub confidence_lower: Option<Vec<T>>,
pub confidence_upper: Option<Vec<T>>,
pub prediction_lower: Option<Vec<T>>,
pub prediction_upper: Option<Vec<T>>,
pub residuals: Option<Vec<T>>,
pub robustness_weights: Option<Vec<T>>,
pub diagnostics: Option<Diagnostics<T>>,
pub iterations_used: Option<usize>,
pub fraction_used: T,
pub cv_scores: Option<Vec<T>>,
pub enp: Option<T>,
pub trace_hat: Option<T>,
pub delta1: Option<T>,
pub delta2: Option<T>,
pub residual_scale: Option<T>,
pub leverage: Option<Vec<T>>,
}
impl<T: Float> LoessResult<T> {
pub fn has_confidence_intervals(&self) -> bool {
self.confidence_lower.is_some() && self.confidence_upper.is_some()
}
pub fn has_prediction_intervals(&self) -> bool {
self.prediction_lower.is_some() && self.prediction_upper.is_some()
}
pub fn has_cv_scores(&self) -> bool {
self.cv_scores.is_some()
}
pub fn best_cv_score(&self) -> Option<T> {
self.cv_scores.as_ref().and_then(|scores| {
scores
.iter()
.copied()
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Equal))
})
}
}
impl<T: Float + Display + Debug> Display for LoessResult<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
writeln!(f, "Summary:")?;
let n = self.y.len();
writeln!(f, " Data points: {}", n)?;
writeln!(f, " Dimensions: {}", self.dimensions)?;
writeln!(f, " Distance: {:?}", self.distance_metric)?;
writeln!(f, " Degree: {:?}", self.polynomial_degree)?;
writeln!(f, " Fraction: {}", self.fraction_used)?;
if let Some(iters) = self.iterations_used {
writeln!(f, " Iterations: {}", iters)?;
}
if self.robustness_weights.is_some() {
writeln!(f, " Robustness: Applied")?;
}
if self.has_cv_scores() {
if let Some(best_score) = self.best_cv_score() {
writeln!(f, " Best CV score: {}", best_score)?;
}
}
writeln!(f)?;
if let Some(diag) = &self.diagnostics {
writeln!(f, "{}", diag)?;
}
writeln!(f, "Smoothed Data:")?;
let has_std_err = self.standard_errors.is_some();
let has_conf = self.has_confidence_intervals();
let has_pred = self.has_prediction_intervals();
let has_resid = self.residuals.is_some();
let has_weights = self.robustness_weights.is_some();
if self.dimensions == 1 {
write!(f, "{:>8} {:>12}", "X", "Y_smooth")?;
} else {
write!(f, "{:>8} {:>12}", "X (nD)", "Y_smooth")?;
}
if has_std_err {
write!(f, " {:>12}", "Std_Err")?;
}
if has_conf {
write!(f, " {:>12} {:>12}", "Conf_Lower", "Conf_Upper")?;
}
if has_pred {
write!(f, " {:>12} {:>12}", "Pred_Lower", "Pred_Upper")?;
}
if has_resid {
write!(f, " {:>12}", "Residual")?;
}
if has_weights {
write!(f, " {:>10}", "Rob_Weight")?;
}
writeln!(f)?;
let line_width = 21
+ if has_std_err { 13 } else { 0 }
+ if has_conf { 26 } else { 0 }
+ if has_pred { 26 } else { 0 }
+ if has_resid { 13 } else { 0 }
+ if has_weights { 11 } else { 0 };
writeln!(f, "{:-<width$}", "", width = line_width)?;
let n = self.x.len();
let show_all = n <= 20;
let rows_to_show: Vec<usize> = if show_all {
(0..n).collect()
} else {
(0..10).chain(n - 10..n).collect()
};
let mut prev_idx = 0;
for (i, &idx) in rows_to_show.iter().enumerate() {
if i > 0 && idx != prev_idx + 1 {
writeln!(f, "{:>8}", "...")?;
}
prev_idx = idx;
if self.dimensions == 1 {
write!(f, "{:>8.2} {:>12.6}", self.x[idx], self.y[idx])?;
} else {
write!(f, "{:>8.2} {:>12.6}", "[...]", self.y[idx])?;
}
if has_std_err {
if let Some(se) = &self.standard_errors {
write!(f, " {:>12.6}", se[idx])?;
}
}
if has_conf {
if let (Some(lower), Some(upper)) = (&self.confidence_lower, &self.confidence_upper)
{
write!(f, " {:>12.6} {:>12.6}", lower[idx], upper[idx])?;
}
}
if has_pred {
if let (Some(lower), Some(upper)) = (&self.prediction_lower, &self.prediction_upper)
{
write!(f, " {:>12.6} {:>12.6}", lower[idx], upper[idx])?;
}
}
if has_resid {
if let Some(resid) = &self.residuals {
write!(f, " {:>12.6}", resid[idx])?;
}
}
if has_weights {
if let Some(weights) = &self.robustness_weights {
write!(f, " {:>10.4}", weights[idx])?;
}
}
writeln!(f)?;
}
Ok(())
}
}