Christopher Battles
2012-May-11 20:33 UTC
[R] NLS sensitivity to start= values or poles in data range
Greetings R-help! I'm fairly new to R and am trying to expand my knowledge beyond using R for simple summary statistics and basic tests. To that end I am attempting to write an interactive R-script that will perform a general rational function fit to a given dataset based on the example given at http://www.itl.nist.gov/div898/handbook/pmd/section6/pmd64.htm. The problem that I seem to have is that the fitted function is nearly identical to the NIST results when I use their initial values (these are representative values that are close to actual data, but not taken from the dataset) for generating the starting point for the nls() routine. However if I try to use actual data points extracted from the dataset for the preliminary fit, the fitted function can often fail to approximate the data. One culprit that I suspect may be that a pole (real root of the denominator) of the initial fit is laying in the range of the data, since the NIST starting values produce a preliminary fit that does not have any poles over the data range. Unfortunately, I'm not familiar enough with the nls() routines to pass judgement. The attached code reads in the data from the web, performs the preliminary fit using lm(), passes that to nls() and plots the data, starting values, preliminary fit and final fit. The code block in the middle defining indsubset and depsubset contain three different options for the starting values. I thank you all in advance for any help or insights. Simplified code follows... Actual code for the interactive version is available at http://www.schrodingersghost.com/RationalFit-0.2.R # Start of Code # Read and plot the data InputData <- read.table("http://itl.nist.gov/div898/strd/nls/data/LINKS/DATA/Hahn1.dat", header=0, row.names=NULL, col.names=c("CTE","K"), skip=60) indvar <- 2 depvar <- 1 indvarname <- names(InputData)[indvar] depvarname <- names(InputData)[depvar] attach(InputData) matplot(InputData[indvar], InputData[depvar], type='p', pch=4, xlab = indvarname, ylab = depvarname, main = bquote(paste(.(depvarname), " vs ", .(indvarname))) ) # Specify values for determining nls start ######################################## # Actual Data equally spaced in data set #indsubset <- c(14.13, 72.77, 163.48, 273.13, 419.51, 549.53, 850.98) #depsubset <- c(0.08, 7.267, 13.974, 16.337, 17.767, 18.61, 21.085) # Actual Data closest values to NIST starting values #indsubset <- c(14.13,31.30,40.03,50.24,120.25,202.14,845.97) #depsubset <- c(0.08,1.089,2.241,3.697,12.005,15.190,20.830) # NIST Starting Values indsubset <- c(10,30,40,50,120,200,800) depsubset <- c(0,2,3,5,12,15,20) # ######################################### points(indsubset,depsubset, type="p", pch=19, col="red") # Generate starting values for nls per NIST process initmodel <- (depsubset ~ I(indsubset^1) + I(indsubset^2) + I(indsubset^3) + I(-1 * indsubset^1 * depsubset) + I(-1 * indsubset^2 * depsubset) + I(-1 * indsubset^3 * depsubset)) InputDatalm <- lm(initmodel) summary(InputDatalm) fitted.values(InputDatalm) nlsstart <- coef(InputDatalm) names(nlsstart) <- c("A0", "A1", "A2", "A3", "B1", "B2", "B3") nlsstart <- as.list(nlsstart) attach(nlsstart) curve((A0*x^0+A1*x^1+A2*x^2+A3*x^3)/(1+B1*x^1+B2*x^2+B3*x^3), from = min(K), to = max(K), add=TRUE, col="blue", lty=5, lwd=2 ) # Check the poles of the starting function poles <- polyroot(c(1,B1,B2,B3)) cat((poles), '\n') # Run the nls using above values as starting points fullmodel <- {CTE ~ (A0 * K^0 + A1 * K^1 + A2 * K^2 + A3 * K^3)/ (1 + B1 * K^1 + B2 * K^2 + B3 * K^3)} rationalfit <- nls(fullmodel,start=nlsstart) summary(rationalfit) rationalcoef <- as.list(coef(rationalfit)) attach(rationalcoef) curve((A0*x^0+A1*x^1+A2*x^2+A3*x^3)/(1+B1*x^1+B2*x^2+B3*x^3), from = min(K), to = max(K), add=TRUE, lwd=2) legend(600,5,c("Starting Values","Initial Value Fit","Full Model Fit","Data"), lty=c(0,5,1,0), pch=c(19,NA,NA,4), col=c("red","blue","black","black"), plot=TRUE) # Check the poles of the final function poles <- polyroot(c(1,B1,B2,B3)) cat((poles), '\n') # Cleanup detach(InputData) detach(rationalcoef) # End of Code Thank you, Christopher Battles