【问题标题】:Solve best fit polynomial and plot drop-down lines求解最佳拟合多项式并绘制下拉线
【发布时间】:2017-05-31 22:36:18
【问题描述】:

我在 Windows 10 上使用 R 3.3.1(64 位)。我有一个适合二阶多项式的 x-y 数据集。我想在 y=4 处求解 x 的最佳拟合多项式,并绘制从 y=4 到 x 轴的下拉线。

这将在数据帧 v1 中生成数据:

v1 <- structure(list(x = c(-5.2549, -3.4893, -3.5909, -2.5546, -3.7247, 
-5.1733, -3.3451, -2.8993, -2.6835, -3.9495, -4.9649, -2.8438, 
-4.6926, -3.4768, -3.1221, -4.8175, -4.5641, -3.549, -3.08, -2.4153, 
-2.9882, -3.4045, -4.6394, -3.3404, -2.6728, -3.3517, -2.6098, 
-3.7733, -4.051, -2.9385, -4.5024, -4.59, -4.5617, -4.0658, -2.4986, 
-3.7559, -4.245, -4.8045, -4.6615, -4.0696, -4.6638, -4.6505, 
-3.7978, -4.5649, -5.7669, -4.519, -3.8561, -3.779, -3.0549, 
-3.1241, -2.1423, -3.2759, -4.224, -4.028, -3.3412, -2.8832, 
-3.3866, -0.1852, -3.3763, -4.317, -5.3607, -3.3398, -1.9087, 
-4.431, -3.7535, -3.2545, -0.806, -3.1419, -3.7269, -3.4853, 
-4.3129, -2.8891, -3.0572, -5.3309, -2.5837, -4.1128, -4.6631, 
-3.4695, -4.1045, -7.064, -5.1681, -6.4866, -2.7522, -4.6305, 
-4.2957, -3.7552, -4.9482, -5.6452, -6.0302, -5.3244, -3.9819, 
-3.8123, -5.3085, -5.6096, -6.4557), y = c(0.99, 0.56, 0.43, 
2.31, 0.31, 0.59, 0.62, 1.65, 2.12, 0.1, 0.24, 1.68, 0.09, 0.59, 
1.23, 0.4, 0.36, 0.49, 1.41, 3.29, 1.22, 0.56, 0.1, 0.67, 2.38, 
0.43, 1.56, 0.07, 0.08, 1.53, -0.01, 0.12, 0.1, 0.04, 3.42, 0.23, 
0, 0.34, 0.15, 0.03, 0.19, 0.17, 0.2, 0.09, 2.3, 0.07, 0.15, 
0.18, 1.07, 1.21, 3.4, 0.8, -0.04, 0.02, 0.74, 1.59, 0.71, 10.64, 
0.64, -0.01, 1.06, 0.81, 4.58, 0.01, 0.14, 0.59, 7.35, 0.63, 
0.17, 0.38, -0.08, 1.1, 0.89, 0.94, 1.52, 0.01, 0.1, 0.38, 0.02, 
7.76, 0.72, 4.1, 1.36, 0.13, -0.02, 0.13, 0.42, 1.49, 2.64, 1.01, 
0.08, 0.22, 1.01, 1.53, 4.39)), .Names = c("x", "y"), class = "data.frame", row.names = c(NA, 
-95L))

这是绘制 y 与 x、绘制最佳拟合多项式并在 y=4 处绘制一条线的代码。

> attach(v1)
> # simple x-y plot of the data
> plot(x,y, pch=16)
> # 2nd order polynomial fit
> fit2 <- lm(y~poly(x,2,raw=TRUE))
> summary(fit2)
> # generate range of numbers for plotting polynomial
> xx <- seq(-8,0, length=50)
> # overlay best fit polynomial
>lines(xx, predict(fit2, data.frame(x=xx)), col="blue")
> # add horizontal line at y=4
> abline(h=4, col="red")
>

从图中可以明显看出 y=4 在 x 约为 -2 和 -6.5,但我想实际求解这些值的回归多项式。

理想情况下,我想要从红蓝线交叉点下降到 x 轴的线(即绘制终止于两个 y=4 解决方案的垂直 ablines)。如果这是不可能的,我会很高兴有好的旧垂直斜线一直向上走,只要它们处于正确的 x 解决方案值。

此图表示当 y>4 时将不符合规格的零件,因此我想使用下拉线突出显示将产生符合规格零件的 x 值范围。

