我正在尝试将指数衰减函数拟合到在高 x 值时变为负数的 y 值,但无法nls
正确配置我的函数。
目标
我对衰减函数的斜率(根据某些来源)感兴趣。我如何得到这个斜率并不重要,但模型应该尽可能适合我的数据(即,如果拟合良好,线性化问题是可以接受的;请参阅“线性化”)。然而,之前关于该主题的作品使用了以下指数衰减函数(Stedmon 等人的封闭访问文章,等式 3):
S
我感兴趣的斜率在哪里K
,允许负值的校正因子和(即截距)a
的初始值。x
我需要在 R 中执行此操作,因为我正在编写一个函数,它将发色溶解有机物 (CDOM) 的原始测量值转换为研究人员感兴趣的值。
示例数据
由于数据的性质,我不得不使用 PasteBin。示例数据可在此处获得。
dt <-
将 PasteBin 中的代码编写并复制到您的 R 控制台。IE
dt <- structure(list(x = ...
数据如下所示:
library(ggplot2)
ggplot(dt, aes(x = x, y = y)) + geom_point()
时出现负 y 值。
试图找到解决方案nls
最初的尝试使用nls
产生了一个奇点,看到我刚刚看到参数的起始值,这应该不足为奇:
nls(y ~ a * exp(-S * x) + K, data = dt, start = list(a = 0.5, S = 0.1, K = -0.1))
# Error in nlsModel(formula, mf, start, wts) :
# singular gradient matrix at initial parameter estimates
按照这个答案,我可以尝试制作更好的拟合启动参数来帮助该nls
功能:
K0 <- min(dt$y)/2
mod0 <- lm(log(y - K0) ~ x, data = dt) # produces NaNs due to the negative values
start <- list(a = exp(coef(mod0)[1]), S = coef(mod0)[2], K = K0)
nls(y ~ a * exp(-S * x) + K, data = dt, start = start)
# Error in nls(y ~ a * exp(-S * x) + K, data = dt, start = start) :
# number of iterations exceeded maximum of 50
该函数似乎无法找到具有默认迭代次数的解决方案。让我们增加迭代次数:
nls(y ~ a * exp(-S * x) + K, data = dt, start = start, nls.control(maxiter = 1000))
# Error in nls(y ~ a * exp(-S * x) + K, data = dt, start = start, nls.control(maxiter = 1000)) :
# step factor 0.000488281 reduced below 'minFactor' of 0.000976562
更多错误。扔掉它!让我们强制函数给我们一个解决方案:
mod <- nls(y ~ a * exp(-S * x) + K, data = dt, start = start, nls.control(maxiter = 1000, warnOnly = TRUE))
mod.dat <- data.frame(x = dt$x, y = predict(mod, list(wavelength = dt$x)))
ggplot(dt, aes(x = x, y = y)) + geom_point() +
geom_line(data = mod.dat, aes(x = x, y = y), color = "red")
好吧,这绝对不是一个好的解决方案......
线性化问题
许多人已经成功地将他们的指数衰减函数线性化(来源:1、2、3)。在这种情况下,我们需要确保没有 y 值是负数或 0。让我们在计算机的浮点限制内使最小 y 值尽可能接近 0 :
K <- abs(min(dt$y))
dt$y <- dt$y + K*(1+10^-15)
fit <- lm(log(y) ~ x, data=dt)
ggplot(dt, aes(x = x, y = y)) + geom_point() +
geom_line(aes(x=x, y=exp(fit$fitted.values)), color = "red")
好多了,但是模型在低 x 值时不能完美地跟踪 y 值。
请注意,该nls
函数仍然无法适应指数衰减:
K0 <- min(dt$y)/2
mod0 <- lm(log(y - K0) ~ x, data = dt) # produces NaNs due to the negative values
start <- list(a = exp(coef(mod0)[1]), S = coef(mod0)[2], K = K0)
nls(y ~ a * exp(-S * x) + K, data = dt, start = start)
# Error in nlsModel(formula, mf, start, wts) :
# singular gradient matrix at initial parameter estimates
负值重要吗?
负值显然是测量误差,因为吸收系数不能为负。那么,如果我将 y 值设为正数呢?是我感兴趣的坡度。如果加法不影响坡度,我应该解决:
dt$y <- dt$y + 0.1
fit <- lm(log(y) ~ x, data=dt)
ggplot(dt, aes(x = x, y = y)) + geom_point() + geom_line(aes(x=x, y=exp(fit$fitted.values)), color = "red")
好吧,这并没有那么顺利......高 x 值显然应该尽可能接近零。
问题
我显然在这里做错了什么。使用 R 估计拟合在具有负 y 值的数据上的指数衰减函数的斜率的最准确方法是什么?