【问题标题】:fitting first order equation with nlme and lsoda用 nlme 和 lsoda 拟合一阶方程
【发布时间】:2019-10-08 19:48:12
【问题描述】:

我尝试使用nlmelsoda 拟合一阶微分模型。 这是基本思想:我首先定义允许生成微分方程解的函数:

library(deSolve)

ODE1 <- function(time, x, parms) {with(as.list(c(parms, x)), {
  import <- excfunc(time)
  dS <- import*k/tau - (S-yo)/tau 
  res <- c(dS)
  list(res)})}


solution_ODE1 = function(tau1,k1,yo1,excitation,time){
  excfunc <- approxfun(time, excitation, rule = 2)
  parms  <- c(tau = tau1, k = k1, yo = yo1, excfunc = excfunc)
  xstart = c(S = yo1)
  out <-  lsoda(xstart, time, ODE1, parms)
  return(out[,2])
}

然后我根据两个 ID 的等式生成数据:

time <- 0:49
excitation <- c(rep(0,10),rep(1,10),rep(0,10),rep(1,10),rep(0,10))
simu_data <- data.frame(signal = c(solution_ODE1(3,2,0.1,excitation,time)+rnorm(length(time),0,0.1),
                                   solution_ODE1(3.2,1.5,0.3,excitation,time)+rnorm(length(time),0,0.1)),
                        time = rep(time,2),
                        excitation = rep(excitation,2),
                        ID = rep(c("A","B"),each = length(time)))

这是它的样子:

library(ggplot2)
ggplot(simu_data)+
  geom_point(aes(time,signal,color = "signal"),size = 2)+
  geom_line(aes(time,excitation,color = "excitation"))+
  facet_wrap(~ID)

然后我尝试使用 nlme 来适应:

fit1 <- nlme(signal ~ solution_ODE1(damping,gain,eq,excitation,time),
             data = simu_data,
             fixed = damping + gain + eq ~1,
             random =  damping   ~ 1 ,
             groups = ~ ID,
             start = c(damping = 5, gain = 1,eq = 0))

我得到了这个错误,我没有得到:

eval(substitute(expr), data, enclos = parent.frame()) 中的错误: 找不到对象“k”

traceback 表明错误来自 ODE1 模型,该模型在生成值时有效。

