public static function spearmansRho(array $X, array $Y)
{
if (count($X) !== count($Y)) {
throw new Exception\BadDataException('Both random variables must have the same number of elements');
}
$n = count($X);
// Sorted Xs and Ys
$Xs = $X;
$Ys = $Y;
rsort($Xs);
rsort($Ys);
// Determine ranks of each X and Y
// Some items might show up multiple times, so record each successive rank.
$rg⟮X⟯ = [];
$rg⟮Y⟯ = [];
foreach ($Xs as $rank => $xᵢ) {
if (!isset($rg⟮X⟯[$xᵢ])) {
$rg⟮X⟯[$xᵢ] = [];
}
$rg⟮X⟯[$xᵢ][] = $rank;
}
foreach ($Ys as $rank => $yᵢ) {
if (!isset($rg⟮Y⟯[$yᵢ])) {
$rg⟮Y⟯[$yᵢ] = [];
}
$rg⟮Y⟯[$yᵢ][] = $rank;
}
// Determine average rank of each X and Y
// Rank will not change if value only shows up once.
// Average is for when values show up multiple times.
$rg⟮X⟯ = array_map(function ($x) {
return array_sum($x) / count($x);
}, $rg⟮X⟯);
$rg⟮Y⟯ = array_map(function ($y) {
return array_sum($y) / count($y);
}, $rg⟮Y⟯);
// Difference between the two ranks of each observation
$d = array_map(function ($x, $y) use($rg⟮X⟯, $rg⟮Y⟯) {
return abs($rg⟮X⟯[$x] - $rg⟮Y⟯[$y]);
}, $X, $Y);
// Numerator: 6 ∑ dᵢ²
$d² = Map\Single::square($d);
$∑d² = array_sum($d²);
// Denominator: n(n² − 1)
$n⟮n² − 1⟯ = $n * ($n ** 2 - 1);
/*
* 6 ∑ dᵢ²
* ρ = 1 - ---------
* n(n² − 1)
*/
return 1 - 6 * $∑d² / $n⟮n² − 1⟯;
}