4

In R I use nls to do a nonlinear least-squares fit. How then do I plot the model function using the values of the coefficients that the fit provided?

(Yes, this is a very naive question from an R relative newbie.)

murray
  • 737
  • 2
  • 10
  • 28
  • 8
    Scroll down near the bottom of `?nls` and there's an example for you. – joran Mar 29 '12 at 03:48
  • Sorry, but the example there is of little help; I just don't understand what several of the arguments to plot and lines there mean. Please help! My data frame is called `xy`, with components `x` and `y`, and has `dim(xy)` being `17 2`. And I've named `fitted` the result of the nls call. How do I plot the model function for the found values of the coefficients, along with the original data points? – murray Mar 29 '12 at 13:22
  • @Murray: Take a look at this line from an example: `lines(x, predict(nlmod), col=2)` . This works because `predict` knows how to calculate the predicted y-values from the output of `nls` . Alternatively, take a little time to sift through all the components of your `fitted` object, find the coefficients, and use them to write your own fit-function. That will give you some confidence that `nls` did what you want. – Carl Witthoft Mar 29 '12 at 14:03

4 Answers4

14

Using the first example from ?nls and following the example I pointed you to line by line achieves the following:

#This is just our data frame
DNase1 <- subset(DNase, Run == 1)
DNase1$lconc <- log(DNase1$conc)
#Fit the model
fm1DNase1 <- nls(density ~ SSlogis(lconc, Asym, xmid, scal), DNase1)

#Plot the original points
# first argument is the x values, second is the y values
plot(DNase1$lconc,DNase1$density)

#This adds to the already created plot a line
# once again, first argument is x values, second is y values
lines(DNase1$lconc,predict(fm1DNase1))

The predict method for a nls argument is automatically returning the fitted y values. Alternatively, you add a step and do

yFitted <- predict(fm1DNase1)

and pass yFitted in the second argument to lines instead. The result looks like this:

enter image description here

Or if you want a "smooth" curve, what you do is to simply repeat this but evaluate the function at more points:

r <- range(DNase1$lconc)
xNew <- seq(r[1],r[2],length.out = 200)
yNew <- predict(fm1DNase1,list(lconc = xNew))

plot(DNase1$lconc,DNase1$density)
lines(xNew,yNew)
joran
  • 169,992
  • 32
  • 429
  • 468
  • 1
    No, the `lines` is $not$ what I wanted to plot (along with the original data points). Rather, I wanted to plot the model function using the values of the coefficients found from the call to nls. How to I extract those coefficients? And then how do I substitute them into the form of the model function? Specifically, the model is `y ~ k * x^n`. I now want to plot the (smooth) curve given by the equation `y = k * x^n` with the values of k and n as found from `nls`. – murray Mar 29 '12 at 19:21
  • 1
    @murray Um...calling `lines(...,predict())` does _exactly_ what you just described. – joran Mar 29 '12 at 19:24
  • @murray Namely, it evaluates the _fitted_ function, using the estimated coefficients, as the original data points. If you want the function evaluated at _different_ points, you'll have to supply them to `predict`. – joran Mar 29 '12 at 19:25
  • @murray I added another example showing exactly how to make the curve smoother. – joran Mar 29 '12 at 19:40
  • 1
    For the values of the coefficients found by nls, I want to plot the resulting model as a smooth function for those coefficients -- the same way I'd plot, say, y = 5*x^2. Is there no way to plot a function in R without explicitly giving a list of x-values? – murray Mar 29 '12 at 19:55
  • @murray Well, there's `curve`, but that's just doing exactly what I've done by hand, and it will be way _more_ work shoehorning everything into an expression or function. Your confusion may be in thinking that there is actually such a thing as a "smooth curve" when plotting. There isn't. There's just a function evaluated at enough points that your eye can't tell the difference. _Every_ plot in _every_ language is at some point explicitly plotting a sequence of x values and connecting them with a line. – joran Mar 29 '12 at 20:09
  • 1
    thanks for all the responses! I'm well aware that to a computer there's no such thing as a smooth curve for plotting -- that it's all just sampling points and drawing segments (and ultimately, mapping that to appropriate pixels). But with systems such as Mathematica, I don't have to think about that and instead can work at a "higher" level of abstraction, namely, simply asking for a plot of a function and letting the system do all the dirty work (including adaptive sampling of input values according to rapidity of change of values). – murray Mar 30 '12 at 00:21
3

coef(x) returns the coefficients for regression results x.

model<-nls(y~a+b*x^k,my.data,list(a=0.,b=1.,k=1))
plot(y~x,my.data)
a<-coef(model)[1]
b<-coef(model)[2]
k<-coef(model)[3]
lines(x<-c(1:10),a+b*x^k,col='red')

For example.

Graham Giller
  • 607
  • 5
  • 2
1

I know what you want (I'm a Scientist). This isn't it, but at least shows how to use 'curve' to plot your fitting function over any range, and the curve will be smooth. Using the same data set as above:

nonlinFit <- nls(density ~ a - b*exp(-c*conc), data = DNase1, start = list(a=1, b=1, c=1) )

fitFnc <- function(x) predict(nonlinFit, list(conc=x))

curve(fitFnc, from=.5, to=10)

or,

curve(fitFnc, from=8.2, to=8.4)

or,

curve(fitFnc, from=.1, to=50) # well outside the data range

or whatever (without setting up a sequence of evaluation points first).

I'm a rudimentary R programmer, so I don't know how to implement (elegantly) something like ReplaceAll ( /. ) in Mathematica that one would use to replace occurrences of the symbolic parameters in the model, with the fitted parameters. This first step works although it looks horrible:

myModel <- "a - b*exp(-c*conc)"

nonlinFit <- nls(as.formula(paste("density ~", myModel)), data = DNase1, start = list(a=1, b=1, c=1) )

It leaves you with a separate 'model' (as a character string), that you might be able to make use of with the fitted parameters ... cleanly (NOT digging out a, b, c) would simply use nonlinFit ... not sure how though.

Community
  • 1
  • 1
SteveK9
  • 19
  • 3
-1

The function "curve" will plot functions for you.