Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

mir.stat.descriptive.multivariate

This module contains algorithms for multivariate descriptive statistics.
License:
Authors:
John Michael Hall
enum CovarianceAlgo: int;
Covariance algorithms.
online
Performs Welford's online algorithm for updating covariance. While it only iterates each input once, it can be slower for smaller inputs. However, it is also more accurate. Can also put another CovarianceAccumulator of the same type, which uses the parallel algorithm from Chan et al.
naive
Calculates covariance using E(x*y) - E(x)*E(y) (alowing for adjustments for population/sample variance). This algorithm can be numerically unstable.
twoPass
Calculates covariance using a two-pass algorithm whereby the inputs are first centered and then the sum of products is calculated from that. May be faster than online and generally more accurate than the naive algorithm.
assumeZeroMean
Calculates covariance assuming the mean of the inputs is zero.
hybrid
When slices, slice-like objects, or ranges are the inputs, uses the two-pass algorithm. When an individual data-point is added, uses the online algorithm.
struct CovarianceAccumulator(T, CovarianceAlgo covarianceAlgo, Summation summation) if (isMutable!T && (covarianceAlgo == CovarianceAlgo.naive));
Examples:
import mir.math.sum: Summation;
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;

CovarianceAccumulator!(double, CovarianceAlgo.naive, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == 82.25 / 12 - (29.25 * 36) / (12 * 12);
v.covariance(false).shouldApprox == 82.25 / 11 - (29.25 * 36) / (12 * 12) * (12.0 / 11);

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == 94.25 / 13 - (33.25 * 39) / (13 * 13);
v.covariance(false).shouldApprox == 94.25 / 12 - (33.25 * 39) / (13 * 13) * (13.0 / 12);
alias S = Summator!(T, summation);
S summatorLeft;
S summatorRight;
S summatorOfProducts;
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
void put(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
void put(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && is(elementType!RangeX : T) && isInputRange!RangeY && !isConvertibleToSlice!RangeY && is(elementType!RangeY : T));
void put()(T x, T y);
void put(U, Summation sumAlgo)(CovarianceAccumulator!(U, covarianceAlgo, sumAlgo) v);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F sumOfProducts(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F covariance(F = T)(bool isPopulation);
struct CovarianceAccumulator(T, CovarianceAlgo covarianceAlgo, Summation summation) if (isFloatingPoint!T && isMutable!T && (covarianceAlgo == CovarianceAlgo.online));
Examples:
import mir.math.sum: Summation;
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;

CovarianceAccumulator!(double, CovarianceAlgo.online, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == -5.5 / 13;
v.covariance(false).shouldApprox == -5.5 / 12;
alias S = Summator!(T, summation);
S summatorLeft;
S summatorRight;
S centeredSummatorOfProducts;
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
this()(T x, T y);
void put(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
void put(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && isInputRange!RangeY && !isConvertibleToSlice!RangeY);
void put()(T x, T y);
void put(U, CovarianceAlgo covAlgo, Summation sumAlgo)(CovarianceAccumulator!(U, covAlgo, sumAlgo) v)
if (covAlgo != CovarianceAlgo.assumeZeroMean);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F covariance(F = T)(bool isPopulation);
struct CovarianceAccumulator(T, CovarianceAlgo covarianceAlgo, Summation summation) if (isMutable!T && (covarianceAlgo == CovarianceAlgo.twoPass));
Examples:
import mir.math.sum: Summation;
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;

auto v = CovarianceAccumulator!(double, CovarianceAlgo.twoPass, Summation.naive)(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;
alias S = Summator!(T, summation);
this(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
this(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && is(elementType!RangeX : T) && isInputRange!RangeY && !isConvertibleToSlice!RangeY && is(elementType!RangeY : T));
this()(T x, T y);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F covariance(F = T)(bool isPopulation);
struct CovarianceAccumulator(T, CovarianceAlgo covarianceAlgo, Summation summation) if (isMutable!T && (covarianceAlgo == CovarianceAlgo.assumeZeroMean));
Examples:
import mir.stat.transform: center;
import mir.math.sum: Summation;
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.center;
auto y = b.center;

CovarianceAccumulator!(double, CovarianceAlgo.assumeZeroMean, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == 6.5 / 13;
v.covariance(false).shouldApprox == 6.5 / 12;
Summator!(T, summation) centeredSummatorOfProducts;
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
this()(T x, T y);
void put(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
void put(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && is(elementType!RangeX : T) && isInputRange!RangeY && !isConvertibleToSlice!RangeY && is(elementType!RangeY : T));
void put()(T x, T y);
void put(U, Summation sumAlgo)(CovarianceAccumulator!(U, covarianceAlgo, sumAlgo) v);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F covariance(F = T)(bool isPopulation);
struct CovarianceAccumulator(T, CovarianceAlgo covarianceAlgo, Summation summation) if (isFloatingPoint!T && isMutable!T && (covarianceAlgo == CovarianceAlgo.hybrid));
Examples:
import mir.math.sum: Summation;
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;

CovarianceAccumulator!(double, CovarianceAlgo.hybrid, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == -5.5 / 13;
v.covariance(false).shouldApprox == -5.5 / 12;
alias S = Summator!(T, summation);
S summatorLeft;
S summatorRight;
S centeredSummatorOfProducts;
this()(T x, T y);
this(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
this(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && isInputRange!RangeY && !isConvertibleToSlice!RangeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
void put()(T x, T y);
void put(U, CovarianceAlgo covAlgo, Summation sumAlgo)(CovarianceAccumulator!(U, covAlgo, sumAlgo) v);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F covariance(F = T)(bool isPopulation);
template covariance(F, CovarianceAlgo covarianceAlgo = CovarianceAlgo.hybrid, Summation summation = Summation.appropriate) if (isFloatingPoint!F)

template covariance(CovarianceAlgo covarianceAlgo = CovarianceAlgo.hybrid, Summation summation = Summation.appropriate)

template covariance(F, string covarianceAlgo, string summation = "appropriate")

template covariance(string covarianceAlgo, string summation = "appropriate")
Calculates the covariance of the inputs.
If x and y are both slices or convertible to slices, then they must be one-dimensional.
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
covarianceAlgo algorithm for calculating covariance (default: CovarianceAlgo.hybrid)
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The covariance of the inputs
Examples:
Covariance 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.covariance(y, true).shouldApprox == -5.5 / 12;
x.covariance(y).shouldApprox == -5.5 / 11;
Examples:
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.covariance(y).shouldApprox == -5.5 / 11;

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

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

// And the assumeZeroMean algorithm is way off
assert(!x.covariance!"assumeZeroMean"(y).approxEqual(-5.5 / 11));
Examples:
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.
+/
x.covariance(y).shouldApprox == 1.0e208 / 3;
x.covariance(y, true).shouldApprox == 1.0e208 / 4;

x.covariance!("online")(y).shouldApprox == 1.0e208 / 3;
x.covariance!("online", "kbn")(y).shouldApprox == 1.0e208 / 3;
x.covariance!("online", "kb2")(y).shouldApprox == 1.0e208 / 3;
x.covariance!("online", "precise")(y).shouldApprox == 1.0e208 / 3;
x.covariance!(double, "online", "precise")(y).shouldApprox == 1.0e208 / 3;

auto z = uint.max.repeat(3);
z.covariance!float(z).shouldApprox == 0.0;
static assert(is(typeof(z.covariance!float(z)) == float));
Examples:
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.covariance(y).shouldApprox == -18.583333 / 11;
static assert(is(typeof(x.covariance(y)) == double));

x.covariance!float(y).shouldApprox == -18.583333 / 11;
static assert(is(typeof(x.covariance!float(y)) == float));
Examples:
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.covariance(y, true).shouldApprox == -5.5 / 12;
x.covariance(y).shouldApprox == -5.5 / 11;
F covariance(RangeX, RangeY)(RangeX x, RangeY y, bool isPopulation = false)
if (isInputRange!RangeX && isInputRange!RangeY);
Parameters:
RangeX x range, must be finite iterable
RangeY y range, must be finite iterable
bool isPopulation true if population covariance, false if sample covariance (default)
enum CorrelationAlgo: int;
Correlation algorithms.
online
Performs Welford's online algorithm for updating correlation. While it only iterates each input once, it can be slower for smaller inputs. However, it is also more accurate. Can also put another CorrelationAccumulator of the same type, which uses the parallel algorithm from Chan et al.
naive
Calculates correlation using (E(x*y) - E(x)*E(y))/(sqrt(E(x^2)-E(x)^2)*sqrt(E(y^2)-E(y)^2)) (alowing for adjustments for population/sample variance). This algorithm can be numerically unstable.
twoPass
Calculates correlation using a two-pass algorithm whereby the inputs are first centered and then the sum of products is calculated from that. May be faster than online and generally more accurate than the naive algorithm.
assumeZeroMean
Calculates correlation assuming the mean of the inputs is zero.
assumeStandardized
Calculates correlation assuming the mean of the inputs is zero and standard deviation is one.
hybrid
When slices, slice-like objects, or ranges are the inputs, uses the two-pass algorithm. When an individual data-point is added, uses the online algorithm.
struct CorrelationAccumulator(T, CorrelationAlgo correlationAlgo, Summation summation) if (isMutable!T && (correlationAlgo == CorrelationAlgo.naive));
Examples:
import mir.math.sum: Summation;
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;

CorrelationAccumulator!(double, CorrelationAlgo.naive, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == 82.25 / 12 - (29.25 * 36) / (12 * 12);
v.covariance(false).shouldApprox == 82.25 / 11 - (29.25 * 36) / (12 * 12) * (12.0 / 11);

v.correlation.shouldApprox == -0.0623684;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == 94.25 / 13 - (33.25 * 39) / (13 * 13);
v.covariance(false).shouldApprox == 94.25 / 12 - (33.25 * 39) / (13 * 13) * (13.0 / 12);

v.correlation.shouldApprox == -0.0611234;
alias S = Summator!(T, summation);
S summatorLeft;
S summatorRight;
S summatorOfProducts;
S summatorOfSquaresLeft;
S summatorOfSquaresRight;
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
void put(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
void put(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && is(elementType!RangeX : T) && isInputRange!RangeY && !isConvertibleToSlice!RangeY && is(elementType!RangeY : T));
void put()(T x, T y);
void put(U, Summation sumAlgo)(CorrelationAccumulator!(U, correlationAlgo, sumAlgo) v);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F sumOfProducts(F = T)();
@property F sumOfSquaresLeft(F = T)();
@property F sumOfSquaresRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F centeredSumOfSquaresLeft(F = T)();
@property F centeredSumOfSquaresRight(F = T)();
@property F covariance(F = T)(bool isPopulation);
@property F correlation(F = T)();
struct CorrelationAccumulator(T, CorrelationAlgo correlationAlgo, Summation summation) if (isFloatingPoint!T && isMutable!T && (correlationAlgo == CorrelationAlgo.online));
Examples:
import mir.math.sum: Summation;
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;

CorrelationAccumulator!(double, CorrelationAlgo.online, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.correlation.shouldApprox == -0.0623684;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == -5.5 / 13;
v.covariance(false).shouldApprox == -5.5 / 12;

v.correlation.shouldApprox == -0.0611234;
alias S = Summator!(T, summation);
S summatorLeft;
S summatorRight;
S centeredSummatorOfProducts;
S centeredSummatorOfSquaresLeft;
S centeredSummatorOfSquaresRight;
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
this()(T x, T y);
void put(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
void put(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && isInputRange!RangeY && !isConvertibleToSlice!RangeY);
void put()(T x, T y);
void put(U, CorrelationAlgo covAlgo, Summation sumAlgo)(CorrelationAccumulator!(U, covAlgo, sumAlgo) v)
if (!is(covAlgo == CorrelationAlgo.assumeZeroMean));
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F centeredSumOfSquaresLeft(F = T)();
@property F centeredSumOfSquaresRight(F = T)();
@property F covariance(F = T)(bool isPopulation);
@property F correlation(F = T)();
struct CorrelationAccumulator(T, CorrelationAlgo correlationAlgo, Summation summation) if (isMutable!T && (correlationAlgo == CorrelationAlgo.twoPass));
Examples:
import mir.math.sum: Summation;
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;

auto v = CorrelationAccumulator!(double, CorrelationAlgo.twoPass, Summation.naive)(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.correlation.shouldApprox == -0.0623684;
alias S = Summator!(T, summation);
this(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
this(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && is(elementType!RangeX : T) && isInputRange!RangeY && !isConvertibleToSlice!RangeY && is(elementType!RangeY : T));
this()(T x, T y);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F centeredSumOfSquaresLeft(F = T)();
@property F centeredSumOfSquaresRight(F = T)();
@property F covariance(F = T)(bool isPopulation);
@property F correlation(F = T)();
struct CorrelationAccumulator(T, CorrelationAlgo correlationAlgo, Summation summation) if (isMutable!T && (correlationAlgo == CorrelationAlgo.assumeZeroMean));
Examples:
import mir.stat.transform: center;
import mir.math.sum: Summation;
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.center;
auto y = b.center;

CorrelationAccumulator!(double, CorrelationAlgo.assumeZeroMean, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.correlation.shouldApprox == -0.0623684;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == 6.5 / 13;
v.covariance(false).shouldApprox == 6.5 / 12;

v.correlation.shouldApprox == 0.0628802;
alias S = Summator!(T, summation);
S centeredSummatorOfProducts;
S centeredSummatorOfSquaresLeft;
S centeredSummatorOfSquaresRight;
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
this()(T x, T y);
void put(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
void put(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && is(elementType!RangeX : T) && isInputRange!RangeY && !isConvertibleToSlice!RangeY && is(elementType!RangeY : T));
void put()(T x, T y);
void put(U, Summation sumAlgo)(CorrelationAccumulator!(U, correlationAlgo, sumAlgo) v);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F centeredSumOfSquaresLeft(F = T)();
@property F centeredSumOfSquaresRight(F = T)();
@property F covariance(F = T)(bool isPopulation);
@property F correlation(F = T)();
struct CorrelationAccumulator(T, CorrelationAlgo correlationAlgo, Summation summation) if (isFloatingPoint!T && isMutable!T && (correlationAlgo == CorrelationAlgo.hybrid));
Examples:
import mir.math.sum: Summation;
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;

CorrelationAccumulator!(double, CorrelationAlgo.hybrid, Summation.naive) v;
v.put(x, y);

v.covariance(true).shouldApprox == -5.5 / 12;
v.covariance(false).shouldApprox == -5.5 / 11;

v.correlation.shouldApprox == -0.0623684;

v.put(4.0, 3.0);
v.covariance(true).shouldApprox == -5.5 / 13;
v.covariance(false).shouldApprox == -5.5 / 12;

v.correlation.shouldApprox == -0.0611234;
alias S = Summator!(T, summation);
S summatorLeft;
S summatorRight;
S centeredSummatorOfProducts;
S centeredSummatorOfSquaresLeft;
S centeredSummatorOfSquaresRight;
this()(T x, T y);
this(IteratorX, IteratorY, SliceKind kindX, SliceKind kindY)(Slice!(IteratorX, 1, kindX) x, Slice!(IteratorY, 1, kindY) y);
this(SliceLikeX, SliceLikeY)(SliceLikeX x, SliceLikeY y)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeY && !isSlice!SliceLikeY);
this(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && !isConvertibleToSlice!RangeX && isInputRange!RangeY && !isConvertibleToSlice!RangeY);
void put(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
void put()(T x, T y);
void put(U, CorrelationAlgo covAlgo, Summation sumAlgo)(CorrelationAccumulator!(U, covAlgo, sumAlgo) v);
@property size_t count();
@property F sumLeft(F = T)();
@property F sumRight(F = T)();
@property F meanLeft(F = T)();
@property F meanRight(F = T)();
@property F centeredSumOfProducts(F = T)();
@property F centeredSumOfSquaresLeft(F = T)();
@property F centeredSumOfSquaresRight(F = T)();
@property F covariance(F = T)(bool isPopulation);
@property F correlation(F = T)();
template correlation(F, CorrelationAlgo correlationAlgo = CorrelationAlgo.hybrid, Summation summation = Summation.appropriate) if (isFloatingPoint!F)

template correlation(CorrelationAlgo correlationAlgo = CorrelationAlgo.hybrid, Summation summation = Summation.appropriate)

template correlation(F, string correlationAlgo, string summation = "appropriate")

template correlation(string correlationAlgo, string summation = "appropriate")
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)
Returns:
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;
Examples:
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));
Examples:
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));
Examples:
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));
Examples:
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;
F correlation(RangeX, RangeY)(RangeX x, RangeY y)
if (isInputRange!RangeX && isInputRange!RangeY);
Parameters:
RangeX x range, must be finite iterable
RangeY y range, must be finite iterable