import mir.math.common: approxEqual; import mir.ndslice.slice: sliced; assert(coefficientOfVariation([1.0, 2, 3]).approxEqual(1.0 / 2.0)); assert(coefficientOfVariation([1.0, 2, 3], true).approxEqual(0.816497 / 2.0)); assert(coefficientOfVariation!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(1.870829 / 2.5)); static assert(is(typeof(coefficientOfVariation!float([1, 2, 3])) == float));
Coefficient of variation 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.coefficientOfVariation.approxEqual(2.231299 / 2.437500));
Coefficient of variation 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.coefficientOfVariation.approxEqual(2.231299 / 2.437500));
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.coefficientOfVariation; assert(y.approxEqual(2.231299 / 1_000_000_002.437500)); // The naive variance algorithm is numerically unstable in this case, but // the difference is small as coefficientOfVariation is a ratio auto z0 = x.coefficientOfVariation!"naive"; assert(!z0.approxEqual(y, 0x1p-20f, 0x1p-30f)); // But the two-pass algorithm provides a consistent answer auto z1 = x.coefficientOfVariation!"twoPass"; assert(z1.approxEqual(y));
Can also set algorithm or output type
import mir.math.common: approxEqual; import mir.ndslice.slice: sliced; // Set population standard deviation, standardDeviation algorithm, sum algorithm or output type auto a = [1.0, 1e100, 1, -1e100].sliced; auto x = a * 10_000; bool populationTrue = true; /++ For this case, failing to use a summation algorithm results in an assert error because the mean is zero due to floating point precision issues. +/ //assert(x.coefficientOfVariation!("online").approxEqual(8.164966e103 / 0.0)); /++ 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.coefficientOfVariation!("online", "kbn").approxEqual(8.164966e103 / 5000.0)); assert(x.coefficientOfVariation!("online", "kb2").approxEqual(8.164966e103 / 5000.0)); assert(x.coefficientOfVariation!("online", "precise").approxEqual(8.164966e103 / 5000.0)); assert(x.coefficientOfVariation!(double, "online", "precise").approxEqual(8.164966e103 / 5000.0)); assert(x.coefficientOfVariation!(double, "online", "precise")(populationTrue).approxEqual(7.071068e103 / 5000.0)); auto y = [uint.max - 2, uint.max - 1, uint.max].sliced; auto z = y.coefficientOfVariation!ulong; assert(z == (1.0 / (cast(double) uint.max - 1))); static assert(is(typeof(z) == double)); assert(y.coefficientOfVariation!(ulong, "online") == (1.0 / (cast(double) uint.max - 1)));
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.coefficientOfVariation; assert(y.approxEqual(2.151462f / 2.416667)); static assert(is(typeof(y) == double)); assert(x.coefficientOfVariation!float.approxEqual(2.151462f / 2.416667));
coefficientOfVariation works for other user-defined types (provided they can be converted to a floating point)
import mir.math.common: approxEqual; static struct Foo { float x; alias x this; } Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)]; assert(foo.coefficientOfVariation.approxEqual(1f / 2f));
Arbitrary coefficientOfVariation
import mir.math.common: approxEqual; assert(coefficientOfVariation(1.0, 2, 3).approxEqual(1.0 / 2.0)); assert(coefficientOfVariation!float(1, 2, 3).approxEqual(1f / 2f));