Leonardo Castro
Leonardo Castro

Reputation: 107

Why can't the R function "nls" reach convergence when the initial curve looks so close to the points?

I want to fit the points below with the function whose first guess is represented by the line:

Points with curve with initially guessed parameters.

But I get the following error message:

Error in nls(y ~ A * (1 + erf(E * B * (x - C)/sqrt(2))) * dnorm(B * (x - : gradiente singular
Traceback:

1. nls(y ~ A * (1 + erf(E * B * (x - C)/sqrt(2))) * dnorm(B * (x - 
 .     C)) + D, data = fdata, start = list(A = A0, B = B0, C = C0, 
 .     D = D0, E = E0))

Here's my code:

erf <- function(x) 2 * pnorm(x * sqrt(2)) - 1

x = c(0.275, 0.325, 0.375, 0.425, 0.475, 0.525, 0.575, 0.625, 0.675, 0.725, 0.775, 0.825, 0.875, 0.925, 0.975)
y = c(33, 69, 48, 57, 51, 46, 36, 42, 26, 22, 22, 18, 16, 9, 5)

plot(x, y, xlim=c(0,1))

fdata = data.frame(x = x, y = y)

# Initial parameter values:
A0 = 1.3*max(y); B0 = 3.8*max(x); C0 = 1.0*x[which.max(y)]; D0 = 0.0; E0 = 4.0

# Plot curve with initial parameter values:
xfit0 = seq(from=0.0, to=1.0, length.out=100)
yfit0 = A0*(1+erf(E0*B0*(xfit0-C0)/sqrt(2)))*dnorm(B0*(xfit0-C0))+D0
lines(xfit0, yfit0, lwd=3)

# Do the fit:
fit <- nls(y~A*(1+erf(E*B*(x-C)/sqrt(2)))*dnorm(B*(x-C))+D, data = fdata, start = list(A = A0, B = B0, C = C0, D = D0, E = E0))
lines(fdata$x, predict(fit), col="red", lwd=2)

Why can't nls converge even for such a close initial guess?

Upvotes: 1

Views: 216

Answers (1)

G. Grothendieck
G. Grothendieck

Reputation: 270195

There aren't enough points to get a decent fit, particularly in some regions of the graph. Add some points using spline interpolation and then fit. To do that replace the last two lines of code in the question with these:

fdata.s <- spline(fdata)
fit <- nls(y~A*(1+erf(E*B*(x-C)/sqrt(2)))*dnorm(B*(x-C))+D, data = fdata.s, 
  start = list(A = A0, B = B0, C = C0, D = D0, E = E0))
lines(fdata.s$x, fitted(fit), col="red", lwd=2)

screenshot

Upvotes: 2

Related Questions