The OpenD Programming Language

coefficientOfVariation

Examples

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));

Meta