moc/math

PHP数学拟合库。执行数据的线性和非线性拟合。包括拟合时使用的各种数学类。包含大量的线性代数,如矩阵和向量代数。

dev-master 2014-05-23 12:57 UTC

This package is not auto-updated.

Last update: 2024-09-14 15:28:37 UTC


README

PHP数学库。包括矩阵操作、方程求解以及线性和非线性拟合

一些矩阵计算也可以使用PHP的lapack扩展完成。MOC/Math包是一个原生PHP实现,速度可能较慢,但不需要任何外部库。

该库功能尚未完善,仅包含进行线性和非线性拟合所需的最基本功能。算法灵感来源于非常优秀的书籍《数值食谱》。

安装

最简单的方法是使用composer将其包含在现有的composer项目中。

composer require moc/math

然后您可以直接在项目中使用它。

<?php
require_once __DIR__ . '/vendor/autoload.php';
use \MOC\Math\DataSeries;
$data = DataSeries::fromArray(array(
	array(0, 1.0, 0.1),
	array(1, 1.5, 0.2),
	array(2, 3.0, 0.09),
	array(3, 4.8, 0.11),
	array(4, 6.1, 0.15)
));
foreach ($data as $point) {
	printf("X: %4.2f\tY: %4.2f\n", $point->getX(), $point->getY());
}

矩阵和向量

矩阵类使用二维行优先数组构建。示例

$matrix = new Matrix(array(
	array(1, 2, 5),
	array(5, 2, 7),
	array(3, 9, 2)
));

矩阵还有两个工厂类,用于创建空和单位矩阵

 $identity = Matrix::identity(2)

将创建对角线元素为1,其余元素为0的2x2矩阵

$empty = Matrix::emptyMatrix(2,2)

将创建所有元素都设置为0的2x2矩阵。

矩阵类包括以下方法

  • multiplyWithVectorFromRight
  • equals
  • getInverse

向量类通过以下方式构建

