2016-04-25 20:55:34 +00:00
|
|
|
<?php
|
|
|
|
|
2016-11-20 21:53:17 +00:00
|
|
|
declare(strict_types=1);
|
2016-04-25 20:55:34 +00:00
|
|
|
|
|
|
|
namespace Phpml\Regression;
|
|
|
|
|
2016-05-07 21:04:58 +00:00
|
|
|
use Phpml\Helper\Predictable;
|
2016-04-29 22:58:54 +00:00
|
|
|
use Phpml\Math\Matrix;
|
2016-04-27 21:51:14 +00:00
|
|
|
|
2016-04-25 20:55:34 +00:00
|
|
|
class LeastSquares implements Regression
|
|
|
|
{
|
2016-05-07 21:04:58 +00:00
|
|
|
use Predictable;
|
2016-04-29 21:03:08 +00:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2017-02-01 18:06:38 +00:00
|
|
|
private $samples = [];
|
2016-04-29 21:03:08 +00:00
|
|
|
|
2016-04-25 20:55:34 +00:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2017-02-01 18:06:38 +00:00
|
|
|
private $targets = [];
|
2016-04-25 20:55:34 +00:00
|
|
|
|
2016-04-27 21:04:59 +00:00
|
|
|
/**
|
2016-04-29 22:58:54 +00:00
|
|
|
* @var float
|
2016-04-27 21:04:59 +00:00
|
|
|
*/
|
2016-04-29 22:58:54 +00:00
|
|
|
private $intercept;
|
2016-04-27 21:04:59 +00:00
|
|
|
|
|
|
|
/**
|
2016-04-29 22:58:54 +00:00
|
|
|
* @var array
|
2016-04-27 21:04:59 +00:00
|
|
|
*/
|
2016-04-29 22:58:54 +00:00
|
|
|
private $coefficients;
|
2016-04-27 21:04:59 +00:00
|
|
|
|
2016-04-29 21:03:08 +00:00
|
|
|
public function train(array $samples, array $targets)
|
2016-04-25 20:55:34 +00:00
|
|
|
{
|
2017-02-01 18:06:38 +00:00
|
|
|
$this->samples = array_merge($this->samples, $samples);
|
|
|
|
$this->targets = array_merge($this->targets, $targets);
|
2016-04-27 21:51:14 +00:00
|
|
|
|
2016-04-29 22:58:54 +00:00
|
|
|
$this->computeCoefficients();
|
2016-04-25 20:55:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2016-05-07 21:04:58 +00:00
|
|
|
public function predictSample(array $sample)
|
2016-04-27 21:51:14 +00:00
|
|
|
{
|
2016-04-29 21:03:08 +00:00
|
|
|
$result = $this->intercept;
|
2016-04-29 22:58:54 +00:00
|
|
|
foreach ($this->coefficients as $index => $coefficient) {
|
|
|
|
$result += $coefficient * $sample[$index];
|
2016-04-29 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
2016-04-27 21:51:14 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 07:56:37 +00:00
|
|
|
public function getCoefficients() : array
|
2016-04-25 20:55:34 +00:00
|
|
|
{
|
2016-04-29 22:58:54 +00:00
|
|
|
return $this->coefficients;
|
2016-04-29 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 07:56:37 +00:00
|
|
|
public function getIntercept() : float
|
2016-04-30 11:32:40 +00:00
|
|
|
{
|
|
|
|
return $this->intercept;
|
|
|
|
}
|
|
|
|
|
2016-04-29 21:03:08 +00:00
|
|
|
/**
|
2016-04-29 22:59:10 +00:00
|
|
|
* coefficient(b) = (X'X)-1X'Y.
|
2016-04-29 21:03:08 +00:00
|
|
|
*/
|
2016-04-29 22:58:54 +00:00
|
|
|
private function computeCoefficients()
|
2016-04-29 21:03:08 +00:00
|
|
|
{
|
2016-04-30 11:54:01 +00:00
|
|
|
$samplesMatrix = $this->getSamplesMatrix();
|
|
|
|
$targetsMatrix = $this->getTargetsMatrix();
|
2016-04-29 22:58:54 +00:00
|
|
|
|
|
|
|
$ts = $samplesMatrix->transpose()->multiply($samplesMatrix)->inverse();
|
|
|
|
$tf = $samplesMatrix->transpose()->multiply($targetsMatrix);
|
2016-04-27 21:51:14 +00:00
|
|
|
|
2016-04-29 22:58:54 +00:00
|
|
|
$this->coefficients = $ts->multiply($tf)->getColumnValues(0);
|
|
|
|
$this->intercept = array_shift($this->coefficients);
|
2016-04-25 20:55:34 +00:00
|
|
|
}
|
2016-04-30 11:54:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Add one dimension for intercept calculation.
|
|
|
|
*/
|
2017-11-06 07:56:37 +00:00
|
|
|
private function getSamplesMatrix() : Matrix
|
2016-04-30 11:54:01 +00:00
|
|
|
{
|
|
|
|
$samples = [];
|
|
|
|
foreach ($this->samples as $sample) {
|
|
|
|
array_unshift($sample, 1);
|
|
|
|
$samples[] = $sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Matrix($samples);
|
|
|
|
}
|
|
|
|
|
2017-11-06 07:56:37 +00:00
|
|
|
private function getTargetsMatrix() : Matrix
|
2016-04-30 11:54:01 +00:00
|
|
|
{
|
|
|
|
if (is_array($this->targets[0])) {
|
|
|
|
return new Matrix($this->targets);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Matrix::fromFlatArray($this->targets);
|
|
|
|
}
|
2016-04-25 20:55:34 +00:00
|
|
|
}
|