nalgebra/src/base/statistics.rs

399 lines
11 KiB
Rust
Raw Normal View History

2021-07-16 12:56:58 +08:00
use std::mem::MaybeUninit;
2019-03-23 21:29:07 +08:00
use crate::allocator::Allocator;
2020-03-21 19:16:46 +08:00
use crate::storage::Storage;
2021-04-11 17:00:38 +08:00
use crate::{Const, DefaultAllocator, Dim, Matrix, OVector, RowOVector, Scalar, VectorSlice, U1};
2020-03-21 19:16:46 +08:00
use num::Zero;
use simba::scalar::{ClosedAdd, Field, SupersetOf};
2020-11-15 23:57:49 +08:00
/// # Folding on columns and rows
2021-07-17 12:17:56 +08:00
impl<T, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Returns a row vector where each element is the result of the application of `f` on the
/// corresponding column of the original matrix.
#[inline]
#[must_use]
2020-03-21 19:16:46 +08:00
pub fn compress_rows(
&self,
2021-04-11 17:00:38 +08:00
f: impl Fn(VectorSlice<T, R, S::RStride, S::CStride>) -> T,
) -> RowOVector<T, C>
2020-03-21 19:16:46 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, U1, C>,
2020-03-21 19:16:46 +08:00
{
let ncols = self.data.shape().1;
2021-07-16 12:56:58 +08:00
let mut res = RowOVector::new_uninitialized_generic(Const::<1>, ncols);
for i in 0..ncols.value() {
2020-11-15 23:57:49 +08:00
// TODO: avoid bound checking of column.
2020-03-21 19:16:46 +08:00
unsafe {
2021-07-16 14:53:28 +08:00
*res.get_unchecked_mut((0, i)) = MaybeUninit::new(f(self.column(i)));
2020-03-21 19:16:46 +08:00
}
}
2021-07-16 14:53:28 +08:00
unsafe { res.assume_init() }
}
/// Returns a column vector where each element is the result of the application of `f` on the
/// corresponding column of the original matrix.
///
/// This is the same as `self.compress_rows(f).transpose()`.
#[inline]
#[must_use]
2020-03-21 19:16:46 +08:00
pub fn compress_rows_tr(
&self,
2021-04-11 17:00:38 +08:00
f: impl Fn(VectorSlice<T, R, S::RStride, S::CStride>) -> T,
) -> OVector<T, C>
2020-03-21 19:16:46 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, C>,
2020-03-21 19:16:46 +08:00
{
let ncols = self.data.shape().1;
2021-07-16 12:56:58 +08:00
let mut res = Matrix::new_uninitialized_generic(ncols, Const::<1>);
for i in 0..ncols.value() {
2020-11-15 23:57:49 +08:00
// TODO: avoid bound checking of column.
2020-03-21 19:16:46 +08:00
unsafe {
2021-07-16 12:56:58 +08:00
*res.vget_unchecked_mut(i) = MaybeUninit::new(f(self.column(i)));
2020-03-21 19:16:46 +08:00
}
}
2021-07-16 12:56:58 +08:00
unsafe { res.assume_init() }
}
/// Returns a column vector resulting from the folding of `f` on each column of this matrix.
#[inline]
#[must_use]
2020-03-21 19:16:46 +08:00
pub fn compress_columns(
&self,
2021-04-11 17:00:38 +08:00
init: OVector<T, R>,
f: impl Fn(&mut OVector<T, R>, VectorSlice<T, R, S::RStride, S::CStride>),
) -> OVector<T, R>
2020-03-21 19:16:46 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator: Allocator<T, R>,
2020-03-21 19:16:46 +08:00
{
for i in 0..self.ncols() {
2021-07-16 14:53:28 +08:00
f(&mut init, self.column(i))
}
2021-07-16 14:53:28 +08:00
init
}
}
2020-11-15 23:57:49 +08:00
/// # Common statistics operations
2021-04-11 17:00:38 +08:00
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/*
*
* Sum computation.
*
*/
/// The sum of all the elements of this matrix.
///
/// # Example
///
/// ```
/// # use nalgebra::Matrix2x3;
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.sum(), 21.0);
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn sum(&self) -> T
2020-11-15 23:57:49 +08:00
where
2021-04-11 17:00:38 +08:00
T: ClosedAdd + Zero,
2020-11-15 23:57:49 +08:00
{
2021-04-11 17:00:38 +08:00
self.iter().cloned().fold(T::zero(), |a, b| a + b)
}
/// The sum of all the rows of this matrix.
2019-02-03 18:06:06 +08:00
///
/// Use `.row_variance_tr` if you need the result in a column vector instead.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, Matrix3x2};
/// # use nalgebra::{RowVector2, RowVector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.row_sum(), RowVector3::new(5.0, 7.0, 9.0));
///
/// let mint = Matrix3x2::new(1,2,3,4,5,6);
/// assert_eq!(mint.row_sum(), RowVector2::new(9,12));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn row_sum(&self) -> RowOVector<T, C>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: ClosedAdd + Zero,
DefaultAllocator: Allocator<T, U1, C>,
2020-04-06 00:49:48 +08:00
{
self.compress_rows(|col| col.sum())
}
/// The sum of all the rows of this matrix. The result is transposed and returned as a column vector.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, Matrix3x2};
/// # use nalgebra::{Vector2, Vector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.row_sum_tr(), Vector3::new(5.0, 7.0, 9.0));
///
/// let mint = Matrix3x2::new(1,2,3,4,5,6);
/// assert_eq!(mint.row_sum_tr(), Vector2::new(9,12));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn row_sum_tr(&self) -> OVector<T, C>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: ClosedAdd + Zero,
DefaultAllocator: Allocator<T, C>,
2020-04-06 00:49:48 +08:00
{
self.compress_rows_tr(|col| col.sum())
}
/// The sum of all the columns of this matrix.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, Matrix3x2};
/// # use nalgebra::{Vector2, Vector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.column_sum(), Vector2::new(6.0, 15.0));
///
/// let mint = Matrix3x2::new(1,2,3,4,5,6);
/// assert_eq!(mint.column_sum(), Vector3::new(3,7,11));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn column_sum(&self) -> OVector<T, R>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: ClosedAdd + Zero,
DefaultAllocator: Allocator<T, R>,
2020-04-06 00:49:48 +08:00
{
let nrows = self.data.shape().0;
2021-04-11 17:00:38 +08:00
self.compress_columns(OVector::zeros_generic(nrows, Const::<1>), |out, col| {
*out += col;
})
}
/*
*
* Variance computation.
*
*/
/// The variance of all the elements of this matrix.
///
/// # Example
///
/// ```
2019-02-03 18:06:06 +08:00
/// # #[macro_use] extern crate approx;
/// # use nalgebra::Matrix2x3;
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
2019-02-03 18:06:06 +08:00
/// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8);
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn variance(&self) -> T
2020-11-15 23:57:49 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
2020-11-15 23:57:49 +08:00
{
2020-11-16 19:11:24 +08:00
if self.is_empty() {
2021-04-11 17:00:38 +08:00
T::zero()
} else {
2021-04-11 17:00:38 +08:00
let val = self.iter().cloned().fold((T::zero(), T::zero()), |a, b| {
2020-03-21 19:16:46 +08:00
(a.0 + b.inlined_clone() * b.inlined_clone(), a.1 + b)
});
2021-04-11 17:00:38 +08:00
let denom = T::one() / crate::convert::<_, T>(self.len() as f64);
let vd = val.1 * denom.inlined_clone();
val.0 * denom - vd.inlined_clone() * vd
}
}
/// The variance of all the rows of this matrix.
2019-02-03 18:06:06 +08:00
///
/// Use `.row_variance_tr` if you need the result in a column vector instead.
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, RowVector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
2019-02-03 18:06:06 +08:00
/// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn row_variance(&self) -> RowOVector<T, C>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
DefaultAllocator: Allocator<T, U1, C>,
2020-04-06 00:49:48 +08:00
{
self.compress_rows(|col| col.variance())
}
/// The variance of all the rows of this matrix. The result is transposed and returned as a column vector.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, Vector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
2019-02-03 18:06:06 +08:00
/// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn row_variance_tr(&self) -> OVector<T, C>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
DefaultAllocator: Allocator<T, C>,
2020-04-06 00:49:48 +08:00
{
self.compress_rows_tr(|col| col.variance())
}
/// The variance of all the columns of this matrix.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate approx;
/// # use nalgebra::{Matrix2x3, Vector2};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8);
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn column_variance(&self) -> OVector<T, R>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
DefaultAllocator: Allocator<T, R>,
2020-04-06 00:49:48 +08:00
{
let (nrows, ncols) = self.data.shape();
let mut mean = self.column_mean();
mean.apply(|e| -(e.inlined_clone() * e));
2021-04-11 17:00:38 +08:00
let denom = T::one() / crate::convert::<_, T>(ncols.value() as f64);
self.compress_columns(mean, |out, col| {
for i in 0..nrows.value() {
unsafe {
let val = col.vget_unchecked(i);
2020-03-21 19:16:46 +08:00
*out.vget_unchecked_mut(i) +=
denom.inlined_clone() * val.inlined_clone() * val.inlined_clone()
}
}
})
}
/*
*
* Mean computation.
*
*/
/// The mean of all the elements of this matrix.
///
/// # Example
///
/// ```
/// # use nalgebra::Matrix2x3;
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.mean(), 3.5);
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn mean(&self) -> T
2020-11-15 23:57:49 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
2020-11-15 23:57:49 +08:00
{
2020-11-16 19:11:24 +08:00
if self.is_empty() {
2021-04-11 17:00:38 +08:00
T::zero()
} else {
2019-03-23 21:29:07 +08:00
self.sum() / crate::convert(self.len() as f64)
}
}
/// The mean of all the rows of this matrix.
///
2019-02-03 18:06:06 +08:00
/// Use `.row_mean_tr` if you need the result in a column vector instead.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, RowVector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn row_mean(&self) -> RowOVector<T, C>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
DefaultAllocator: Allocator<T, U1, C>,
2020-04-06 00:49:48 +08:00
{
self.compress_rows(|col| col.mean())
}
/// The mean of all the rows of this matrix. The result is transposed and returned as a column vector.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, Vector3};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn row_mean_tr(&self) -> OVector<T, C>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
DefaultAllocator: Allocator<T, C>,
2020-04-06 00:49:48 +08:00
{
self.compress_rows_tr(|col| col.mean())
}
/// The mean of all the columns of this matrix.
///
/// # Example
///
/// ```
/// # use nalgebra::{Matrix2x3, Vector2};
///
/// let m = Matrix2x3::new(1.0, 2.0, 3.0,
/// 4.0, 5.0, 6.0);
/// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0));
/// ```
#[inline]
#[must_use]
2021-04-11 17:00:38 +08:00
pub fn column_mean(&self) -> OVector<T, R>
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
T: Field + SupersetOf<f64>,
DefaultAllocator: Allocator<T, R>,
2020-04-06 00:49:48 +08:00
{
let (nrows, ncols) = self.data.shape();
2021-04-11 17:00:38 +08:00
let denom = T::one() / crate::convert::<_, T>(ncols.value() as f64);
self.compress_columns(OVector::zeros_generic(nrows, Const::<1>), |out, col| {
out.axpy(denom.inlined_clone(), &col, T::one())
})
}
}