use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
use scirs2_core::numeric::{Float, Zero};
use crate::error::{LinalgError, LinalgResult};
#[allow(dead_code)]
pub fn covariancematrix<F>(data: &ArrayView2<F>, ddof: Option<usize>) -> LinalgResult<Array2<F>>
where
F: Float
+ Zero
+ scirs2_core::numeric::FromPrimitive
+ Send
+ Sync
+ scirs2_core::ndarray::ScalarOperand
+ 'static,
{
let n_samples = data.nrows();
let n_features = data.ncols();
if n_samples <= 1 {
return Err(LinalgError::InvalidInputError(
"Need at least 2 samples to compute covariance".to_string(),
));
}
let ddof = ddof.unwrap_or(1);
if ddof >= n_samples {
return Err(LinalgError::InvalidInputError(format!(
"Delta degrees of freedom ({ddof}) must be less than sample count ({n_samples})"
)));
}
let mean = data.mean_axis(Axis(0)).expect("Operation failed");
let centered = data.to_owned() - &mean;
let mut cov = Array2::zeros((n_features, n_features));
let normalizer = F::from(n_samples - ddof).expect("Operation failed");
for i in 0..n_features {
for j in 0..=i {
let mut sum = F::zero();
for k in 0..n_samples {
sum = sum + centered[[k, i]] * centered[[k, j]];
}
let val = sum / normalizer;
cov[[i, j]] = val;
cov[[j, i]] = val; }
}
Ok(cov)
}
#[allow(dead_code)]
pub fn correlationmatrix<F>(data: &ArrayView2<F>, ddof: Option<usize>) -> LinalgResult<Array2<F>>
where
F: Float
+ Zero
+ scirs2_core::numeric::FromPrimitive
+ Send
+ Sync
+ scirs2_core::ndarray::ScalarOperand
+ 'static,
{
let cov = covariancematrix(data, ddof)?;
let n_features = cov.nrows();
let mut std_devs = Array1::zeros(n_features);
for i in 0..n_features {
std_devs[i] = cov[[i, i]].sqrt();
}
let mut corr = Array2::zeros((n_features, n_features));
for i in 0..n_features {
for j in 0..n_features {
if std_devs[i] > F::epsilon() && std_devs[j] > F::epsilon() {
corr[[i, j]] = cov[[i, j]] / (std_devs[i] * std_devs[j]);
} else {
corr[[i, j]] = F::zero();
}
}
}
for i in 0..n_features {
corr[[i, i]] = F::one();
}
Ok(corr)
}
#[allow(dead_code)]
pub fn mahalanobis_distance<F>(
x: &ArrayView1<F>,
mean: &ArrayView1<F>,
cov: &ArrayView2<F>,
) -> LinalgResult<F>
where
F: Float
+ Zero
+ scirs2_core::numeric::One
+ scirs2_core::numeric::NumAssign
+ std::iter::Sum
+ Send
+ Sync
+ scirs2_core::ndarray::ScalarOperand
+ 'static,
{
if x.len() != mean.len() || x.len() != cov.nrows() || x.len() != cov.ncols() {
return Err(LinalgError::ShapeError(format!(
"Incompatible dimensions: x: {:?}, mean: {:?}, cov: {:?}",
x.shape(),
mean.shape(),
cov.shape()
)));
}
let dev = x - mean;
let inv_dev = crate::solve::solve(cov, &dev.view(), None)?;
let dist_sq = dev.dot(&inv_dev);
Ok(dist_sq.sqrt())
}