Search code examples
rlinenls

Error in xy.coords with lines function when using nls in R


Error in xy.coords with lines function when using nls in R

I am running a nonlinear regression in R, for which the initial output looks good. However, when I try to plot the values with a line function, the line function itself gives the error “Error in xy.coords(x, y) : 'x' and 'y' lengths differ“. The lengths of the x and y data are the same, and I know that it is coming from the lines function specifically, as the data plots but the line does not. How do I get the line to plot correctly?

Generally my code looks like this:

x = c(400,240,230,130,117,28)
y = c(0,15,35,85,110,135)
df = data.frame(x,y)

plot(x,y,ylim=rev(range(y)),ylab='y',xlab='x',las=1)
m=nls(x~Y0*exp(-a*y),data=df,start=list(Y0=415,a=0.015))

b=0:415
y=predict(m,list(x=b))
lines(b,y,type="l",col="blue")
m

My only problem with this now is that the line will not plot (the points do). The regression by itself also runs fine, but I am wondering if there is something upstream of the lines function that is causing the error for the plotting of the regression line.


Solution

  • I suspect you need list(y=b) instead of list(x=b).

    Your x is the objective value (it's on the LHS of the nls regression) and y is the dependent value (RHS), and predict typically takes for its newdata= argument a list/frame of dependent variables.

    ynew <- predict(m, list(y = b))
    plot(x, y, ylim = rev(range(y)), ylab = 'y', xlab = 'x', las = 1)
    lines(b, ynew, type = "l", col = "blue")
    

    enter image description here

    FYI, I chose to use ynew instead of y since you've already used y for the length-6 prep data; while it's perfectly fine to reuse it, if you ever try to use x against y again, it will be mismatched.

    Further, generally I prefer to not use the x and y variables individually for just the purpose: instead, use df$x and df$y. Personally I tend to not define x/y at all in the first place, doing as I've done in my code block above, but when I have to define them externally first, I either remove them or rename them so I don't accidentally use them elsewhere. This means your plot is a little fragile, as is lines.

    For this, I suggest a slight modification to your code-flow:

    # changed
    df <- data.frame(x = c(400,240,230,130,117,28), y = c(0,15,35,85,110,135))
    # unchanged
    m <- nls(x ~ Y0*exp(-a*y), data = df, start = list(Y0 = 415, a = 0.015))
    # changed
    newdf <- data.frame(y = 0:415)
    newdf$b <- predict(m, newdf)
    plot(y ~ x, data = df, ylim = rev(range(df$y)), ylab = 'y', xlab = 'x', las = 1)
    lines(b ~ y, data = newdf, type = "l", col = "blue")
    

    The above produces the same plot and the same model results, but you are less fragile to accidental reuse of x or y when you're expecting different data. In general, in plots it's often best to always use data.frames so that your xs and ys pair up as expected.