【问题讨论】:

    标签: r plot regression solver


    【解决方案1】:

    您可以使用二次公式来计算值:

    betas <- coef(fit2)    # get coefficients
    betas[1] <- betas[1] - 4    # adjust intercept to look for values where y = 4
    
    # note degree increases, so betas[1] is c, etc.
    betas
    ##             (Intercept) poly(x, 2, raw = TRUE)1 poly(x, 2, raw = TRUE)2 
    ##               8.7555833               6.0807302               0.7319848 
    
    solns <- c((-betas[2] + sqrt(betas[2]^2 - 4 * betas[3] * betas[1])) / (2 * betas[3]), 
               (-betas[2] - sqrt(betas[2]^2 - 4 * betas[3] * betas[1])) / (2 * betas[3]))
    
    solns
    ## poly(x, 2, raw = TRUE)1 poly(x, 2, raw = TRUE)1 
    ##               -1.853398               -6.453783 
    
    segments(solns, -1, solns, 4, col = 'green')    # add segments to graph
    

    更简单(如果你能找到的话)是polyroot

    polyroot(betas)
    ## [1] -1.853398+0i -6.453783+0i
    

    由于它返回一个复数向量,如果您想将其传递给segments,则需要将其包装在as.numeric 中。

    【讨论】:

      【解决方案2】:

      我完全理解这个简单的二次多项式有解析解。我向您展示数值解决方案的原因是您在回归设置中提出了这个问题。当您有更复杂的回归曲线时,一般来说,数值解可能总是您的解。

      下面我将使用uniroot 函数。如果您不熟悉它,请先阅读此简短答案:Uniroot solution in R


      这是使用您的代码生成的图。你快到了。这是一个求根问题,您可以在数字上使用uniroot。让我们定义一个函数:

      f <- function (x) {
        ## subtract 4
        predict(fit2, newdata = data.frame(x = x)) - 4
        }
      

      从图中可以看出有两个根,一个在[-7, -6]里面,一个在[-3, -1]里面。我们使用uniroot 来查找两者:

      x1 <- uniroot(f, c(-7, -6))$root
      #[1] -6.453769
      
      x2 <- uniroot(f, c(-3, -1))$root
      #[1] -1.853406
      

      现在您可以将一条垂直线从这些点向下拖到 x 轴:

      y1 <- f(x1) + 4  ## add 4 back
      y2 <- f(x2) + 4  
      
      abline(h = 0, col = 4)  ## x-axis
      segments(x1, 0, x1, y1, lty = 2)
      segments(x2, 0, x2, y2, lty = 2)
      

      【讨论】:

        【解决方案3】:

        你有一个二次方程

        0.73198 * x^2 + 6.08073 * x + 12.75558 = 4
        OR
        0.73198 * x^2 + 6.08073 * x + 8.75558 = 0
        

        您可以只使用二次公式来解析解决这个问题。 R 给出两个根:

        (-6.08073 + sqrt(6.08073^2 -4*0.73198 * 8.75558)) / (2 * 0.73198)
        [1] -1.853392
        (-6.08073 - sqrt(6.08073^2 -4*0.73198 * 8.75558)) / (2 * 0.73198)
        [1] -6.453843
        

        abline(v=c(-1.853392, -6.453843))

        【讨论】:

          【解决方案4】:

          这里是另一种解决方案,基于this

          attach(v1)
          fit2 = lm(y~poly(x,2,raw=TRUE))
          xx = seq(-8,0, length=50)
          
          vector1 = predict(fit2, data.frame(x=xx)) 
          vector2= replicate(length(vector1),4)
          
          # Find points where vector1 is above vector2.
          above = vector1 > vector2
          
          # Points always intersect when above=TRUE, then FALSE or reverse
          intersect.points = which(diff(above)!=0)    
          
          # Find the slopes for each line segment.
          vector1.slopes = vector1[intersect.points+1] - vector1[intersect.points]
          vector2.slopes = vector2[intersect.points+1] - vector2[intersect.points]
          
          # Find the intersection for each segment.
          x.points = intersect.points + ((vector2[intersect.points] - vector1[intersect.points]) / (vector1.slopes-vector2.slopes))
          y.points = vector1[intersect.points] + (vector1.slopes*(x.points-intersect.points))
          
          #Scale x.points to the axis value of xx
          x.points = xx[1] + ((x.points - 1)/(49))*(xx[50]-xx[1])
          
          plot(xx, y = vector1, type= "l", col = "blue")
          points(x,y,pch = 20)
          lines(x = c(x.points[1],x.points[1]), y = c(0,y.points[1]), col='red')
          lines(x = c(x.points[2],x.points[2]), y = c(0,y.points[2]), col='red')
          

          【讨论】:

            【解决方案5】:

            已经提出了很多解决方案,这里是另一个。

            很明显,我们有兴趣找到满足多项式(二次)方程a_0 + a_1.x + a_2.x^2 = 4x 值,其中a_0, a_1, a_2 是拟合多项式的系数。我们可以将方程重写为标准二次方程ax^2+bx+c=0,并使用Sridhar's 公式使用拟合多项式的系数和多项式回归求根,如下所示:

            a <- fit2$coefficients[3]
            b <- fit2$coefficients[2]
            c <- fit2$coefficients[1] - 4
            
            as.numeric((-b + sqrt(b^2-4*a*c)) / (2*a))
            #[1] -1.853398
            as.numeric((-b-+ sqrt(b^2-4*a*c)) / (2*a))
            #[1] -6.453783
            

            我们也可以使用一些数值方法,例如 Newton-Raphson 来找到根(虽然有更快的数值方法,但这将解决我们的目的,而且速度也很快,在我的机器上使用 ~160 ms),因为我们从下面的代码可以看出,数值解和理论解是一致的。

            a <- fit2$coefficients  # fitted quadratic polynomial coefficients
            
            f <- function(x) {
              as.numeric(a[1] + a[2]*x + a[3]*x^2-4)
            }
            
            df <- function(x) {
              as.numeric(a[2] + 2*a[3]*x)
            } 
            
            Newton.Raphson <- function(x0) {
              eps <- 1e-6
              x <- x0
              while(TRUE) {
                x <- x0 - f(x0) / df(x0)
                if (abs(x - x0) < eps) {
                  return(x0)
                }
                x0 <- x
              }
            }
            
            t1 <- Sys.time()
            x1 <- Newton.Raphson(-10)
            x2 <- Newton.Raphson(10)
            x1
            #[1] -6.453783
            x2
            #[1] -1.853398
            s2
            print(paste('time taken to compute the roots:' ,Sys.time() - t1))
            #[1] "time taken to compute the roots: 0.0160109996795654"
            points(x1, 4, pch=19, col='green')
            points(x2, 4, pch=19, col='green')
            abline(v=x1, col='green')
            abline(v=x2, col='green')
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-03-31
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-08-05
              • 2022-11-04
              • 1970-01-01
              相关资源
              最近更新 更多