The OpenD Programming Language

correlation

Calculates the correlation of the inputs.

If x and y are both slices or convertible to slices, then they must be one-dimensional.

Parameters

F

controls type of output

correlationAlgo

algorithm for calculating correlation (default: CorrelationAlgo.hybrid)

summation

algorithm for calculating sums (default: Summation.appropriate)

Return Value

The correlation of the inputs

Examples

Correlation of vectors

import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

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;
auto y = [-0.75,   6.0, -0.25, 8.25, 5.75,  3.5,
           9.25, -0.75,   2.5, 1.25,   -1, 2.25].sliced;

x.correlation(y).shouldApprox == -0.0623684;

Can also set algorithm type

import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

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 b = [-0.75,   6.0, -0.25, 8.25, 5.75,  3.5,
           9.25, -0.75,   2.5, 1.25,   -1, 2.25].sliced;

auto x = a + 10.0 ^^ 9;
auto y = b + 10.0 ^^ 9;

x.correlation(y).shouldApprox == -0.0623684;

// The naive algorithm is numerically unstable in this case
//assert(!x.correlation!"naive"(y).approxEqual(-0.0623684));

// The two-pass algorithm provides the same answer as hybrid
x.correlation!"twoPass"(y).shouldApprox == -0.0623684;

// And the assumeZeroMean algorithm is way off
assert(!x.correlation!"assumeZeroMean"(y).approxEqual(-0.0623684));

Can also set algorithm or output type

import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;
import mir.test: shouldApprox;

//Set population covariance, covariance algorithm, sum algorithm or output type

auto a = [1.0, 1e100, 1, -1e100].sliced;
auto b = [1.0e100, 1, 1, -1e100].sliced;
auto x = a * 10_000;
auto y = b * 10_000;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean
from the second and fourth numbers has no effect (for `y` the same is true
for the first and fourth). Further, after centering and multiplying `x` and
`y`, the third numbers in the slice has precision too low to be included in
the centered sum of the products. For the calculations below, the "true"
correlation should be a tiny amount above 0.5, but it is as if the
calculation happens between [0, 1, 0, -1] and [1, 0, 0, -1].
+/
x.correlation(y).shouldApprox == 0.5;

x.correlation!("online")(y).shouldApprox == 0.5;
x.correlation!("online", "kbn")(y).shouldApprox == 0.5;
x.correlation!("online", "kb2")(y).shouldApprox == 0.5;
x.correlation!("online", "precise")(y).shouldApprox == 0.5;
x.correlation!(double, "online", "precise")(y).shouldApprox == 0.5;

auto z1 = [uint.max - 2, uint.max - 1, uint.max].sliced;
auto z2 = [uint.max - 3, uint.max - 2, uint.max - 1].sliced;
z1.correlation(z2).shouldApprox == 1.0;
static assert(is(typeof(z1.correlation!float(z2)) == float));

For integral slices, pass output type as template parameter to ensure output type is correct.

import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;
auto y = [6, 3, 7, 1, 1, 1,
          9, 5, 3, 1, 3, 7].sliced;

x.correlation(y).shouldApprox == -0.27934577;
static assert(is(typeof(x.correlation(y)) == double));

x.correlation!float(y).shouldApprox == -0.27934577;
static assert(is(typeof(x.correlation!float(y)) == float));

Works with @nogc

import mir.ndslice.allocation: mininitRcslice;
import mir.test: shouldApprox;

static immutable 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];
static immutable b = [-0.75,   6.0, -0.25, 8.25, 5.75,  3.5,
                       9.25, -0.75,   2.5, 1.25,   -1, 2.25];
auto x = mininitRcslice!double(12);
auto y = mininitRcslice!double(12);
x[] = a;
y[] = b;

x.correlation(y).shouldApprox == -0.0623684;

Meta