参数值作为另一个向量的函数。解解

Posted

技术标签:

【中文标题】参数值作为另一个向量的函数。解解【英文标题】:Parameter values as a function of another vector. deSolve 【发布时间】:2022-01-22 05:14:05 【问题描述】:

我希望建立一个人口动态模型,其中每个参数值对应于当天的温度。例如

简单模型

 library(deSolve)
set.seed(1)

pars <- c(alpha = 1, beta = 0.2, delta = 0.5, gamma = 0.2)



lv_model <- function(pars, times = seq(0, 50, by = 1)) 
  # initial state 
  state <- c(x = 1, y = 2)
  # derivative
  deriv <- function(t, state, pars) 
    with(as.list(c(state, pars)), 
      d_x <- alpha * x - beta * x * y
      d_y <- delta * beta * x * y - gamma * y
      return(list(c(x = d_x, y = d_y)))
    )
  
  # solve
  ode(y = state, times = times, func = deriv, parms = pars)

lv_results <- lv_model(pars = pars, times = seq(0, 50, by = 1))

我现在想使用一系列每日温度 DailyTemperature&lt;-floor(runif(50,0,40))

并使参数值成为温度的函数

TraitTemperature<-seq(1,40,1)

#trait responses to temperature
alpha<- abs(rnorm(40,mean = 0.5,sd=1))
beta<- abs(rnorm(40,mean = 0.2,sd=0.5))
delta<-abs(rnorm(40,mean=1,sd=2))
gamma<- seq(0.025,1,0.025)
parameters<-as.data.frame(cbind(TraitTemperature,alpha,beta,delta,gamma))

这样对于迭代的每个时间步,它都会查看每日温度,然后在参数数据框中找到相应的温度值。

回顾档案,我看到 if/else 语句用于在特定时间步更改单个参数和使用强制函数,但我认为它们不适用于这里。

我希望这是有道理的,我对如何使它起作用的想法很感兴趣。到目前为止,我还尝试使用 for loop 遍历每日温度列表,然后使用 match 函数来识别值,但这并没有利用每日时间步长。

【问题讨论】:

deSolve 没有太多经验,但我确实使用迭代方法进行了很多此类动态建模。因此,解决此问题的另一种方法可能是将您的 DE 转换为一种格式,其中 y 在时间 t 的值是时间 t-1 的函数。然后在循环中迭代函数。如果速度是个问题,最好在 Rcpp 中进行此迭代,因为 R 在这种情况下会变得有点慢。 如果我理解正确的话,这就是我们所说的强制。您可以在 deSolve 帮助页面 ?forcings 或例如以下页面中找到更多相关信息:tpetzoldt.github.io/deSolve-forcing/deSolve-forcing.html 有几种方法可以做到这一点。一种想法是根据温度为参数创建 4 个信号,但如果信号的索引(例如温度)与时间向量完全对应,也可以通过索引访问来实现(见下文)。另一种方法是使用 simecol 包中的approxTime1,它能够一次返回整个参数值向量。最后,它也可以通过回调来完成,其中parms 是一个进行任意插值的函数。 【参考方案1】:

这是一种可能的方法,使用DailyTemperature 作为强制,然后使用parameters 数据框作为查找表。这里不需要match,只要温度是整数,数据框中的温度对应数据框的行索引即可。

我想补充一点,原则上,不连续的强迫会使模型变慢,因为 ODE 根据定义是连续的。幸运的是,由于求解器非常强大,它应该适用于实际应用:

library(deSolve)
set.seed(1)

deriv <- function(t, state, pars) 

  pars <- parameters[DailyTemperature[floor(t + 1)], 2:5]
  #print(pars)
  
  with(as.list(c(state, pars)), 
    d_x <- alpha * x - beta * x * y
    d_y <- delta * beta * x * y - gamma * y
    list(c(x = d_x, y = d_y), alpha=alpha, beta=beta, gamma=gamma, delta=delta)
  )



state <- c(x = 1, y = 2)
times = seq(0, 50, by = 1)

# pars <- c(alpha = 1, beta = 0.2, delta = 0.5, gamma = 0.2)
parameters <- data.frame(
  TraitTemperature = seq(1,40,1),
  alpha = abs(rnorm(40,mean = 0.5,sd=1)),
  beta = abs(rnorm(40,mean = 0.2,sd=0.5)),
  delta = abs(rnorm(40,mean=1,sd=2)),
  gamma = seq(0.025,1,0.025)
)


DailyTemperature <- floor(runif(51, 0, 40)) # one more because start zero

out <- ode(y = state, times = times, func = deriv, parms = pars)
plot(out)

参数列表

在上面的示例中,从ode 传递的pars 变量只是被从全局变量parametersDailyTemperature 派生的pars 覆盖。这可行,但也可以考虑将两者都作为列表传递给deriv 函数。

