nalgebra/src/base/statistics.rs

311 lines
9.4 KiB
Rust
Raw Normal View History

2019-03-23 21:29:07 +08:00
use crate::{Scalar, Dim, Matrix, VectorN, RowVectorN, DefaultAllocator, U1, VectorSliceN};
use alga::general::{Field, SupersetOf};
2019-03-23 21:29:07 +08:00
use crate::storage::Storage;
use crate::allocator::Allocator;
Move `Copy` constraint from the definition of `Scalar` to all its use-sites. This should semantically be a no-op, but enables refactorings to use non-Copy scalars on a case-by-case basis. Also, the only instance of a `One + Zero` trait bound was changed into a `Zero + One` bound to match the others. The following sed scripts were used in the refactoring (with each clause added to reduce the error count of `cargo check`): ```bash export RELEVANT_SOURCEFILES="$(find src -name '*.rs') $(find examples -name '*.rs')" for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar,/N: Scalar+Copy,/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Field/N: Scalar + Copy + Field/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Zero/N: Scalar + Copy + Zero/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Closed/N: Scalar + Copy + Closed/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Eq/N: Scalar + Copy + Eq/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + PartialOrd/N: Scalar + Copy + PartialOrd/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: *Scalar + Zero/N: Scalar + Copy + Zero/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + PartialEq/N: Scalar + Copy + PartialEq/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar>/N: Scalar+Copy>/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + $bound/N: Scalar + Copy + $bound/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: *Scalar + $bound/N: Scalar + Copy + $bound/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\): *Scalar,/N\1: Scalar+Copy,/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: *Scalar + $trait/N: Scalar + Copy + $trait/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\): *Scalar + Superset/N\1: Scalar + Copy + Superset/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\): *Scalar + \([a-zA-Z]*Eq\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \([a-zA-Z]*Eq\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(hash::\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar {/N\1: Scalar + Copy {/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Zero\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Bounded\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Lattice\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Meet\|Join\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(fmt::\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Ring\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Hash\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Send\|Sync\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/One + Zero/Zero + One/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Zero\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \($marker\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar>/N\1: Scalar + Copy>/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/Scalar+Copy/Scalar + Copy/' $f; done ```
2019-11-20 04:57:37 +08:00
impl<N: Scalar + Copy, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, 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]
pub fn compress_rows(&self, f: impl Fn(VectorSliceN<N, R, S::RStride, S::CStride>) -> N) -> RowVectorN<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
let ncols = self.data.shape().1;
let mut res = unsafe { RowVectorN::new_uninitialized_generic(U1, ncols) };
for i in 0..ncols.value() {
// FIXME: avoid bound checking of column.
unsafe { *res.get_unchecked_mut((0, i)) = f(self.column(i)); }
}
res
}
/// 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]
pub fn compress_rows_tr(&self, f: impl Fn(VectorSliceN<N, R, S::RStride, S::CStride>) -> N) -> VectorN<N, C>
where DefaultAllocator: Allocator<N, C> {
let ncols = self.data.shape().1;
let mut res = unsafe { VectorN::new_uninitialized_generic(ncols, U1) };
for i in 0..ncols.value() {
// FIXME: avoid bound checking of column.
unsafe { *res.vget_unchecked_mut(i) = f(self.column(i)); }
}
res
}
/// Returns a column vector resulting from the folding of `f` on each column of this matrix.
#[inline]
pub fn compress_columns(&self, init: VectorN<N, R>, f: impl Fn(&mut VectorN<N, R>, VectorSliceN<N, R, S::RStride, S::CStride>)) -> VectorN<N, R>
where DefaultAllocator: Allocator<N, R> {
let mut res = init;
for i in 0..self.ncols() {
f(&mut res, self.column(i))
}
res
}
}
Move `Copy` constraint from the definition of `Scalar` to all its use-sites. This should semantically be a no-op, but enables refactorings to use non-Copy scalars on a case-by-case basis. Also, the only instance of a `One + Zero` trait bound was changed into a `Zero + One` bound to match the others. The following sed scripts were used in the refactoring (with each clause added to reduce the error count of `cargo check`): ```bash export RELEVANT_SOURCEFILES="$(find src -name '*.rs') $(find examples -name '*.rs')" for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar,/N: Scalar+Copy,/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Field/N: Scalar + Copy + Field/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Zero/N: Scalar + Copy + Zero/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Closed/N: Scalar + Copy + Closed/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + Eq/N: Scalar + Copy + Eq/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + PartialOrd/N: Scalar + Copy + PartialOrd/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: *Scalar + Zero/N: Scalar + Copy + Zero/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + PartialEq/N: Scalar + Copy + PartialEq/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar>/N: Scalar+Copy>/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: Scalar + $bound/N: Scalar + Copy + $bound/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: *Scalar + $bound/N: Scalar + Copy + $bound/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\): *Scalar,/N\1: Scalar+Copy,/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N: *Scalar + $trait/N: Scalar + Copy + $trait/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\): *Scalar + Superset/N\1: Scalar + Copy + Superset/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\): *Scalar + \([a-zA-Z]*Eq\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \([a-zA-Z]*Eq\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(hash::\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar {/N\1: Scalar + Copy {/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Zero\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Bounded\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Lattice\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Meet\|Join\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(fmt::\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Ring\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Hash\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Send\|Sync\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/One + Zero/Zero + One/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \(Zero\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar + \($marker\)/N\1: Scalar + Copy + \2/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/N\([0-9]\?\): *Scalar>/N\1: Scalar + Copy>/' $f; done for f in $RELEVANT_SOURCEFILES; do sed -i 's/Scalar+Copy/Scalar + Copy/' $f; done ```
2019-11-20 04:57:37 +08:00
impl<N: Scalar + Copy + Field + SupersetOf<f64>, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, 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]
pub fn sum(&self) -> N {
self.iter().cloned().fold(N::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, 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));
/// ```
#[inline]
pub fn row_sum(&self) -> RowVectorN<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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, 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));
/// ```
#[inline]
pub fn row_sum_tr(&self) -> VectorN<N, C>
where DefaultAllocator: Allocator<N, C> {
self.compress_rows_tr(|col| col.sum())
}
/// The sum 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_sum(), Vector2::new(6.0, 15.0));
/// ```
#[inline]
pub fn column_sum(&self) -> VectorN<N, R>
where DefaultAllocator: Allocator<N, R> {
let nrows = self.data.shape().0;
self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| {
out.axpy(N::one(), &col, N::one())
})
}
/*
*
* 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]
pub fn variance(&self) -> N {
if self.len() == 0 {
N::zero()
} else {
let val = self.iter().cloned().fold((N::zero(), N::zero()), |a, b| (a.0 + b * b, a.1 + b));
2019-03-23 21:29:07 +08:00
let denom = N::one() / crate::convert::<_, N>(self.len() as f64);
val.0 * denom - (val.1 * denom) * (val.1 * denom)
}
}
/// 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]
pub fn row_variance(&self) -> RowVectorN<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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]
pub fn row_variance_tr(&self) -> VectorN<N, C>
where DefaultAllocator: Allocator<N, C> {
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]
pub fn column_variance(&self) -> VectorN<N, R>
where DefaultAllocator: Allocator<N, R> {
let (nrows, ncols) = self.data.shape();
let mut mean = self.column_mean();
mean.apply(|e| -(e * e));
2019-03-23 21:29:07 +08:00
let denom = N::one() / crate::convert::<_, N>(ncols.value() as f64);
self.compress_columns(mean, |out, col| {
for i in 0..nrows.value() {
unsafe {
let val = col.vget_unchecked(i);
*out.vget_unchecked_mut(i) += denom * *val * *val
}
}
})
}
/*
*
* 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]
pub fn mean(&self) -> N {
if self.len() == 0 {
N::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]
pub fn row_mean(&self) -> RowVectorN<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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]
pub fn row_mean_tr(&self) -> VectorN<N, C>
where DefaultAllocator: Allocator<N, C> {
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]
pub fn column_mean(&self) -> VectorN<N, R>
where DefaultAllocator: Allocator<N, R> {
let (nrows, ncols) = self.data.shape();
2019-03-23 21:29:07 +08:00
let denom = N::one() / crate::convert::<_, N>(ncols.value() as f64);
self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| {
out.axpy(denom, &col, N::one())
})
}
}