use nalgebra::DMatrix;
use std::fmt;
pub mod matrix_solvers;
pub mod spatial_lag;
pub mod spatial_error;
pub mod gwr;
pub mod diagnostics;
#[cfg(test)]
pub mod test_data;
pub use spatial_lag::SpatialLagRegression;
pub use spatial_error::SpatialErrorRegression;
pub use gwr::GeographicallyWeightedRegression;
pub type RegressionResult<T> = Result<T, String>;
#[derive(Debug, Clone)]
pub struct EffectDecomposition {
pub direct_effects: Vec<f64>,
pub indirect_effects: Vec<f64>,
pub total_effects: Vec<f64>,
pub direct_se: Vec<f64>,
pub indirect_se: Vec<f64>,
pub total_se: Vec<f64>,
pub direct_pvalues: Vec<f64>,
pub indirect_pvalues: Vec<f64>,
pub total_pvalues: Vec<f64>,
}
impl EffectDecomposition {
pub fn new(
direct: Vec<f64>,
indirect: Vec<f64>,
total: Vec<f64>,
direct_se: Vec<f64>,
indirect_se: Vec<f64>,
total_se: Vec<f64>,
) -> RegressionResult<Self> {
let n = direct.len();
if indirect.len() != n || total.len() != n || direct_se.len() != n
|| indirect_se.len() != n || total_se.len() != n
{
return Err("Effect decomposition vectors must have equal length".to_string());
}
let direct_pvalues = direct
.iter()
.zip(direct_se.iter())
.map(|(effect, se)| {
if *se > 0.0 {
crate::weights::two_tailed_normal_p(effect / se)
} else {
1.0
}
})
.collect();
let indirect_pvalues = indirect
.iter()
.zip(indirect_se.iter())
.map(|(effect, se)| {
if *se > 0.0 {
crate::weights::two_tailed_normal_p(effect / se)
} else {
1.0
}
})
.collect();
let total_pvalues = total
.iter()
.zip(total_se.iter())
.map(|(effect, se)| {
if *se > 0.0 {
crate::weights::two_tailed_normal_p(effect / se)
} else {
1.0
}
})
.collect();
Ok(EffectDecomposition {
direct_effects: direct,
indirect_effects: indirect,
total_effects: total,
direct_se,
indirect_se,
total_se,
direct_pvalues,
indirect_pvalues,
total_pvalues,
})
}
}
#[derive(Debug, Clone)]
pub struct ConvergenceDiagnostics {
pub converged: bool,
pub iterations: usize,
pub max_iterations: usize,
pub final_gradient_norm: f64,
pub tolerance: f64,
pub stopping_reason: String,
}
#[derive(Debug, Clone)]
pub struct PreFlightDiagnostics {
pub design_matrix_condition_number: f64,
pub design_matrix_rank: usize,
pub response_variance: f64,
pub design_warnings: Vec<String>,
pub response_warnings: Vec<String>,
pub weights_warnings: Vec<String>,
pub can_proceed: bool,
}
impl fmt::Display for PreFlightDiagnostics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"PreFlightDiagnostics {{\n condition_number: {:.2e},\n rank: {},\n response_variance: {:.6},\n can_proceed: {},\n warnings: {:?}\n}}",
self.design_matrix_condition_number,
self.design_matrix_rank,
self.response_variance,
self.can_proceed,
[
self.design_warnings.clone(),
self.response_warnings.clone(),
self.weights_warnings.clone()
]
.concat()
)
}
}
#[derive(Debug, Clone)]
pub struct ResidualSummary {
pub mean: f64,
pub std_dev: f64,
pub min: f64,
pub q25: f64,
pub median: f64,
pub q75: f64,
pub max: f64,
pub morans_i: f64,
pub morans_i_pvalue: f64,
pub interpretation: String,
}
impl fmt::Display for ResidualSummary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ResidualSummary {{\n mean: {:.6},\n std_dev: {:.6},\n range: [{:.6}, {:.6}],\n morans_i: {:.6} (p={:.6}),\n interpretation: \"{}\"\n}}",
self.mean, self.std_dev, self.min, self.max, self.morans_i, self.morans_i_pvalue, self.interpretation
)
}
}
#[derive(Debug, Clone)]
pub struct RegressionResultBase {
pub coefficients: Vec<f64>,
pub standard_errors: Vec<f64>,
pub t_statistics: Vec<f64>,
pub p_values: Vec<f64>,
pub fitted: Vec<f64>,
pub residuals: Vec<f64>,
pub rss: f64,
pub tss: f64,
pub r_squared: f64,
pub r_squared_adj: f64,
pub log_likelihood: f64,
pub aic: f64,
pub n_obs: usize,
pub n_params: usize,
pub preflight: PreFlightDiagnostics,
pub convergence: Option<ConvergenceDiagnostics>,
pub residual_summary: ResidualSummary,
}
#[derive(Debug, Clone)]
pub struct SpatialLagResult {
pub base: RegressionResultBase,
pub rho: f64,
pub rho_se: f64,
pub rho_t: f64,
pub rho_pvalue: f64,
pub effects: Option<EffectDecomposition>,
}
#[derive(Debug, Clone)]
pub struct SpatialErrorResult {
pub base: RegressionResultBase,
pub lambda: f64,
pub lambda_se: f64,
pub lambda_t: f64,
pub lambda_pvalue: f64,
pub method: String, }
#[derive(Debug, Clone)]
pub struct GWRResult {
pub local_coefficients: DMatrix<f64>,
pub local_standard_errors: DMatrix<f64>,
pub local_t_statistics: DMatrix<f64>,
pub local_p_values: DMatrix<f64>,
pub fitted: Vec<f64>,
pub residuals: Vec<f64>,
pub r_squared: f64,
pub aic: f64,
pub bandwidth: f64,
pub aicc: f64,
pub kernel: String, pub n_obs: usize,
pub n_params: usize,
pub coefficient_stability: Vec<f64>,
pub preflight: PreFlightDiagnostics,
}