deriv <- function(t, state, p) 
  
  parameters <- p[[1]]
  DailyTemperature <- p[[2]]

  parms <- parameters[DailyTemperature[floor(t + 1)], 2:5]
  # ...

然后:

out <- ode(y = state, times = times, func = deriv,
  parms = list(parameters, DailyTemperature))

【讨论】:

非常感谢!我将以 0.1 的间隔使用温度值,所以我猜我会在代码的第 4 行使用match 函数? ``` pars 您还可以使用 approxfun 的插值来获取给定温度的参数行,或者更快地获取直接计算指数的公式。 所以我已经了解了 approxfun 我遇到的问题是使其成为时间和 DailyTemperature 的函数。例如alphainput&lt;- approxfun(alpha,rule = 2) 将遍历 alpha 向量,但不一定针对 DailyTemperature。我对索引也不是很好。有什么想法吗?非常感谢您的帮助【参考方案2】:

这似乎适用于使用当前可重现代码进行索引:

set.seed(1)
deriv <- function(t, state, pars) 
  pars<- parameters[match(parameters$TraitTemperature[parameters[2:5]],DailyTemperature),]
  #print(pars)
  
  with(as.list(c(state, pars)), 
    d_x <- alpha * x - beta * x * y
    d_y <- delta * beta * x * y - gamma * y
    list(c(x = d_x, y = d_y), alpha=alpha, beta=beta, gamma=gamma, delta=delta)
  )


state <- c(x = 1, y = 2)
times = seq(0, 50, by = 1)

# pars <- c(alpha = 1, beta = 0.2, delta = 0.5, gamma = 0.2)
parameters <- data.frame(
  TraitTemperature = seq(1,40,1),
  alpha = abs(rnorm(40,mean = 0.5,sd=1)),
  beta = abs(rnorm(40,mean = 0.2,sd=0.5)),
  delta = abs(rnorm(40,mean=1,sd=2)),
  gamma = seq(0.025,1,0.025)
)

DailyTemperature <- floor(runif(51, 0, 40)) # one more because start zero

out <- ode(y = state, times = times, func = deriv, parms = pars)
plot(out)

但是如果我增加值的分辨率,例如


# Parameter datasets 
parameters <- data.frame(
  TraitTemperature = seq(0.1,40,0.1),
  alpha = abs(rnorm(400,mean = 0.5,sd=1)),
  beta = abs(rnorm(400,mean = 0.2,sd=0.5)),
  delta = abs(rnorm(400,mean=1,sd=2)),
  gamma = seq(0.0025,1,0.0025)
)

# random daily temperature dataset 
DailyTemperature <- round(runif(51, 0, 40),1) # one more because start zero

然后我在某些时间步得到 NA

【讨论】:

这里最好再问一个问题。尽管如此,我仍然可以为这个问题写第二个答案,但为了避免让稍后可能阅读此内容的 SO 用户感到困惑,我建议您提取具体问题,然后再问一次。然后,我们可以将 Q+A 移至新线程,从此处删除过时的代码。【参考方案3】:

OP 扩展了她/他的问题,因此它可能是启动新线程的首选方式,但为了提供快速反馈,我尝试给出另一个答案。

在二维(时间和温度)中存在多种插值或索引方法。我的首选方法是创建 2D 模型,然后使用 2D 插值方法。如果参数表面是平滑的,而不是像给定示例中那样随机,则此方法效果最佳。但是,为了简单起见,也可以使用舍入和表格查找。如果值不是整数,由于舍入效应和有限的精度(IEEE 数字格式),精确比较通常不起作用,因此可以使用which.min 代替匹配:

DailyTemperature <- round(runif(51, 0, 40), 1)

TraitTemperature <- seq(0, 40, by=0.1)
N <- length(TraitTemperature)

parameters <- data.frame(
  TraitTemperature = TraitTemperature,
  alpha = abs(rnorm(N, mean = 0.5, sd=1)),
  beta = abs(rnorm(N, mean = 0.2, sd=0.5)),
  delta = abs(rnorm(N, mean=1,sd=2)),
  gamma = seq(0.025, 1, length.out=N)
)


t <- 17
actualTemp <- DailyTemperature[floor(t+1)]
actualTemp
pars <- parameters[which.min(abs(actualTemp - parameters$TraitTemperature)), 1:5]

head(pars)

【讨论】:

以上是关于参数值作为另一个向量的函数。解解的主要内容,如果未能解决你的问题,请参考以下文章

使用函数的返回值作为另一个函数调用的参数[重复]

将一个函数的多个返回值作为参数传递给另一个函数

将函数的值作为输入参数返回给另一个

对象作为参数传递给另一个类,通过值还是引用?

vector<wstring> 作为返回值和参数

函数的默认值作为函数参数