controls type of output
algorithm for calculating variance (default: VarianceAlgo.hybrid)
algorithm for calculating sums (default: Summation.appropriate)
The variance of the input, must be floating point or complex type
import mir.math.common: approxEqual; import mir.complex.math: capproxEqual = approxEqual; import mir.ndslice.slice: sliced; import mir.complex; alias C = Complex!double; assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2)); assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3)); assert(variance([C(1, 3), C(2), C(3)]).capproxEqual(C(-4, -6) / 2)); assert(variance!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 5)); static assert(is(typeof(variance!float([1, 2, 3])) == float));
Variance of vector
import mir.math.common: approxEqual; import mir.ndslice.slice: sliced; auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced; assert(x.variance.approxEqual(54.76562 / 11));
Variance of matrix
import mir.math.common: approxEqual; import mir.ndslice.fuse: fuse; auto x = [ [0.0, 1.0, 1.5, 2.0, 3.5, 4.25], [2.0, 7.5, 5.0, 1.0, 1.5, 0.0] ].fuse; assert(x.variance.approxEqual(54.76562 / 11));
Column variance of matrix
import mir.algorithm.iteration: all; import mir.math.common: approxEqual; import mir.ndslice.fuse: fuse; import mir.ndslice.topology: alongDim, byDim, map; auto x = [ [0.0, 1.0, 1.5, 2.0], [3.5, 4.25, 2.0, 7.5], [5.0, 1.0, 1.5, 0.0] ].fuse; auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2]; // Use byDim or alongDim with map to compute variance of row/column. assert(x.byDim!1.map!variance.all!approxEqual(result)); assert(x.alongDim!0.map!variance.all!approxEqual(result)); // FIXME // Without using map, computes the variance of the whole slice // assert(x.byDim!1.variance == x.sliced.variance); // assert(x.alongDim!0.variance == x.sliced.variance);
Can also set algorithm type
import mir.math.common: approxEqual; import mir.ndslice.slice: sliced; auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25, 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced; auto x = a + 1_000_000_000; auto y = x.variance; assert(y.approxEqual(54.76562 / 11)); // The naive algorithm is numerically unstable in this case auto z0 = x.variance!"naive"; assert(!z0.approxEqual(y)); auto z1 = x.variance!"online"; assert(z1.approxEqual(54.76562 / 11)); // But the two-pass algorithm provides a consistent answer auto z2 = x.variance!"twoPass"; assert(z2.approxEqual(y)); // And the assumeZeroMean algorithm is way off auto z3 = x.variance!"assumeZeroMean"; assert(z3.approxEqual(1.2e19 / 11));
Can also set algorithm or output type
import mir.math.common: approxEqual; import mir.ndslice.slice: sliced; import mir.ndslice.topology: repeat; //Set population variance, variance algorithm, sum algorithm or output type auto a = [1.0, 1e100, 1, -1e100].sliced; auto x = a * 10_000; /++ Due to Floating Point precision, when centering `x`, subtracting the mean from the second and fourth numbers has no effect. Further, after centering and squaring `x`, the first and third numbers in the slice have precision too low to be included in the centered sum of squares. +/ assert(x.variance(false).approxEqual(2.0e208 / 3)); assert(x.variance(true).approxEqual(2.0e208 / 4)); assert(x.variance!("online").approxEqual(2.0e208 / 3)); assert(x.variance!("online", "kbn").approxEqual(2.0e208 / 3)); assert(x.variance!("online", "kb2").approxEqual(2.0e208 / 3)); assert(x.variance!("online", "precise").approxEqual(2.0e208 / 3)); assert(x.variance!(double, "online", "precise").approxEqual(2.0e208 / 3)); assert(x.variance!(double, "online", "precise")(true).approxEqual(2.0e208 / 4)); auto y = uint.max.repeat(3); auto z = y.variance!ulong; assert(z == 0.0); static assert(is(typeof(z) == double));
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual; import mir.ndslice.slice: sliced; auto x = [0, 1, 1, 2, 4, 4, 2, 7, 5, 1, 2, 0].sliced; auto y = x.variance; assert(y.approxEqual(50.91667 / 11)); static assert(is(typeof(y) == double)); assert(x.variance!float.approxEqual(50.91667 / 11));
Variance works for complex numbers and other user-defined types (provided they can be converted to a floating point or complex type)
import mir.complex.math: approxEqual; import mir.ndslice.slice: sliced; import mir.complex; alias C = Complex!double; auto x = [C(1, 2), C(2, 3), C(3, 4), C(4, 5)].sliced; assert(x.variance.approxEqual((C(0, 10)) / 3));
Compute variance along specified dimention of tensors
import mir.algorithm.iteration: all; import mir.math.common: approxEqual; import mir.ndslice.fuse: fuse; import mir.ndslice.topology: as, iota, alongDim, map, repeat; auto x = [ [0.0, 1, 2], [3.0, 4, 5] ].fuse; assert(x.variance.approxEqual(17.5 / 5)); auto m0 = [4.5, 4.5, 4.5]; assert(x.alongDim!0.map!variance.all!approxEqual(m0)); assert(x.alongDim!(-2).map!variance.all!approxEqual(m0)); auto m1 = [1.0, 1.0]; assert(x.alongDim!1.map!variance.all!approxEqual(m1)); assert(x.alongDim!(-1).map!variance.all!approxEqual(m1)); assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!variance.all!approxEqual(repeat(3600.0 / 2, 3, 4, 5)));
Arbitrary variance
assert(variance(1.0, 2, 3) == 1.0); assert(variance!float(1, 2, 3) == 1f);
Calculates the variance of the input
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.