# Double exponential fit in R

I am not yet very good friends with R, but I hope to be! Right now I'm having a lot of troubles with fitting a model to a set of data.

My data looks like:

data <- as.data.frame(cbind(c(0,5,10,15,30,50,60,90,120,180,240),c(0.48, 1.15, 1.03, 1.37, 5.55, 16.77, 20.97, 21.67, 10.50, 2.28, 1.58)))
colnames(data) <- c("times","RNA")


The column RNA is dependent on the time.

I have been trying to fit a double exponential curve to the data, but I don't think it is working so well. This is the code for the fitting:

library(minpack.lm)
fit <- nlsLM(RNA~z*(exp(-v*times)-exp(-u*times)),data=test_2,start=list(u=0.67,v=0.018,z=0.98))

plot(data,pch=19)
lines(data[1],fitted(fit),col=2)


which produces this:

I am not very happy with the fit, since it doesn't really include the "top" of the data (max?). Do any of you have any suggestions about what to do next? I have no clue about what to try else, but maybe another exponential?

Do you have some suggested reading for this, since I'm not looking for a direct answer but more for some ideas...if it's possible!

• Hi, based on your code and the plot, you seem to have no obvious difficulty with the coding aspect of this problem, which means you might want to ask Cross Validated. Judging from the curve, I think it is really not bad. Keep in mind that you don't want to overfit, as overfitting is typically associated with low generalizability. You could, though, try fitting several related models: en.wikipedia.org/wiki/… Feb 13, 2017 at 14:54
• @DavidC. Oh okay, thanks! Maybe I really don't trust my ability in R, so this was just to make sure everything is correct :) I will see if Cross Validated can help me then!
– Gliz
Feb 13, 2017 at 14:59
• @Gliz May be you can check out splines. With enough degree of freedom, splines can fit all the points exactly, but as David said this may not be a good idea. R code example Spline primer
Feb 13, 2017 at 15:07
• I often find it useful to have a go at manually changing the parameters in the model to see if I can get it to look better than the fit does. That can often tell you if the fit's got stuck in a local minimum or something like that. I don't know how to do it in R though.
– DavidW
Feb 13, 2017 at 17:06
• You are asking essentially the same question here twice. Feb 13, 2017 at 18:10

The functional form of your double exponential curve doesn't capture the shape of your data. Notice that the best fit that you obtained is concave down at $t=0$, where you might expect a gentle slope and positive second derivative.

The "double exponential" functional form is usually associated with the Laplace distribution: $$f(t):= a\exp\left(-\frac{|t-c|}b\right)$$ where $a$ measures the height, $b$ the 'slope', and $c$ the location of the peak. Here's an R implementation using the nls function:

test <- data.frame(times=c(0,5,10,15,30,50,60,90,120,180,240), RNA=c(0.48, 1.15, 1.03, 1.37, 5.55, 16.77, 20.97, 21.67, 10.50, 2.28, 1.58))

nonlin <- function(t, a, b, c) { a * (exp(-(abs(t-c) / b))) }
nlsfit <- nls(RNA ~ nonlin(times, a, b, c), data=test, start=list(a=25, b=20, c=75))

with(test, plot(times, RNA, pch=19, ylim=c(0,40)))
tseq <- seq(0,250,.1)
pars <- coef(nlsfit)
print(pars)
lines(tseq, nonlin(tseq, pars[1], pars[2], pars[3]), col=2)


The resulting fit is OK, but it doesn't capture the right skew in your data set, nor does it do a good job near zero:

One way to fix this is to use a different slope for the left half and the right half of the peak, yielding a four-parameter model:

nonlin <- function(t, a, bl, br, c) {
ifelse(t < c, a * exp((t-c) / bl), a * exp(-(t-c) / br))
}

nlsfit <- nls(RNA ~ nonlin(times, a, bl, br, c), data=test, start=list(a=25, bl=20, br=20, c=75))


With this modification the fit is quite a bit better:

Another approach is to take the log of the time values to remove the skew. In essence you're fitting a double exponential relationship between RNA and log(time):

nonlin <- function(t, a, b, c) { a * (exp(-(abs(log(t)-c) / b))) }
nlsfit <- nls(RNA ~ nonlin(times, a, b, c), data=test, start=list(a=25, b=1, c=4))


With this three parameter model the fit is closer to zero at $t=0$ (perhaps too close to zero), while the right tail has gotten thicker:

