如何使用C ++生成符合2D散点图数据的方法

时间:2012-07-12 10:11:08

标签: c++ algorithm matlab curve-fitting linear-regression

我曾经使用MATLAB,对于我提出的问题,我可以使用p = polyfit(x,y,1)来估算板中散射数据的最佳拟合线。我想知道我可以依赖哪些资源来用C ++实现线拟合算法。我知道这个主题有很多算法,对我来说我希望算法应该快,同​​时它可以在MATLAB中获得相当的polyfit函数精度。

7 个答案:

答案 0 :(得分:10)

本页描述的算法比维基百科更容易,没有额外的步骤来计算方法等:http://faculty.cs.niu.edu/~hutchins/csci230/best-fit.htm。几乎从那里引用,在C ++中它是:

#include <vector>
#include <cmath>

struct Point {
  double _x, _y;
};
struct Line {
  double _slope, _yInt;
  double getYforX(double x) {
    return _slope*x + _yInt;
  }
  // Construct line from points
  bool fitPoints(const std::vector<Point> &pts) {
    int nPoints = pts.size();
    if( nPoints < 2 ) {
      // Fail: infinitely many lines passing through this single point
      return false;
    }
    double sumX=0, sumY=0, sumXY=0, sumX2=0;
    for(int i=0; i<nPoints; i++) {
      sumX += pts[i]._x;
      sumY += pts[i]._y;
      sumXY += pts[i]._x * pts[i]._y;
      sumX2 += pts[i]._x * pts[i]._x;
    }
    double xMean = sumX / nPoints;
    double yMean = sumY / nPoints;
    double denominator = sumX2 - sumX * xMean;
    // You can tune the eps (1e-7) below for your specific task
    if( std::fabs(denominator) < 1e-7 ) {
      // Fail: it seems a vertical line
      return false;
    }
    _slope = (sumXY - sumX * yMean) / denominator;
    _yInt = yMean - _slope * xMean;
    return true;
  }
};

