using MathNet.Numerics.LinearAlgebra;
public class PolynomialRegression
{
Vector x_data, y_data, coef;
int order;
public PolynomialRegression(Vector x_data, Vector y_data, int order)
{
if (x_data.Length != y_data.Length)
{
throw new IndexOutOfRangeException();
}
this.x_data = x_data;
this.y_data = y_data;
this.order = order;
int N = x_data.Length;
Matrix A = new Matrix(N, order + 1);
for (int i = 0; i < N; i++)
{
A.SetRowVector( VandermondeRow(x_data[i]) , i);
}
// Least Squares of |y=A(x)*c|
// tr(A)*y = tr(A)*A*c
// inv(tr(A)*A)*tr(A)*y = c
Matrix At = Matrix.Transpose(A);
Matrix y2 = new Matrix(y_data, N);
coef = (At * A).Solve(At * y2).GetColumnVector(0);
}
Vector VandermondeRow(double x)
{
double[] row = new double[order + 1];
for (int i = 0; i <= order; i++)
{
row[i] = Math.Pow(x, i);
}
return new Vector(row);
}
public double Fit(double x)
{
return Vector.ScalarProduct( VandermondeRow(x) , coef);
}
public int Order { get { return order; } }
public Vector Coefficients { get { return coef; } }
public Vector XData { get { return x_data; } }
public Vector YData { get { return y_data; } }
}
然后我像这样使用它:
using MathNet.Numerics.LinearAlgebra;
class Program
{
static void Main(string[] args)
{
Vector x_data = new Vector(new double[] { 0, 1, 2, 3, 4 });
Vector y_data = new Vector(new double[] { 1.0, 1.4, 1.6, 1.3, 0.9 });
var poly = new PolynomialRegression(x_data, y_data, 2);
Console.WriteLine("{0,6}{1,9}", "x", "y");
for (int i = 0; i < 10; i++)
{
double x = (i * 0.5);
double y = poly.Fit(x);
Console.WriteLine("{0,6:F2}{1,9:F4}", x, y);
}
}
}
public class PolynomialRegression
{
private int _order;
private Vector<double> _coefs;
public PolynomialRegression(DenseVector xData, DenseVector yData, int order)
{
_order = order;
int n = xData.Count;
var vandMatrix = new DenseMatrix(xData.Count, order + 1);
for (int i = 0; i < n; i++)
vandMatrix.SetRow(i, VandermondeRow(xData[i]));
// var vandMatrixT = vandMatrix.Transpose();
// 1 variant:
//_coefs = (vandMatrixT * vandMatrix).Inverse() * vandMatrixT * yData;
// 2 variant:
//_coefs = (vandMatrixT * vandMatrix).LU().Solve(vandMatrixT * yData);
// 3 variant (most fast I think. Possible LU decomposion also can be replaced with one triangular matrix):
_coefs = vandMatrix.TransposeThisAndMultiply(vandMatrix).LU().Solve(TransposeAndMult(vandMatrix, yData));
}
private Vector<double> VandermondeRow(double x)
{
double[] result = new double[_order + 1];
double mult = 1;
for (int i = 0; i <= _order; i++)
{
result[i] = mult;
mult *= x;
}
return new DenseVector(result);
}
private static DenseVector TransposeAndMult(Matrix m, Vector v)
{
var result = new DenseVector(m.ColumnCount);
for (int j = 0; j < m.RowCount; j++)
for (int i = 0; i < m.ColumnCount; i++)
result[i] += m[j, i] * v[j];
return result;
}
public double Calculate(double x)
{
return VandermondeRow(x) * _coefs;
}
}
4 回答
我使用了MathNet.Iridium版本,因为它与.NET 3.5和VS2008兼容 . 该方法基于Vandermonde矩阵 . 然后我创建了一个类来保存我的多项式回归
然后我像这样使用它:
用输出计算
[1,0.57,-0.15]
的系数:这匹配来自Wolfram Alpha的quadratic结果 .
Edit 1 要获得所需的拟合,请尝试以下初始化
x_data
和y_data
:产生以下系数(从最低功率到最高功率)
@ ja72代码非常好 . 但是我将它移植到Math.NET的现有版本上(我现在不支持MathNet.Iridium)并优化代码大小和性能(Math.Pow函数因为性能低而不能在我的解决方案中使用) .
它也可以在github:gist上找到 .
我不认为你想要非线性回归 . 即使您使用二次函数,它仍然称为线性回归 . 你想要的是多变量回归 . 如果你想要一个二次方,你只需要为你的因变量添加一个x平方项 .
我会看看http://mathforum.org/library/drmath/view/53796.html,试着了解它是如何完成的 .
然后this有一个很好的实现,我认为会帮助你 .