Forcing nls to fit a curve passing through a specified point

前端 未结 2 1374
悲哀的现实
悲哀的现实 2020-12-21 13:55

I\'m trying to fit a Boltzmann sigmoid 1/(1+exp((x-p1)/p2)) to this small experimental dataset:

xdata <- c(-60,-50,-40,-30,-20,-10,-0,10)
yda         


        
2条回答
  •  囚心锁ツ
    2020-12-21 14:28

    It is not possible to force the fit to go through 0 using the function you provide (without an off-set) as we discussed in the comments below your question.

    However, you can force the curve to go through other data points by setting weights for individual data points. So e.g. if you give a data point A a weight equals 1 and a data point B a weight equals 1000, the data point B is much more important (in terms of the contribution to the sum of residuals which is going to be minimized) for the fit than A and the fit will therefore be forced to go through B.

    Here is the entire code and I explain it in more detail below:

    # your data
    xdata <- c(-60, -50, -40, -30, -20, -10, -0, 10)
    ydata <- c(0.04, 0.09, 0.38, 0.63, 0.79, 1, 0.83, 0.56)
    plot(xdata, ydata, ylim=c(0, 1.1))
    fit <-nls(ydata ~ 1 / (1 + exp((xdata - p1) / p2)), start=list(p1=mean(xdata), p2=-5))
    
    # plot the fit
    xr = data.frame(xdata = seq(min(xdata), max(xdata), len=200))
    lines(xr$xdata, predict(fit, newdata=xr))
    
    # set all weights to 1, do the fit again; the plot looks identical to the previous one
    we = rep(1, length(xdata))
    fit2 = nls(ydata ~ 1 / (1 + exp((xdata - p1) / p2)), weights=we, start=list(p1=mean(xdata) ,p2=-5))
    lines(xr$xdata, predict(fit2, newdata=xr), col='blue')
    
    # set weight for the data point -30,0.38, and fit again
    we[3] = 1000
    fit3 = nls(ydata ~ 1 / (1 + exp((xdata - p1) / p2)), weights=we, start=list(p1=mean(xdata), p2=-5))
    lines(xr$xdata, predict(fit3, newdata=xr), col='red')
    legend('topleft', c('fit without weights', 'fit with weights 1', 'weighted fit for -40,0.38'), 
           lty=c(1, 1, 1),
           lwd=c(2.5, 2.5, 2.5),
           col=c('black', 'blue', 'red'))
    

    The output looks as follows; as you can see the fit now goes through the desired data point (red line):

    So what is going on: I first fit as you did, then I fit with weights whereby all weights are set to 1; therefore, the plot looks identical to the one before and the blue line hides the black line. Then - for fit3 - I change the weight for the third data point to 1000 which means that it is now much more "important" for the least square fit than the other points and the new fit goes through this data point (the red line).

    Here is also a second example where I changed the line

    we[3] = 1000
    

    to

    we[2] = 1000
    

    which forces the fit to go through the second data point:

    If you want to get more information about the weights argument you can read here: documentation

提交回复
热议问题