16.    eval(substitute(expr), data, enclos = parent.frame()) 
15.    eval(substitute(expr), data, enclos = parent.frame()) 
14.    with.default(as.list(c(parms, x)), {
    import <- excfunc(time)
    dS <- import * k/tau - (S - yo)/tau
    res <- c(dS) ... 
13.    with(as.list(c(parms, x)), {
    import <- excfunc(time)
    dS <- import * k/tau - (S - yo)/tau
    res <- c(dS) ... 
12.    func(time, state, parms, ...) 
11.    Func2(times[1], y) 
10.    eval(Func2(times[1], y), rho) 
9.    checkFunc(Func2, times, y, rho) 
8.    lsoda(xstart, time, ODE1, parms) 
7.    solution_ODE1(damping, gain, eq, excitation, time) 
6.    eval(model, data.frame(data, pars)) 
5.    eval(model, data.frame(data, pars)) 
4.    eval(modelExpression[[2]], envir = nlEnv) 
3.    eval(modelExpression[[2]], envir = nlEnv) 
2.    nlme.formula(signal ~ solution_ODE1(damping, gain, eq, excitation, 
    time), data = simu_data, fixed = damping + gain + eq ~ 1, 
    random = damping ~ 1, groups = ~ID, start = c(damping = 5, 
        gain = 1, eq = 0)) 
1.    nlme(signal ~ solution_ODE1(damping, gain, eq, excitation, time), 
    data = simu_data, fixed = damping + gain + eq ~ 1, random = damping ~ 
        1, groups = ~ID, start = c(damping = 5, gain = 1, eq = 0)) 

有人知道我应该如何进行吗?


编辑

我尝试按照 mikeck 的建议进行修改:

ODE1 <- function(time, x, parms) {
  import <- parms$excfunc(time)
  dS <- import*parms$k/parms$tau - (x["S"]-parms$yo)/parms$tau 
  res <- c(dS)
  list(res)}

生成数据没有问题。但是现在使用nlme 给出:

checkFunc(Func2, times, y, rho) 中的错误: func()返回的导数个数(0)必须等于初始条件向量的长度(100)

具有以下回溯:

> traceback()
10: stop(paste("The number of derivatives returned by func() (", 
        length(tmp[[1]]), ") must equal the length of the initial conditions vector (", 
        length(y), ")", sep = ""))
9: checkFunc(Func2, times, y, rho)
8: lsoda(xstart, time, ODE1, parms) at #5
7: solution_ODE1(damping, gain, eq, excitation, time)
6: eval(model, data.frame(data, pars))
5: eval(model, data.frame(data, pars))
4: eval(modelExpression[[2]], envir = nlEnv)
3: eval(modelExpression[[2]], envir = nlEnv)
2: nlme.formula(signal ~ solution_ODE1(damping, gain, eq, excitation, 
       time), data = simu_data, fixed = damping + gain + eq ~ 1, 
       random = damping ~ 1, groups = ~ID, start = c(damping = 5, 
           gain = 1, eq = 0))
1: nlme(signal ~ solution_ODE1(damping, gain, eq, excitation, time), 
       data = simu_data, fixed = damping + gain + eq ~ 1, random = damping ~ 
           1, groups = ~ID, start = c(damping = 5, gain = 1, eq = 0))

【问题讨论】:

  • 你试过nlmeODE包吗?
  • 我实际上正在尝试。我对它有点困难,但也许它会奏效。我仍然很高兴为这种奇怪的行为找到解决方案/解释
  • 我做了一些调整 - 参数应该使用 list(),而不是 c(),并且我已经制作了 xstart &lt;- yo1(然后在 ODE1 中直接引用 x,但我仍然收到“非法输入”消息...
  • 您是否尝试过将ODE1() 重新定义为不使用with(),即使用parms$k 等?错误消息看起来可能是一个以某种方式出现的范围界定问题。
  • @mikeck 我试过了,它改变了错误信息。我编辑了我的问题。我不明白nlme 在内部做什么,但它看起来为函数提供了初始条件向量,从而产生错误

标签: r ode nls nlme


【解决方案1】:

在您的示例中,您的 times 向量不会单调运行。我认为这与lsoda 混淆了。时间在这里运作的方式的背景/意义是什么?用两组拟合随机效应模型并没有什么意义。您是否尝试将同一曲线拟合到两个独立的时间序列?

这是一个经过一些调整的精简示例(并非所有内容都可以折叠为数字向量而不会丢失必要的结构):

library(deSolve)
ODE1 <- function(time, x, parms) {
    with(as.list(parms), {
        import <- excfunc(time)
        dS <- import*k/tau - (x-yo)/tau 
        res <- c(dS)
        list(res)
    })
}
solution_ODE1 = function(tau1,k1,yo1,excitation,time){
    excfunc <- approxfun(time, excitation, rule = 2)
    parms  <- list(tau = tau1, k = k1, yo = yo1, excfunc = excfunc)
    xstart = yo1
    out <-  lsoda(xstart, time, ODE1, parms)
    return(out[,2])
}
time <- 0:49
excitation <- c(rep(0,10),rep(1,10),rep(0,10),rep(1,10),rep(0,10))
simu_data <- data.frame(time = rep(time,2),
                        excitation = rep(excitation,2))
svec <- c(damping = 3, gain = 1.75, eq = 0.2)

这行得通:

with(c(simu_data, as.list(svec)),
     solution_ODE1(damping,gain,eq,excitation[1:50],time[1:50]))

但如果我们再包含一个步骤(以便时间重置为 0),它就会失败:

with(c(simu_data, as.list(svec)),
     solution_ODE1(damping,gain,eq,excitation[1:51],time[1:51]))

lsoda(xstart, time, ODE1, parms) 中的错误: 在采取任何集成步骤之前检测到非法输入 - 请参阅书面消息

【讨论】:

  • 我以两个主题为例。目标是适合(模拟)数据的真实面板,例如100 个人。
  • 时间向量为每个主题单调运行(ID 变量在simu_data)。当然,如果您将第二个主题的时间值包含到 lsoda 中,则会导致错误,因为您将有两倍的时间值。这里 lsoda 可以完美地生成数据,因为我使用它来生成 simu_data 数据集。但似乎nlme 在改变导致错误的参数时做了一些事情,我不知道是什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-28
  • 1970-01-01
  • 2015-09-09
  • 1970-01-01
  • 2013-09-02
  • 2021-01-29
  • 1970-01-01
相关资源
最近更新 更多