C# 中的非线性回归
Posted
技术标签:
【中文标题】C# 中的非线性回归【英文标题】:Non-linear regression in C# 【发布时间】:2011-12-09 04:14:17 【问题描述】:我正在寻找一种基于二维数据集生成非线性(最好是二次)曲线的方法,用于预测目的。现在我正在使用我自己的普通最小二乘法 (OLS) 实现来产生线性趋势,但我的趋势更适合曲线模型。我正在分析的数据是一段时间内的系统负载。
这是我用来产生线性系数的方程式:
我看过 Math.NET Numerics 和其他一些库,但它们要么提供 interpolation 而不是 regression(这对我没用),或者代码无法以某种方式工作。
任何人都知道任何可以产生这种曲线系数的免费开源库或代码示例?
【问题讨论】:
这正是我正在寻找的结果:www3.wolframalpha.com/input/… 另一个带有链接的 q:***.com/questions/882009/… 一个相关的代码项目:codeproject.com/KB/recipes/QuadraticRegression.aspx 前者是一组插值模型的链接,而后者给出了无法使用的不正确/不准确的结果,即使在将其转换为使用decimal
而不是double
之后也是如此。
标题错误。这是线性回归,因为多项式可以表示为参数的线性组合。公认的解决方案正是这样做的:将多项式分解为 Vandermonde 矩阵和参数向量的乘积。
【参考方案1】:
我会查看http://mathforum.org/library/drmath/view/53796.html 以尝试了解如何做到这一点。
那么this 有一个很好的实现,我认为它会对你有所帮助。
【讨论】:
那个实现对我不起作用。它开箱即用地给出了完全不正确的结果(系数为 0/1/0),经过一些调整后仍然完全不准确。与我之前发现的 Wolfram Alpha 结果完全不同。不幸的是,您提供的链接中的数学超出了我的范围。【参考方案2】:我不认为你想要非线性回归。即使你使用的是二次函数,它仍然被称为线性回归。您想要的称为多元回归。如果您想要一个二次项,您只需将 x 平方项添加到您的因变量。
【讨论】:
线性回归给了我一条线。我不知道如何编辑我发布的方程式来做任何不同的事情。我想得到这样的结果:www3.wolframalpha.com/input/… 使用公式。但不是 x1 x2 使用你的 x 和 x 平方。如果您仍然有问题,我会尝试在 excel 或伪代码中制作一个示例 calc。【参考方案3】:我使用了MathNet.Iridium 版本,因为它与.NET 3.5 和VS2008 兼容。该方法基于Vandermonde 矩阵。然后我创建了一个类来保存我的多项式回归
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,61,9", "x", "y");
for (int i = 0; i < 10; i++)
double x = (i * 0.5);
double y = poly.Fit(x);
Console.WriteLine("0,6:F21,9:F4", x, y);
[1,0.57,-0.15]
的计算系数与输出:
x y
0.00 1.0000
0.50 1.2475
1.00 1.4200
1.50 1.5175
2.00 1.5400
2.50 1.4875
3.00 1.3600
3.50 1.1575
4.00 0.8800
4.50 0.5275
与 Wolfram Alpha 的 quadratic 结果匹配。
编辑 1
为了达到您想要的效果,请尝试对 x_data
和 y_data
进行以下初始化:
Matrix points = new Matrix( new double[,] 1, 82.96 ,
2, 86.23 , 3, 87.09 , 4, 84.28 ,
5, 83.69 , 6, 89.18 , 7, 85.71 ,
8, 85.05 , 9, 85.58 , 10, 86.95 ,
11, 87.95 , 12, 89.44 , 13, 93.47 );
Vector x_data = points.GetColumnVector(0);
Vector y_data = points.GetColumnVector(1);
产生以下系数(从最低功率到最高)
Coef=[85.892,-0.5542,0.074990]
x y
0.00 85.8920
1.00 85.4127
2.00 85.0835
3.00 84.9043
4.00 84.8750
5.00 84.9957
6.00 85.2664
7.00 85.6871
8.00 86.2577
9.00 86.9783
10.00 87.8490
11.00 88.8695
12.00 90.0401
13.00 91.3607
14.00 92.8312
【讨论】:
完美解决了我的问题。谢谢:) 我优化升级了你的答案:) ***.com/a/12770686/1046374【参考方案4】:@ja72 代码很不错。但我将它移植到当前版本的Math.NET(据我了解,目前不支持MathNet.Iridium)并优化了代码大小和性能(例如,Math.Pow
函数由于性能缓慢而未在我的解决方案中使用) .
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++)
double v_j = v[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;
也可以在 github:gist 上找到。
【讨论】:
以上是关于C# 中的非线性回归的主要内容,如果未能解决你的问题,请参考以下文章