public static function median(array $numbers)
{
if (empty($numbers)) {
return null;
}
// Reset the array key indexes because we don't know what might be passed in
$numbers = array_values($numbers);
// For odd number of numbers, take the middle indexed number
if (count($numbers) % 2 == 1) {
$middle_index = intdiv(count($numbers), 2);
return self::kthSmallest($numbers, $middle_index);
}
// For even number of items, take the mean of the middle two indexed numbers
$left_middle_index = intdiv(count($numbers), 2) - 1;
$left_median = self::kthSmallest($numbers, $left_middle_index);
$right_middle_index = $left_middle_index + 1;
$right_median = self::kthSmallest($numbers, $right_middle_index);
return self::mean([$left_median, $right_median]);
}
/** * Calculate the regression parameters using the Theil-Sen method * * Procedure: * Calculate the slopes of all pairs of points and select the median value * Calculate the intercept using the slope, and the medians of the X and Y values. * b = Ymedian - (m * Xmedian) */ public function calculate() { // The slopes array will be a list of slopes between all pairs of points $slopes = []; $n = count($this->points); for ($i = 0; $i < $n; $i++) { for ($j = $i + 1; $j < $n; $j++) { $pointi = $this->points[$i]; $pointj = $this->points[$j]; $slopes[] = ($pointj[1] - $pointi[1]) / ($pointj[0] - $pointi[0]); } } $this->m = Average::median($slopes); $this->b = Average::median($this->ys) - $this->m * Average::median($this->xs); $this->parameters = [$this->b, $this->m]; }