Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
<?php

declare(strict_types=1);

namespace Phpml\FeatureSelection\ScoringFunction;

use Phpml\FeatureSelection\ScoringFunction;
use Phpml\Math\Matrix;
use Phpml\Math\Statistic\Mean;

/**
 * Quick linear model for testing the effect of a single regressor,
 * sequentially for many regressors.
 *
 * This is done in 2 steps:
 *
 * 1. The cross correlation between each regressor and the target is computed,
 * that is, ((X[:, i] - mean(X[:, i])) * (y - mean_y)) / (std(X[:, i]) *std(y)).
 * 2. It is converted to an F score.
 *
 * Ported from scikit-learn f_regression function (http://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.f_regression.html#sklearn.feature_selection.f_regression)
 */
final class UnivariateLinearRegression implements ScoringFunction
{
    /**
     * @var bool
     */
    private $center;

    /**
     * @param bool $center - if true samples and targets will be centered
     */
    public function __construct(bool $center = true)
    {
        $this->center = $center;
    }

    public function score(array $samples, array $targets): array
    {
        if ($this->center) {
            $this->centerTargets($targets);
            $this->centerSamples($samples);
        }

        $correlations = [];
        foreach (array_keys($samples[0]) as $index) {
            $featureColumn = array_column($samples, $index);
            $correlations[$index] =
< (Matrix::dot($targets, $featureColumn)[0] / (new Matrix($featureColumn, false))->transpose()->frobeniusNorm())
> Matrix::dot($targets, $featureColumn)[0] / (new Matrix($featureColumn, false))->transpose()->frobeniusNorm()
/ (new Matrix($targets, false))->frobeniusNorm(); } $degreesOfFreedom = count($targets) - ($this->center ? 2 : 1); return array_map(function (float $correlation) use ($degreesOfFreedom): float { return $correlation ** 2 / (1 - $correlation ** 2) * $degreesOfFreedom; }, $correlations); } private function centerTargets(array &$targets): void { $mean = Mean::arithmetic($targets); array_walk($targets, function (&$target) use ($mean): void { $target -= $mean; }); } private function centerSamples(array &$samples): void { $means = []; foreach ($samples[0] as $index => $feature) { $means[$index] = Mean::arithmetic(array_column($samples, $index)); } foreach ($samples as &$sample) { foreach ($sample as $index => &$feature) { $feature -= $means[$index]; } } } }