$vector = new Vector(array(2,3,4);

并包括寻找范数、长度等方法。

数据系列和点

Point类表示XY平面上的一个单独的点,可能附有一个误差。

要创建x=0,y=1的点,带有一个可选误差0.5,使用此方法

$point = new \MOC\Math\Point(array(0.00, 1.00, 0.5);

DataSeries对象用于包含一系列数据点。它的构造函数接受点对象的数组,但它还包含从数组创建数据系列的方便的工厂方法。

$data = DataSeries::fromArray(array(
array(0, 1.0),
array(1, 1.5),
array(2, 3.0),
array(3, 4.8),
array(4, 6.1)
));

每个点都附加了误差

$data = DataSeries::fromArray(array(
	array(0, 1.0, 0.1),
	array(1, 1.5, 0.2),
	array(2, 3.0, 0.09),
	array(3, 4.8, 0.11),
	array(4, 6.1, 0.15)
));

数据系列实现了countable、ArrayAccess和Iterator,因此可以使用如下方式

foreach ($data as $point) {
	// Do stuff with $point which is now a \MOC\Math\Point object
}

print count($data);

print "Point 2: " . $data[2]->getY();

高斯-若尔当消元法

还包括一个用于对角化矩阵的高斯-若尔当消元算法实现。这在求解逆矩阵、解M未知数N个方程或拟合数据到模型数据时很有用。

$solver = new \MOC\Math\GaussJordan();
$solver->solve($matrixA, $matrixB);

注意,solve方法实际上会更改矩阵$matrixA和$matrixB。$matrixB必须有与$matrixA相同的行数,否则会抛出异常。

调用solve后,$matrixA将是单位矩阵,所有使$matrixA成为此矩阵的操作也将应用于$matrixB。因此,如果$matrixA是一个方阵且$matrixB是单位矩阵,对solve的调用将使$matrixB成为$matrixA的逆矩阵。

该算法使用部分主元选择。

线性回归或线性最小二乘

Math包的主要目的是提供一个拟合引擎,用于将数据集拟合到给定的模型。其中一部分是线性回归,其中数据被拟合到一个可以用基函数的线性组合来描述的模型。单个基函数在x上可以非线性变化,但不能依赖于参数。

示例

将数据集拟合到函数y(x) = a + bx + cx^2

单个基函数是1、x和x^2,我们希望找到参数a、b、c,使得模型通过最小二乘线性回归最佳拟合我们的数据。

这可以通过调用LinearFitingEngine来完成

<?php
require_once __DIR__ . '/vendor/autoload.php';

use \MOC\Math\DataSeries;
use \MOC\Math\Fitting\LinearFittingEngine;
use \MOC\Math\MathematicalFunction\Polynomial;

$data = DataSeries::fromArray(array(
	array(0, 1.0),
	array(1, 1.5),
	array(2, 3.0),
	array(3, 4.8),
	array(4, 6.1)
));
$functionToFitTo = new Polynomial(2);
$fitter = new LinearFittingEngine($data, $functionToFitTo);
$fitter->fit();
print "Result: " . $functionToFitTo . PHP_EOL; // Will render the function with is parameters
print 'Parameters: ' . PHP_EOL;
print_r($functionToFitTo->getParameters()) . PHP_EOL;
print 'Chi^: ' . $fitter->getChiSquared() . PHP_EOL;

实际的求解是通过带有全主元选择的高斯-若尔当消元法完成的。这的缺点是矩阵对角化可能会遇到零主元元素,导致奇异矩阵。在这种情况下,将抛出异常。

我们可以实现一个处理这种情况更好的奇异值分解算法。这已经在待办事项列表中。

请注意,单独的点也可以设置误差,这将影响考虑这些误差的拟合算法。

$data = \MOC\Math\DataSeries::fromArray(array(
	array(0, 1.0, 0.1),
	array(1, 1.5, 0.2),
	array(2, 3.0, 0.09),
	array(3, 4.8, 0.11),
	array(4, 6.1, 0.15)
));

非线性回归或非线性最小二乘法

将数据拟合到非线性的模型需要不同的方法。而是需要一个迭代解决方案。从一个参数值开始,计算卡方值。然后使用Levenberg-Marquardt方法以智能的方式调整参数,重新计算卡方值。如果拟合更好,则保留这些参数,否则尝试另一组参数。继续这样做,直到卡方值达到最小。最小值可能不是全局最小值,因此,开始时参数的初始值应接近期望值非常重要。

此库还提供了解决此类问题的求解器。

求解器更容易出错,并且需要拟合的函数需要用变量的“最佳猜测”初始化,否则您可能会达到一个局部最小值,这并不是数据的最佳拟合。

示例

将数据序列拟合到由y(x) = a + b*exp(- ((x-c)/d)^2)描述的高斯函数,这正是这种问题。它也可以是(有限个)高斯函数的和,但这个例子只是单个高斯函数。高斯函数是通过实现NonLinearFittingEngine所需的NonLinearCombinationOfFunctions接口的\MOC\Math\MathematicalFunction\GaussianFunction实现的

<?php
require_once __DIR__ . '/vendor/autoload.php';

use \MOC\Math\DataSeries;
use \MOC\Math\Fitting\NonLinearFittingEngine;
use \MOC\Math\MathematicalFunction\GaussianFunction;

$data = DataSeries::fromArray(array(
	array(0.5, 0.25, 0.05),
	array(1.13, 0.7, 0.10),
	array(1.6, 1.8, 0.12),
	array(2.0, 2.8, 0.09),
	array(2.5, 1.8, 0.04),
	array(3.0, 0.7, 0.001),
	array(3.5, 0.2, 0.01)
));
$functionToFitTo = new GaussianFunction();

// Do a best guess on the parameters on basis of the dataset.
$bestGuess = array (0, 1.8, 1.8, 1);
$functionToFitTo->setParameters($bestGuess);

// In this example, we only solve for the b,c and d parameters, we keep the a fixed.
$fitter = new NonLinearFittingEngine($data, $functionToFitTo, array(FALSE, TRUE, TRUE, TRUE));
$fitter->fit();

print 'Result: ' . $functionToFitTo . PHP_EOL; // Will render the function with is parameters
print 'Parameters: ' . PHP_EOL;
print_r($functionToFitTo->getParameters()) . PHP_EOL;
print 'Chi^2: ' . $fitter->getChiSquared() . PHP_EOL;
print 'Iterations: ' . $fitter->getNumberOfIterations() . PHP_EOL;

在范围内评估函数

该库包含一些评估区间内函数的实用函数,当可视化数据时很有用。

<?php
require_once __DIR__ . '/vendor/autoload.php';
use \MOC\Math\DataSeries;
use \MOC\Math\MathematicalFunction\Polynomial;

$function = new \MOC\Math\Polynomial(2);
$function->setParameters(array(0.00, 1, 1));
$data = \MOC\Math\MathUtility::evaluateFunctionInInterval($function, -2.0, 2.0, 100); // Evalute from -2 to 2 in 100 steps

foreach ($data as $point) {
	printf("X: %4.2f\tY: %4.2f\n", $point->getX(), $point->getY());
}