f3000x
f3000x

Reputation: 13

fitting non linear function to data : singular gradient issue

I am trying to fit data to a non linear model, but I am getting "singular gradient" message when I build the model.

here is the data:

    > astrodata
    temperature intensity
    1      277.15       121
    2      282.15       131
    3      287.15       153
    4      292.15       202
    5      297.15       311

The function:

    y=   a * exp(-b * temperature) + c 

What I did so far:

    > temperature <- astrodata$temperature
    temperature
    [1] 277.15 282.15 287.15 292.15 297.15
    >  intensity <- astrodata$intensity
    > c.0 <- min(temperature)*0.5
    > c.0 <- min(intensity)*0.5
    > model.0 <- lm(log(intensity - c.0) ~ temperature, data=astrodata)
    > start <- list(a=exp(coef(model.0)[1]), b=coef(model.0)[2], c=c.0)
    > 
    > model <- nls(intensity ~ a * exp(-b * temperature) + c, data = astrodata, start = start)
    Error in nls(intensity ~ a * exp(b * temperature) + c, data = astrodata,  : 
    singular gradient   

Does anybody has an idea how to solve this ?

Upvotes: 1

Views: 211

Answers (3)

G. Grothendieck
G. Grothendieck

Reputation: 270448

The model is linear in a and c and only nonlinear in b. That suggests we try the "plinear" algorithm. It has the advantage that only the non-linear parameters require starting values.

Note that the formula specification for that algorithm is different and has a RHS which is a matrix with one column per linear parameter.

model <- nls(intensity ~ cbind(exp(-b * temperature), 1), data = astrodata, 
   start = start["b"], algorithm = "plinear")

giving:

> model
Nonlinear regression model
  model: intensity ~ cbind(exp(-b * temperature), 1)
   data: astrodata
         b      .lin1      .lin2 
-1.598e-01  4.728e-19  1.129e+02 
 residual sum-of-squares: 0.003853

Number of iterations to convergence: 5 
Achieved convergence tolerance: 2.594e-07

Also:

plot(intensity ~ temperature, astrodata)
lines(fitted(model) ~ temperature, astrodata)

screenshot

Note: Based on the comment below you don't really need an nls model and it may be good enough to just use geom_line

p <- ggplot(astrodata, aes(temperature, intensity)) + geom_point()
p + geom_line()

or splines:

p + geom_line(data = data.frame(spline(temperature, intensity)), aes(x, y))

Upvotes: 1

James Phillips
James Phillips

Reputation: 4657

One possibility is to double each data point, adding very tiny variations to the doubled data like so:

temperature intensity
1      277.15       121
2      282.15       131
3      287.15       153
4      292.15       202
5      297.15       311
11      277.15000001       121.000001
12      282.15000001       131.000001
13      287.15000001       153.000001
14      292.15000001       202.000001
15      297.15000001       311.000001

In the original data set, each point effectively has the same weight of 1.0, and in the "doubled" data set again each point effectively has the same weight of 2.0 so you get the same fitted parameter values but no error.

Upvotes: 0

Travis Heeter
Travis Heeter

Reputation: 14084

Your data isn't varied enough.

nls uses least squares to work. This is a measurement of the distance between the model and the data points. If there is no distance, nls doesn't work. Your model fits the data exactly, this is called "zero-residual" data. Hence

singular gradient matrix at initial parameter estimates.

It's an overly complicated error message that simply means "There is no error to measure."

You only have 5 (x,y) combos, so this error is almost guaranteed using non-linear analysis with so little data. Use different data or more data.

Upvotes: 0

Related Questions