/** * Calculates the regression parameters. */ public function calculate() { $v = $this->v; $w = $this->w; $x’ = Single::subtract($this->xs, $v); $y’ = Single::subtract($this->ys, $w); $parameters = $this->leastSquares($y’, $x’, 1, 0)->getColumn(0); $this->m = $parameters[0]; $this->b = $this->w - $this->m * $this->v; $this->parameters = [$this->b, $this->m]; }
/** * Calculate the regression parameters by least squares on linearized data * y⁻¹ = K * V⁻¹ * x⁻¹ + V⁻¹ */ public function calculate() { // Linearize the relationship by taking the inverse of both x and y $x’ = Single::pow($this->xs, -1); $y’ = Single::pow($this->ys, -1); // Perform Least Squares Fit $linearized_parameters = $this->leastSquares($y’, $x’)->getColumn(0); // Translate the linearized parameters back. $V = 1 / $linearized_parameters[0]; $K = $linearized_parameters[1] * $V; $this->parameters = [$V, $K]; }
/** * Evaluate for x * Use the smoothness parameter α to determine the subset of data to consider for * local regression. Perform a weighted least squares regression and evaluate x. * * @param number $x * * @return number */ public function evaluate($x) { $α = $this->α; $λ = $this->λ; $n = $this->n; // The number of points considered in the local regression $Δx = Single::abs(Single::subtract($this->xs, $x)); $αᵗʰΔx = Average::kthSmallest($Δx, $this->number_of_points - 1); $arg = Single::min(Single::divide($Δx, $αᵗʰΔx * max($α, 1)), 1); // Kernel function: tricube = (1-arg³)³ $tricube = Single::cube(Single::multiply(Single::subtract(Single::cube($arg), 1), -1)); $weights = $tricube; // Local Regression Parameters $parameters = $this->leastSquares($this->ys, $this->xs, $weights, $λ); $X = new VandermondeMatrix([$x], $λ + 1); return $X->multiply($parameters)[0][0]; }
/** * Sum of squares * * ∑⟮xᵢ⟯² * * @param array $numbers * * @return number */ public static function sumOfSquares(array $numbers) { if (empty($numbers)) { return null; } $∑⟮xᵢ⟯² = array_sum(Map\Single::square($numbers)); return $∑⟮xᵢ⟯²; }
/** * Generalized Hypergeometric Function * * https://en.wikipedia.org/wiki/Generalized_hypergeometric_function * * ∞ * ____ * \ ∏ap⁽ⁿ⁾ * zⁿ * pFq(a₁,a₂,...ap;b₁,b₂,...,bq;z)= > ------------ * / ∏bq⁽ⁿ⁾ * n! * ‾‾‾‾ * n=0 * * Where a⁽ⁿ⁾ is the Pochhammer Function or Rising Factorial * * We are evaluating this as a series: * * (a + n - 1) * z * ∏n = ∏n₋₁ * ----------------- * (b + n - 1) * n * * n (a + n - 1) * z * ₁F₁ = ₁F₁n₋₁ + ∏ ----------------- = ₁F₁n₋₁ + ∏n * 1 (b + n - 1) * n * * @param int $p the number of parameters in the numerator * @param int $q the number of parameters in the denominator * @param array $params a collection of the a, b, and z parameters * * @return number * * @throws BadParameterException if the number of parameters is incorrect */ public static function generalizedHypergeometric(int $p, int $q, ...$params) { $n = count($params); if ($n !== $p + $q + 1) { $expected_num_params = $p + $q + 1; throw new Exception\BadParameterException("Number of parameters is incorrect. Expected {$expected_num_params}; got {$n}"); } $a = array_slice($params, 0, $p); $b = array_slice($params, $p, $q); $z = $params[$n - 1]; $tol = 1.0E-8; $n = 1; $sum = 0; $product = 1; do { $sum += $product; $a_sum = array_product(Single::add($a, $n - 1)); $b_sum = array_product(Single::add($b, $n - 1)); $product *= $a_sum * $z / $b_sum / $n; $n++; } while ($product / $sum > $tol); return $sum; }
/** * Standard error of the regression parameters (coefficients) * * _________ * / ∑eᵢ² * / ----- * se(m) = / ν * / --------- * √ ∑⟮xᵢ - μ⟯² * * where * eᵢ = residual (difference between observed value and value predicted by the model) * ν = n - 2 degrees of freedom * * ______ * / ∑xᵢ² * se(b) = / ---- * √ n * * @return array [m => se(m), b => se(b)] */ public function standardErrors() { $X = new VandermondeMatrix($this->xs, 2); $⟮XᵀX⟯⁻¹ = $this->⟮XᵀX⟯⁻¹; $σ² = $this->meanSquareResidual(); $standard_error_matrix = $⟮XᵀX⟯⁻¹->scalarMultiply($σ²); $standard_error_array = Single::sqrt($standard_error_matrix->getDiagonalElements()); return ['m' => $standard_error_array[1], 'b' => $standard_error_array[0]]; }
/** * ρ - Spearman's rank correlation coefficient (Spearman's rho) * * https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient * * 6 ∑ dᵢ² * ρ = 1 - --------- * n(n² − 1) * * Where * dᵢ: the difference between the two ranks of each observation * * @param array $X values for random variable X * @param array $Y values for random variable Y * * @return number * * @throws BadDataException if both random variables do not have the same number of elements */ 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⟯; }
/** * Infinity norm (‖A‖∞) * Maximum absolute row sum of the matrix * * @return number */ public function infinityNorm() { $m = $this->m; $‖A‖∞ = array_sum(Map\Single::abs($this->A[0])); for ($i = 1; $i < $m; $i++) { $‖A‖∞ = max($‖A‖∞, array_sum(Map\Single::abs($this->A[$i]))); } return $‖A‖∞; }
/** * Max norm (infinity norm) (|x|∞) * * |x|∞ = max |x| * * @return number */ public function maxNorm() { return max(Map\Single::abs($this->A)); }