Finally, you can fit a lognormal distribution to your data. In this case you are positing a bell shape for RNA vs log(time):

nonlin <- function(t, a, b, c) { a * (exp(-(log(t)-c)^2 / b)) }
nlsfit <- nls(RNA ~ nonlin(times, a, b, c), data=test, start=list(a=25, b=1, c=4))


Here the resulting fit is perhaps overly aggressive: it doesn't become positive until well past $t=0$.

BTW, here is an R implementation of the fit to the Gumbel distribution, which is sometimes known as the double exponential. This is the functional form used in James Phillips' answer, and perhaps what you intended to code up.

nonlin <- function(t, a, b, c) { (a/b) * exp( -(t-c)/b - exp(-(t-c)/b) ) }
nlsfit <- nls(RNA ~ nonlin(times, a, b, c), data=test, start=list(a=1000, b=50, c=75))


Which functional form is the right one? Which one to adopt depends not only on the quality of the fit but also on the mechanism that generated the data. Is there a theoretical/biological reason for any of these functional forms? OTOH, maybe none of these forms is justified, and you just want to describe what you've observed using a small number of parameters.

• I must say the form of the last plot fits the mechanism best, and that's why I started with a double exponential fit - and my supervisor suggested it :P
– Gliz
Feb 14, 2017 at 21:19
• Can I ask if you have an idea of how to determine what fit is the best in R? is that the R^2 I should be looking at?
– Gliz
Feb 14, 2017 at 21:26
• @Gliz Hang on, see my edit: the Gumbel distribution is what you probably intended. Feb 14, 2017 at 21:26
• @Gliz You should compute something like $R^2$, which R can return easily. I imagine the Gumbel fit might win on those grounds, and if your supervisor suggested it, then there may have been some theoretical justification for it as well :) Feb 14, 2017 at 21:29
• I think that the Gumbel looks very convincing! Thank you very much :D
– Gliz
Feb 14, 2017 at 21:34

I also tried fitting the data you posted to a double exponential equation with similar results.

If you might use a different equation, I was able to fit the data you posted to the Extreme Value peak equation with good results as shown in the attached graph. It seems to fit well near the origin also.

y = a * exp(-exp(-((x-b)/c))-((x-b)/c)+1.0)
Fitting target of lowest sum of squared absolute error = 5.6978486427595030E+00
a =  2.3314874489295441E+01
b =  7.2755959004396104E+01
c =  3.2201742765438965E+01

Degrees of freedom (error): 8
Degrees of freedom (regression): 2
Chi-squared: 5.69784864276
R-squared: 0.993392917433
Model F-statistic: 601.410929778
Model F-statistic p-value: 1.90563154145e-09
Model log-likelihood: -11.9903875636
AIC: 2.72552501157
BIC: 2.83404190415
Root Mean Squared Error (RMSE): 0.719712609484

a = 2.3314874489295441E+01
std err: 4.62920E-01
t-stat: 3.42673E+01
p-stat: 5.74859E-10
95% confidence intervals: [2.17459E+01, 2.48838E+01]

b = 7.2755959004396104E+01
std err: 1.20647E+00
t-stat: 6.62384E+01
p-stat: 3.00249E-12
95% confidence intervals: [7.02231E+01, 7.52889E+01]

c = 3.2201742765438965E+01
std err: 1.57788E+00
t-stat: 2.56355E+01
p-stat: 5.74859E-09
95% confidence intervals: [2.93051E+01, 3.50984E+01]

Coefficient Covariance Matrix
[ 0.64995768 -0.01513588 -0.67014552]
[-0.01513588  1.69393358  0.83602731]
[-0.67014552  0.83602731  2.2154065 ]


• Thanks! This was exactly what I was looking for! What fitting command did you use in R? Just so I can read up on it :)
– Gliz
Feb 14, 2017 at 9:42
• I did not use R. Years ago I ran a online curve and surface fitting web site named zunzun.com, and this had a "function finder" with a genetic algorithm front end for initial parameter estimation. I used the code to fit your data to over two hundred known equations with three or less parameters, and chose this one from the sorted results. The Python BSD-licensed source code is here if you would like to try it: github.com/zunzun/zunzunsite3 Feb 14, 2017 at 14:02
• Oh that is quite cool! I will check it out definitely
– Gliz
Feb 14, 2017 at 18:22