0

I'm trying to solve the following problem:

x <- c(0.11557577149788574,2.1552479877306925,2.5505873377321175,1.0995198836006757,3.710225290286669,2.386870541964232,0.11557577149788574,0.11557577149788574,2.1552479877306925,2.5505873377321175,1.0995198836006757,3.710225290286669,2.386870541964232,0.11557577149788574)
y <- c(16500,11500,11500,13630,7000,11995,13490,16500,11500,11500,13630,7000,11995,13490)

df <- data.frame(x, y)

m <- nls(y ~ I(a*exp(-b*x)+c), data=df, start=list(a=14000, b=1, c=100), control=nls.control(maxiter=10000, minFactor=1e-7, tol=1e-5, printEval=F, warnOnly=F))

But, even if I try to change the start values and the nls control no value is returned. What I'm doing wrong? I need more points to solve that problem?

Thank you!

M0N0NE
  • 63
  • 1
  • 2
  • 8

3 Answers3

4

You need better starting values.

First order the data frame in increasing x so that plotting will work out nicely.

If we set c to zero we can fit the simpler model to log(y) ~ A - b*x which is linear in all coefficients so it can be fit via lm and no starting values are needed.

Use the starting value for b given by that simpler model. Also note that a and c enter the full model linearly so we can use the plinear algorithm of nls which eliminates the need to give starting values to those coefficients:

o <- order(df$x)
df_o <- df[o, ] # order it by increasing x

fm0 <- lm(log(y) ~ I(-x), df_o) # simpler model to get better starting values

st <- list(b = coef(fm0)[[2]])
fm <- nls(y ~ cbind(a = exp(-b*x), c = 1), df_o, start = st, alg = "plinear")

plot(df_o, col = "red", pch = 20)
lines(fitted(fm) ~ x, df_o)

The result is the following where .lin.a is a and .lin.c is c:

> fm
Nonlinear regression model
  model: y ~ cbind(a = exp(-b * x), c = 1)
   data: df_o
         b     .lin.a     .lin.c 
   -0.4903 -1529.0253 16509.4421 
 residual sum-of-squares: 10555038

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

Here we show input data as red points and draw lines through the fitted result:

enter image description here

G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • Really nice answer, I was wondering why do you put I(-x), is it because of the data? and for example what would happen if I have a value starting in 0, we would have problems with the log(0), is there a way to fix it? Thanks! – Rosa Maria Mar 29 '21 at 08:39
  • 1
    In an lm formula -x means remove variable x but I(-x) means negative x. Regarding log(0), lm has a subset argument which can be used to remove the y=0 row. – G. Grothendieck Mar 29 '21 at 12:45
  • But does removing the 0 values would affect the fitting ? – Rosa Maria Mar 29 '21 at 12:47
  • The log is only used to get starting values. – G. Grothendieck Mar 29 '21 at 12:48
  • I am trying to implement your reasoning in a very small example, but I have errors that I don't understand. It's a set of 13 points x = (33,47 ,61 ,75 ,82 ,89 ,97,103,110 ,117 ,124, 131 ,138) and y = ( 0.0000 ,133.9472, 249.4672, 350.6899 ,434.4080 , 499.8723 , 509.9748 , 712.6437,751.8508 , 897.5127, 1133.8490, 1140.2780, 1727.9490). I want to implement this model a*exp(b*x)+c or a*exp(b*x), but I get errors in the lm for the estimation of the parameters. Could you please help me or give me some advice, please? – Rosa Maria Mar 29 '21 at 12:54
  • 1
    Use df_o <- data.frame(x, y); fm0 <- lm(log(y) ~ I(-x), df_o, subset = -1) and then use the line which defines st and the nls line from the answer which I have modified slightly to handle both situations. – G. Grothendieck Mar 29 '21 at 13:11
  • Ohh this time it worked, but now it intrigues me, most of the posts suggest using this approach to have a better control of the parameter: theta.0 <- min(Volume) * 0.5 model.0 <- lm((log(Volume - theta.0)) ~ Days, data=X) alpha.0 <- -exp(coef(model.0)[1]) beta.0 <- coef(model.0)[2]. But When I applied it again it generates error because of the lm creates NAs, so sorry for this silly question but how do you know which exponential model is the best ? For example in your original answer you set c = 0 , but what does that mean ? how can I fit this a*exp(-b*x)+c – Rosa Maria Mar 29 '21 at 13:38
  • You are referring to variables not defined in the question so I am not going to address that but if you have two nls fits fm and fm2 and want to compare then use anova(fm, fm2) . – G. Grothendieck Mar 29 '21 at 13:38
  • Ok, I will do that Thanks. – Rosa Maria Mar 29 '21 at 13:41
  • THANK YOU @G. Grothendieck! The starting values issue was making me nuts! Your solution worked splendidly for me. – Tavaro Evanis May 12 '22 at 23:35
0

There is not enough information in your dataset to estimate the coefficient of this non-linear model reliably. There is practically no curvature in your data. If you use Levenberg-Marquardt it gets stuck with parameter values that are more or less equal to the linear fit:

plot(y~x)

library(minpack.lm)
m <- nlsLM(y ~ a*exp(-b*x)+c, data=df, start=list(a=14000, b=1, c=100),  
           control = nls.lm.control(maxiter = 1000))
summary(m)
#Formula: y ~ a * exp(-b * x) + c
#
#Parameters:
#    Estimate Std. Error t value Pr(>|t|)
#a  2.308e+06  6.822e+08   0.003    0.997
#b  8.506e-04  2.518e-01   0.003    0.997
#c -2.292e+06  6.822e+08  -0.003    0.997
#
#Residual standard error: 1250 on 11 degrees of freedom

#Number of iterations till stop: 94 
#Achieved convergence tolerance: 1.49e-08
#Reason stopped: Number of calls to `fcn' has reached or exceeded `maxfev' == 400.

lines(x, predict(m), col="green", lwd=2)
abline(lm(y~x), col="red", lty=2, lwd=2)

resulting plot

If the model is based on science, the range of your measured x values is probably too small.

Roland
  • 127,288
  • 10
  • 191
  • 288
0

The problem seems to be in your data.

If you try plot( y ~ x ) then it does not look exponential: indeed it seems to be slightly faster than linear, especially if you see that there are in fact two points in your data with the x y values 3.710225 7000 so you are trying to fit a convex curve to data which suggests a concave curve. Two suggestions:

  1. Remove those two points:

    df2 <- df[df$y!=7000,]

    m2 <- nls(y ~ I(a*exp(-b*x)+c), data=df2, start=list(a=14000, b=1, c=100), control=nls.control(maxiter=10000, minFactor=1e-7, tol=1e-5, printEval=F, warnOnly=F))

to give

Nonlinear regression model
  model: y ~ I(a * exp(-b * x) + c)
   data: df2
        a         b         c 
1.418e+04 1.202e-01 1.030e+03 
 residual sum-of-squares: 9781328

Number of iterations to convergence: 14 
Achieved convergence tolerance: 2.573e-06
  1. Have your start point suggest a concave curve by reversing the signs of a and b

    m <- nls(y ~ I(a*exp(-b*x)+c), data=df, start=list(a=-14000, b=-1, c=100), control=nls.control(maxiter=10000, minFactor=1e-7, tol=1e-5, printEval=F, warnOnly=F))

to give

Nonlinear regression model
  model: y ~ I(a * exp(-b * x) + c)
   data: df
         a          b          c 
-1529.0204    -0.4903 16509.4360 
 residual sum-of-squares: 10555038

Number of iterations to convergence: 7 
Achieved convergence tolerance: 7.471e-07
Henry
  • 6,704
  • 2
  • 23
  • 39
  • What does the I stand for in "y ~ I(a * exp(-b * x) + c)" ? – Will M Mar 22 '20 at 14:24
  • 1
    @William I took it from the original question. In R, it means [AsIs](https://stat.ethz.ch/R-manual/R-devel/library/base/html/AsIs.html) and in the function `formula` *it is used to inhibit the interpretation of operators such as "+", "-", "\*" and "^" as formula operators, so they are used as arithmetical operators* – Henry Mar 22 '20 at 14:35
  • Thanks for the explanation! :D – Will M Mar 23 '20 at 15:34