Optimized fitting coefficients for better fitting - r
I'm running a nonlinear least squares using the minpack.lm package.
However, for each group in the data I would like optimize (minimize) fitting parameters like similar to Python's minimize function.
The minimize() function is a wrapper around Minimizer for running an
optimization problem. It takes an objective function (the function
that calculates the array to be minimized), a Parameters object, and
several optional arguments.
The reason why I need this is that I want to optimize fitting function based on the obtained fitting parameters to find global fitting parameters that can fit both of the groups in the data.
Here is my current approach for fitting in groups,
df <- data.frame(y=c(replicate(2,c(rnorm(10,0.18,0.01), rnorm(10,0.17,0.01))),
c(replicate(2,c(rnorm(10,0.27,0.01), rnorm(10,0.26,0.01))))),
DVD=c(replicate(4,c(rnorm(10,60,2),rnorm(10,80,2)))),
gr = rep(seq(1,2),each=40),logic=rep(c(1,0),each=40))
the fitting equation of these groups is
fitt <- function(data) {
fit <- nlsLM(y~pi*label2*(DVD/2+U1)^2,
data=data,start=c(label2=1,U1=4),trace=T,control = nls.lm.control(maxiter=130))
}
library(minpack.lm)
library(plyr) # will help to fit in groups
fit <- dlply(df, c('gr'), .fun = fitt) #,"Die" only grouped by Waferr
> fit
$`1`
Nonlinear regression model
model: y ~ pi * label2 * (DVD/2 + U1)^2
data: data
label2 U1
2.005e-05 1.630e+03
$`2`
label2 U1
2.654 -35.104
I need to know are there any function that optimizes the sum-of-squares to get best fitting for both of the groups.
We may say that you already have the best fitting parameters as the residual sum-of-squares but I know that minimizer can do this but I haven't find any similar example we can do this in R.
ps. I made it up the numbers and fitting lines.
Not sure about r, but having least squares with shared parameters is usually simple to implement.
A simple python example looks like:
import matplotlib
matplotlib.use('Qt4Agg')
from matplotlib import pyplot as plt
from random import random
from scipy import optimize
import numpy as np
#just for my normal distributed errord
def boxmuller(x0,sigma):
u1=random()
u2=random()
ll=np.sqrt(-2*np.log(u1))
z0=ll*np.cos(2*np.pi*u2)
z1=ll*np.cos(2*np.pi*u2)
return sigma*z0+x0, sigma*z1+x0
#some non-linear function
def f0(x,a,b,c,s=0.05):
return a*np.sqrt(x**2+b**2)-np.log(c**2+x)+boxmuller(0,s)[0]
# residual function for least squares takes two data sets.
# not necessarily same length
# two of three parameters are common
def residuals(parameters,l1,l2,dataPoints):
a,b,c1,c2 = parameters
set1=dataPoints[:l1]
set2=dataPoints[-l2:]
distance1 = [(a*np.sqrt(x**2+b**2)-np.log(c1**2+x))-y for x,y in set1]
distance2 = [(a*np.sqrt(x**2+b**2)-np.log(c2**2+x))-y for x,y in set2]
res = distance1+distance2
return res
xList0=np.linspace(0,8,50)
#some xy data
xList1=np.linspace(0,7,25)
data1=np.array([f0(x,1.2,2.3,.33) for x in xList1])
#more xy data using different third parameter
xList2=np.linspace(0.1,7.5,28)
data2=np.array([f0(x,1.2,2.3,.77) for x in xList2])
alldata=np.array(zip(xList1,data1)+zip(xList2,data2))
# rough estimates
estimate = [1, 1, 1, .1]
#fitting; providing second length is actually redundant
bestFitValues, ier= optimize.leastsq(residuals, estimate,args=(len(data1),len(data2),alldata))
print bestFitValues
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xList1, data1)
ax.scatter(xList2, data2)
ax.plot(xList0,[f0(x,bestFitValues[0],bestFitValues[1],bestFitValues[2] ,s=0) for x in xList0])
ax.plot(xList0,[f0(x,bestFitValues[0],bestFitValues[1],bestFitValues[3] ,s=0) for x in xList0])
plt.show()
#output
>> [ 1.19841984 2.31591587 0.34936418 0.7998094 ]
If required you can even make your minimization yourself. If your parameter space is sort of well behaved, i.e. approximately parabolic minimum, a simple Nelder Mead method is quite OK.
Related
How do I perform Non Linear Least Squares in R with a pre determined lag structure
Suppose I want to estimate the parameters of the following model: $y_t = beta0 (sum_{i=1}^p w(delta;i) x_{t-i})$. Latex version of the equation: https://i.stack.imgur.com/POOlD.png Where y_t and x_{t-i} are known data points, wdelta follows an exponential Almon lag structure with two parameters delta1 and delta2(see image). And beta0 is the common parameter. Generating some data for x and y y <- seq(1:10) x <- rnorm(10,2,5) The literature suggests estimating the model parameters using NLS and the Gaussian Newton Method. R does have a function gaussNewton however I am not sure how to use this. How do I approach the estimation of the parameters beta0,delta1 and delta2? Wikipedia suggest: https://en.wikipedia.org/wiki/Non-linear_least_squares, however I feel like this is not appropriate in this case. The nls function in R is unable to deal with predefined lag structures so this is not an option either. Maybe I could write out the function in the form of the sum of squared residuals and use the optim function? Another option could be to use the nlm function. nonls <- function(delta1,delta2,i,p) { z <- exp(delta1 * i + delta2 *i) wdelta[i] <- exp(delta1 * i + delta2 *i)/sum(z[1:i]) ssr <- (y[i]- (beta0 * wdelta[i] * x[i:p]))^2 } optim(ssr) I look forward to your suggestions.
PyQt-Fit's NonParamRegression vs. R's loess
Are those two functions more or less equivalent? For example, if I have an R call like: loess(formula = myformula, data = mydata, span = myspan, degree = 2, normalize = TRUE, family = "gaussian") How can I obtain the same or similar result with PyQt-Fit? Should I simply call the smooth.NonParamRegression function (http://pythonhosted.org/PyQt-Fit/NonParam_tut.html) with method=npr_methods.LocalPolynomialKernel(q=2)? What about other parameters, such as span, and family? UPDATE I do realize the two implementations are likely not equivalent (https://www.statsdirect.com/help/nonparametric_methods/loess.htm). But any comments regarding "approximating" their outcomes are appreciated.
Statsmodels has a LOWESS implementation (http://www.statsmodels.org/devel/generated/statsmodels.nonparametric.smoothers_lowess.lowess.html). Check out this post on the difference between LOESS and LOWESS: https://stats.stackexchange.com/questions/161069/difference-between-loess-and-lowess Quick example on how to use statsmodels' lowess function in Python import numpy as np import statsmodels.api as sm lowess = sm.nonparametric.lowess Generate two random arrays, x and y: x = np.random.rand(100, 1) y = np.random.rand(100, 1) Run the lowess function (Frac refers to bandwidth. Note that frac and it are set arbitrarily. Also, not all parameters are specified here, some are set to default. For more, see the official documentation): results = lowess(y, x, frac=0.05, it=3) The results are stored in a two-dimensional array. The first column contains the sorted x (exog) values and the second column the associated estimated y (endog) values. If, for instance, you'd like to construct the residuals, you can proceed as follows: res = y - results[:,1]
Fitting experimental data points to different cumulative distributions using R
I am new to programming and using R software, so I would really appreciate your feedback to the current problem that I am trying to solve. So, I have to fit a cumulative distribution with some function (two/three parameter function). This seems to be pretty straight-forward task, but I've been buzzing around this now for some time. Let me show you what are my variables: x=c(0.01,0.011482,0.013183,0.015136,0.017378,0.019953,0.022909,0.026303,0.0302,0.034674,0.039811,0.045709,0.052481,0.060256,0.069183,0.079433,0.091201,0.104713,0.120226,0.138038,0.158489,0.18197,0.20893,0.239883,0.275423,0.316228,0.363078,0.416869,0.47863,0.549541,0.630957,0.724436,0.831764,0.954993,1.096478,1.258925,1.44544,1.659587,1.905461,2.187762,2.511886,2.884031,3.311311,3.801894,4.365158,5.011872,5.754399,6.606934,7.585776,8.709636,10,11.481536,13.182567,15.135612,17.378008,19.952623,22.908677,26.30268,30.199517,34.673685,39.810717,45.708819,52.480746,60.255959,69.183097,79.432823,91.201084,104.712855,120.226443,138.038426,158.489319,181.970086,208.929613,239.883292,275.42287,316.227766,363.078055,416.869383,478.630092,549.540874,630.957344,724.43596,831.763771,954.992586,1096.478196) y=c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00044816,0.00127554,0.00221488,0.00324858,0.00438312,0.00559138,0.00686054,0.00817179,0.00950625,0.01085188,0.0122145,0.01362578,0.01514366,0.01684314,0.01880564,0.02109756,0.0237676,0.02683182,0.03030649,0.0342276,0.03874555,0.04418374,0.05119304,0.06076553,0.07437854,0.09380666,0.12115065,0.15836926,0.20712933,0.26822017,0.34131335,0.42465413,0.51503564,0.60810697,0.69886817,0.78237651,0.85461023,0.91287236,0.95616228,0.98569093,0.99869001,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999) This is the plot where I set up x-axis as log: After some research, I have tried with Sigmoid function, as found on one of the posts (I can't add link since my reputation is not high enough). This is the code: # sigmoid function definition sigmoid = function(params, x) { params[1] / (1 + exp(-params[2] * (x - params[3]))) } # fitting code using nonlinear least square fitmodel <- nls(y~a/(1 + exp(-b * (x-c))), start=list(a=1,b=.5,c=25)) # get the coefficients using the coef function params=coef(fitmodel) # asigning to y2 sigmoid function y2 <- sigmoid(params,x) # plotting y2 function plot(y2,type="l") # plotting data points points(y) This led me to some good fitting results (I don't know how to quantify this). But, when I look at the at the plot of Sigmuid fitting function I don't understand why is the S shape now happening in the range of x-values from 40 until 7 (looking at the S shape should be in x-values from 10 until 200). Since I couldn't explain this behavior, I thought of trying Weibull equation for fitting, but so far I can't make the code running. To sum up: Do you have any idea why is the Sigmoid giving me that weird fitting? Do you know any better two or three parameter equation for this fitting approach? How could I determine the goodness of fit? Something like r^2?
# Data df <- data.frame(x=c(0.01,0.011482,0.013183,0.015136,0.017378,0.019953,0.022909,0.026303,0.0302,0.034674,0.039811,0.045709,0.052481,0.060256,0.069183,0.079433,0.091201,0.104713,0.120226,0.138038,0.158489,0.18197,0.20893,0.239883,0.275423,0.316228,0.363078,0.416869,0.47863,0.549541,0.630957,0.724436,0.831764,0.954993,1.096478,1.258925,1.44544,1.659587,1.905461,2.187762,2.511886,2.884031,3.311311,3.801894,4.365158,5.011872,5.754399,6.606934,7.585776,8.709636,10,11.481536,13.182567,15.135612,17.378008,19.952623,22.908677,26.30268,30.199517,34.673685,39.810717,45.708819,52.480746,60.255959,69.183097,79.432823,91.201084,104.712855,120.226443,138.038426,158.489319,181.970086,208.929613,239.883292,275.42287,316.227766,363.078055,416.869383,478.630092,549.540874,630.957344,724.43596,831.763771,954.992586,1096.478196), y=c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00044816,0.00127554,0.00221488,0.00324858,0.00438312,0.00559138,0.00686054,0.00817179,0.00950625,0.01085188,0.0122145,0.01362578,0.01514366,0.01684314,0.01880564,0.02109756,0.0237676,0.02683182,0.03030649,0.0342276,0.03874555,0.04418374,0.05119304,0.06076553,0.07437854,0.09380666,0.12115065,0.15836926,0.20712933,0.26822017,0.34131335,0.42465413,0.51503564,0.60810697,0.69886817,0.78237651,0.85461023,0.91287236,0.95616228,0.98569093,0.99869001,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999)) # sigmoid function definition sigmoid = function(x, a, b, c) { a * exp(-b * exp(-c * x)) } # fitting code using nonlinear least square fitmodel <- nls(y ~ sigmoid(x, a, b, c), start=list(a=1,b=.5,c=-2), data = df) # plotting y2 function plot(df$x, predict(fitmodel),type="l", log = "x") # plotting data points points(df) The function I used is the Gompertz function and this blog post explains why R² shouldn't be used with nonlinear fits and offers an alternative.
After going through different functions and different data-sets I have found the best solution that gives the answers to all of my questions posted. The code is as it follows for the data-set stated in question: df <- data.frame(x=c(0.01,0.011482,0.013183,0.015136,0.017378,0.019953,0.022909,0.026303,0.0302,0.034674,0.039811,0.045709,0.052481,0.060256,0.069183,0.079433,0.091201,0.104713,0.120226,0.138038,0.158489,0.18197,0.20893,0.239883,0.275423,0.316228,0.363078,0.416869,0.47863,0.549541,0.630957,0.724436,0.831764,0.954993,1.096478,1.258925,1.44544,1.659587,1.905461,2.187762,2.511886,2.884031,3.311311,3.801894,4.365158,5.011872,5.754399,6.606934,7.585776,8.709636,10,11.481536,13.182567,15.135612,17.378008,19.952623,22.908677,26.30268,30.199517,34.673685,39.810717,45.708819,52.480746,60.255959,69.183097,79.432823,91.201084,104.712855,120.226443,138.038426,158.489319,181.970086,208.929613,239.883292,275.42287,316.227766,363.078055,416.869383,478.630092,549.540874,630.957344,724.43596,831.763771,954.992586,1096.478196), y=c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00044816,0.00127554,0.00221488,0.00324858,0.00438312,0.00559138,0.00686054,0.00817179,0.00950625,0.01085188,0.0122145,0.01362578,0.01514366,0.01684314,0.01880564,0.02109756,0.0237676,0.02683182,0.03030649,0.0342276,0.03874555,0.04418374,0.05119304,0.06076553,0.07437854,0.09380666,0.12115065,0.15836926,0.20712933,0.26822017,0.34131335,0.42465413,0.51503564,0.60810697,0.69886817,0.78237651,0.85461023,0.91287236,0.95616228,0.98569093,0.99869001,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999,0.99999999)) library(drc) fm <- drm(y ~ x, data = df, fct = G.3()) #The Gompertz model G.3() plot(fm) #Gompertz Coefficients and residual standard error summary(fm) The plot after fitting
confidence interval around predicted value from complex inverse function
I'm trying to get a 95% confidence interval around some predicted values, but am not capable of achieving this. Basically, I estimated a growth curve like this: set.seed(123) dat=data.frame(size=rnorm(50,10,3),age=rnorm(50,5,2)) S <- function(t,ts,C,K) ((C*K)/(2*pi))*sin(2*pi*(t-ts)) sommers <- function(t,Linf,K,t0,ts,C) Linf*(1-exp(-K*(t-t0)-S(t,ts,C,K)+S(t0,ts,C,K))) model <- nls(size~sommers(age,Linf,K,t0,ts,C),data=dat, start=list(Linf=10,K=4.7,t0=2.2,C=0.9,ts=0.1)) I have independent size measurements, for which I would like to predict the age. Therefore, the inverse of the function, which is not very straightforward, I calculated like this: model.out=coef(model) S.out <- function(t) ((model.out[[4]]*model.out[[2]])/(2*pi))*sin(2*pi*(t-model.out[[5]])) sommers.out <- function(t) model.out[[1]]*(1-exp(-model.out[[2]]*(t-model.out[[3]])-S.out(t)+S.out(model.out[[3]]))) inverse = function (f, lower = -100, upper = 100) { function (y) uniroot((function (x) f(x) - y), lower = lower, upper = upper)[1] } sommers.inverse = inverse(sommers.out, 0, 25) x= sommers.inverse(10) #this works with my complete dataset, but not with this fake one Although this works fine, I need to know the confidence interval (95%) around this estimate (x). For linear models there is for example "predict(... confidence=)". I could also bootstrap the function somehow to get the quantiles associated with the parameters (didn't find how), to then use the extremes of those to calculate the maximum and minimum values predictable. But that doesn't really look like the good way of doing this.... Any help would be greatly appreciated. EDIT after answer: So this worked (explained in the book of Ben Bolker, see answer): vmat = mvrnorm(1000, mu = coef(mfit), Sigma = vcov(mfit)) dist = numeric(1000) for (i in 1:1000) {dist[i] = sommers_inverse(9.938,vmat[i,])} quantile(dist, c(0.025, 0.975)) On the rather bad fake data I gave, this works of course rather horrible. But on the real data (which I have a problem recreating), this is ok!
Unless I'm mistaken, you're going to have to use either regular (parametric) bootstrapping or a method called either "population predictive intervals" (e.g., see section 5 of chapter 7 of Bolker 2008), which assumes that the sampling distributions of your parameters are multivariate Normal. However, I think you may have bigger problems, unless I've somehow messed up your model in adapting it ... Generate data (note that random data may actually bad for testing your model - see below ...) set.seed(123) dat <- data.frame(size=rnorm(50,10,3),age=rnorm(50,5,2)) S <- function(t,ts,C,K) ((C*K)/(2*pi))*sin(2*pi*(t-ts)) sommers <- function(t,Linf,K,t0,ts,C) Linf*(1-exp(-K*(t-t0)-S(t,ts,C,K)+S(t0,ts,C,K))) Plot the data and the initial curve estimate: plot(size~age,data=dat,ylim=c(0,16)) agevec <- seq(0,10,length=1001) lines(agevec,sommers(agevec,Linf=10,K=4.7,t0=2.2,ts=0.1,C=0.9)) I had trouble with nls so I used minpack.lm::nls.lm, which is slightly more robust. (There are other options here, e.g. calculating the derivatives and providing the gradient function, or using AD Model Builder or Template Model Builder, or using the nls2 package.) For nls.lm we need a function that returns the residuals: sommers_fn <- function(par,dat) { with(c(as.list(par),dat),size-sommers(age,Linf,K,t0,ts,C)) } library(minpack.lm) mfit <- nls.lm(fn=sommers_fn, par=list(Linf=10,K=4.7,t0=2.2,C=0.9,ts=0.1), dat=dat) coef(mfit) ## Linf K t0 C ts ## 10.6540185 0.3466328 2.1675244 136.7164179 0.3627371 Here's our problem: plot(size~age,data=dat,ylim=c(0,16)) lines(agevec,sommers(agevec,Linf=10,K=4.7,t0=2.2,ts=0.1,C=0.9)) with(as.list(coef(mfit)), { lines(agevec,sommers(agevec,Linf,K,t0,ts,C),col=2) abline(v=t0,lty=2) abline(h=c(0,Linf),lty=2) }) With this kind of fit, the results of the inverse function are going to be extremely unstable, as the inverse function is many-to-one, with the number of inverse values depending sensitively on the parameter values ... sommers_pred <- function(x,pars) { with(as.list(pars),sommers(x,Linf,K,t0,ts,C)) } sommers_pred(6,coef(mfit)) ## s(6)=9.93 sommers_inverse <- function (y, pars, lower = -100, upper = 100) { uniroot(function(x) sommers_pred(x,pars) -y, c(lower, upper))$root } sommers_inverse(9.938, coef(mfit)) ## 0.28 If I pick my interval very carefully I can get back the correct answer ... sommers_inverse(9.938, coef(mfit), 5.5, 6.2) Maybe your model will be better behaved with more realistic data. I hope so ...
Solution of varying coefficients ODE
I have a set of observed raw data and use 2nd order ODE to fit the data y''+b1(t)y'+b0(t)y = 0 The b1 and b0 are time-dependent and I use principal differential analysis(PDA) (R-package: fda, function: pda.fd)to get the estimate of b1(t) and b0(t) . To check the validity of the estimates of b1(t) and b0(t), I use collocation method (R-package bvpSolve, function:bvpcol) to get the numerical solution of the ODE and compare the solution with the smoothing curve fitting of the raw data. My question is that my numerical solution from bvpcol can caputure the shape of the fitting curve but not for the value of the function. There are different in term of some constant multiples. (Since I am not allowed to post images,please see the link for figure) See the figure of my output. The gray dot is my raw data, the red line is Fourier expansion of the raw data, the green line is numerical solution of bvpcol function and the blue line the green-line/1.62. We can see the green line can capture the shape but with values that are constant times of fourier expansion. I fit several other data and have similar situation but different constant. I am wondering it is the problem of numerical solution of ODE or some other reasons and how to solve this problem to get a good accordance between numerical solution(green) and true Fourier expansion? Any help and idea is appreciated! Here is a raw data and code: RData is here library(fda) library(bvpSolve) # load the data load('y.RData') tvec = 1:length(y) tvec = (tvec-min(tvec))/(max(tvec)-min(tvec)) # create basis fbasis = create.fourier.basis(c(0,1),nbasis=nbasis) bbasis = create.bspline.basis(c(0,1),norder=8,nbasis=47) bfdPar = fdPar(bbasis) yfd = smooth.basis(tvec,y,fbasis)$fd yfdlist = list(yfd) bwtlist = rep(list(bfdPar),2) # PDA fit bwt = pda.fd(yfdlist,bwtlist)$bwtlist # output of estimated coefficients beta0.fd<-bwt[[1]]$fd beta1.fd<-bwt[[2]]$fd # define the vary-coef function in terms of t fbeta0<-function(t)eval.fd(t,beta0.fd) fbeta1<-function(t)eval.fd(t,beta1.fd) # define 2nd order ODE fun2 <- function(t,y,pars) { with(as.list(c(y,pars)),{ beta0 = pars[[1]]; beta1 = pars[[2]]; dy1 = y[2] dy2 = -beta1(t)*y[2]-beta0(t)*y[1] return(list(c(dy1,dy2))) }) } # BVP yinit<-c(p1[1],NA) yend<-c(p1[length(p1)],NA) t<-seq(tvec[1],tvec[length(tvec)],0.005) col<-bvpcol(yini=yinit,yend=yend,x=t,func=fun2,parms=c(fbeta0,fbeta1),atol=1e-5,islin=T) # plot output plot(col[,1],col[,2],col='green',type='l') points(tvec,p1,col='darkgray') lines(yfd,col='red',lwd=2) lines(col[,1],col[,2],col='green',type='l') lines(col[,1],col[,2]/1.62,col='blue',type='l',lwd=2,lty=4) legend('topleft',col=c('green','darkgray','red','blue'), legend=c('ODE solution','raw data','basis curve fitting','ODE solution/1.62'),lty=1)