请注意,如果点的“最佳”描述是垂直线,则此算法和维基百科(http://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line)中的算法都会失败。他们失败是因为他们使用

y = k*x + b 

线方程本质上不能描述垂直线。如果您还要涵盖垂直线描述数据点“最佳”的情况,则需要使用

的线拟合算法
A*x + B*y + C = 0

线方程。您仍然可以修改当前算法以生成该等式:

y = k*x + b <=>
y - k*x - b = 0 <=>
B=1, A=-k, C=-b

就上述代码而言:

B=1, A=-_slope, C=-_yInt

if检查分母等于// Fail: it seems a vertical line的“then”块中,生成以下行等式:

x = xMean <=>
x - xMean = 0 <=>
A=1, B=0, C=-xMean

我刚刚注意到我所引用的原始文章已被删除。此网页为线条拟合提出了一个不同的公式:http://hotmath.com/hotmath_help/topics/line-of-best-fit.html

double denominator = sumX2 - 2 * sumX * xMean + nPoints * xMean * xMean;
...
_slope = (sumXY - sumY*xMean - sumX * yMean + nPoints * xMean * yMean) / denominator;

公式完全相同,因为nPoints*xMean == sumXnPoints*xMean*yMean == sumX * yMean == sumY * xMean

答案 1 :(得分:6)

我建议从头开始编码。这是一个非常简单的C ++实现。您可以直接从这里的公式中为您的数据编码最小二乘拟合的截距和渐变(与polyfit的方法相同)

http://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line

这些是封闭式公式,您可以使用循环轻松评估自己。如果你使用更高度拟合,那么我建议使用矩阵库或更复杂的算法,但对于上面描述的简单线性回归,这就是你所需要的。对于这样的问题,矩阵和线性代数例程会有点过分(在我看来)。

答案 2 :(得分:4)

线的方程是A x + B y + C = 0.

所以很容易(当B不是那么接近于零时)转换为y =(-A / B)* x +( - C / B)

typedef double scalar_type;
typedef std::array< scalar_type, 2 > point_type;
typedef std::vector< point_type > cloud_type;

bool fit( scalar_type & A, scalar_type & B, scalar_type & C, cloud_type const& cloud )
{
    if( cloud.size() < 2 ){ return false; }

    scalar_type X=0, Y=0, XY=0, X2=0, Y2=0;

    for( auto const& point: cloud )
    { // Do all calculation symmetric regarding X and Y
        X  += point[0];
        Y  += point[1];
        XY += point[0] * point[1];
        X2 += point[0] * point[0];
        Y2 += point[1] * point[1];
    }

    X  /= cloud.size();
    Y  /= cloud.size();
    XY /= cloud.size();
    X2 /= cloud.size();
    Y2 /= cloud.size();

    A = - ( XY - X * Y ); //!< Common for both solution

    scalar_type Bx = X2 - X * X;
    scalar_type By = Y2 - Y * Y;

    if( fabs( Bx ) < fabs( By ) ) //!< Test verticality/horizontality
    { // Line is more Vertical.
        B = By;
        std::swap(A,B);
    }
    else
    {   // Line is more Horizontal.
        // Classical solution, when we expect more horizontal-like line
        B = Bx;
    }
    C = - ( A * X + B * Y );

    //Optional normalization:
    // scalar_type  D = sqrt( A*A + B*B );
    // A /= D;
    // B /= D;
    // C /= D;
    return true;
}

答案 3 :(得分:1)

您也可以使用或查看this implementation还有documentation here

答案 4 :(得分:1)

安装线可以通过不同方式完成。 最小二乘意味着最小化平方距离的总和。 但你可以采用另一个成本函数作为例子(非平方)距离。但通常你使用squred distance(最小二乘法)。 还有可能以不同的方式定义距离。 Normaly你只是用“y”轴来表示距离。但您也可以使用总/正交距离。在那里,距离是在x方向和y方向上计算的。如果您在x方向上也有错误(让它成为测量时间)并且您没有在数据中保存的确切时间开始测量,那么这可能是更合适的。对于最小二乘和最小二乘线,拟合算法以封闭形式存在。因此,如果您使用其中一个,您将获得与数据点的平方距离的最小总和的线。在你的定位中,你无法适应更好的路线。您可以将定义更改为另一个成本函数或以另一种方式定义距离的示例。

有许多关于将模型拟合到你能想到的数据中的东西,但是通常它们都使用“最小二乘线拟合”,你应该很好。但如果你有一个特殊情况,就有必要考虑一下你在做什么。在最短的时间内完成最少广场。考虑什么方法最适合你的问题包括理解数学,这可能需要不确定的时间: - )。

答案 5 :(得分:1)

注意:此答案不是此问题的答案,而是这个被标记为“重复”的“ Line closest to a set of points”(在我看来是不正确的),无法将新答案添加到它。

问题要求:

  

找到距所有点的距离最小的线?通过   距离我是指点与线之间的最短距离。

“点与线之间”距离的最常见解释是欧氏距离,而“从所有点”的最常见解释是距离之和(绝对值或平方值)。

当目标是最小化欧几里德距离的平方和时,不是要使用线性回归(LST)的算法。另外,线性回归不能导致垂直线。要使用的算法是“总最小二乘法”。有关问题的描述,请参见示例wikipedia,有关数学公式的详细信息,请参见数学堆栈交换中的this answer

答案 6 :(得分:0)

适合行y=param[0]x+param[1]只需执行此操作:

// loop over data:
{               
sum_x += x[i];
sum_y += y[i];
sum_xy += x[i] * y[i];
sum_x2 += x[i] * x[i];
}

// means
double mean_x = sum_x / ninliers;
double mean_y = sum_y / ninliers;

float varx = sum_x2 - sum_x * mean_x;
float cov = sum_xy - sum_x * mean_y;

//检查零varx

param[0] = cov / varx;
param[1] = mean_y - param[0] * mean_x;

有关该主题的更多信息http://easycalculation.com/statistics/learn-regression.php (公式是相同的,它们只是乘以N除以样本sz。)。如果你想使飞机适合3D数据使用类似的方法 - http://www.mymathforum.com/viewtopic.php?f=13&t=8793

免责声明:所有二次拟合都是线性的,并且在某种意义上它们可以降低参数中的噪声。但是,您可能会对数据中的降噪感兴趣。您可能还想忽略异常值,因为它们可以极大地解决您的解决方案。 RANSAC可以解决这两个问题。请参阅我的帖子: