简体   繁体   English

R- ode function(deSolve包):改变一个参数的值作为时间的function

[英]R- ode function (deSolve package): change the value of a parameter as a function of time

I am trying to solve a first-order differential equation using the function ode from the deSolve package.我正在尝试使用 deSolve package 中的deSolve ode求解一阶微分方程。 The problem is as follows: a drug is administered by a constant infusion rate at some times (infusion times) and eliminated in a first-order rate.问题如下:药物在某些时候(输注时间)以恒定的输注速率给药,并以一级速率消除。 Thus, the process can be described by:因此,该过程可以描述为:

if(t %in% Infusion_times){Infusion <- Infusion_rate} else{Infusion <- 0}
    dC <- -Ke*C + Infusion

where t is the time, Infusion_times is a vector containing at what times the drug is administered, C is the quantity of the drug, Ke is its elimination constant and Infusion is a variable taking the value of the infusion rate when there is infusion, and the value 0 otherwise.其中t是时间, Infusion_times是包含给药时间的向量, C是药物的量, Ke是其消除常数, Infusion是一个变量,取输液时的输液速度值,以及否则值为 0。 So, let's say we want to administer 3 doses, starting at times 0, 24 and 40, with each infusion lasting for two hours, and let's say we want deSolve to compute the answer every 0.02 units of time.因此,假设我们要施用 3 剂,从时间 0、24 和 40 开始,每次输注持续两个小时,假设我们希望deSolve每 0.02 个单位时间计算一次答案。 We want deSolve to solve the differential equation for the times between 0 and 48 with steps of 0.02 times unit, for instance.例如,我们希望deSolve求解 0 到 48 之间时间的微分方程,步长为 0.02 倍单位。 So our vector specifying the times to the ode function would be:因此,我们为ode function 指定时间的向量将是:

times <- seq(from = 0, to = 48, by = 0.02)

The infusion times are given by:输注时间由下式给出:

Infusion_times <- c(seq(from = 0, to = 2, by = 0.02), seq(from = 24, to = 26, by = 0.02), 
                    seq(from = 40, to = 42, by = 0.02))

At first, I was concerned that the problem could be in the comparison of floating-points.起初,我担心问题可能出在浮点的比较中。 To prevent it, I rounded both vectors to two decimal places:为了防止这种情况,我将两个向量四舍五入到小数点后两位:

times <- round(times, 2)
Infusion_times <- round(times, 2)

So now, hopefully, all Infusion_times are included in the times vector:所以现在,希望所有的Infusion_times都包含在times向量中:

(sum(Infusion_times %in% times)/length(Infusion_times))*100
[1] 100

As you can see, all the values in Infusion_times (100%) are contained in the vector times , and thus the variable Infusion should take the value Infusion_rate at the specified times.如您所见, Infusion_times (100%) 中的所有值都包含在向量times中,因此变量Infusion应该在指定时间取值Infusion_rate However, when we solve the equation, it doesn't work.但是,当我们求解方程时,它不起作用。 Let's prove it, but first, let's specify the other values needed by the ode function:让我们证明它,但首先,让我们指定ode function 所需的其他值:

parameters <- c(Ke = 0.5)
amounts <- c(C = 0) #Initial value for drug is 0
Inf_rate <- 5

And now, let's write a function stating the rate of changes, just as required:现在,让我们根据需要编写一个 function 说明变化率:

OneComp <- function(t, amounts, parameters){
  with(as.list(c(amounts, parameters)),{
      if(t %in% Infusion_times){Infuse =Inf_rate} else{Infuse = 0}
      dC <- -Ke*C + Infuse
  list(c(dC))})
}

For those who are not familiar with the deSolve package, the argument t of the function will state the times for which the equation should be integrated, amounts will specify the initial value of C and parameters will give the value of the parameters (in this case, just Ke ). For those who are not familiar with the deSolve package, the argument t of the function will state the times for which the equation should be integrated, amounts will specify the initial value of C and parameters will give the value of the parameters (in this case , 只是Ke )。 And now, let's solve the equation:现在,让我们解方程:

out <- ode(func = OneComp, y = amounts, parms = parameters, times = times)

Let's plot the results:让我们 plot 结果:

library(ggplot2)

ggplot(as.data.frame(out)) + geom_line(aes(x = times, y = C))

This is exactly the same result we would get if Infusion was always equal to 0. However, note that if we were to mimic only a single dose, and we were to try a similar approach, it would work:如果Infusion总是等于 0,这与我们得到的结果完全相同。但是,请注意,如果我们只模拟单次剂量,并且我们要尝试类似的方法,它会起作用:

OneComp <- function(t, amounts, parameters){
      with(as.list(c(amounts, parameters)),{
          if(t < 2){Infuse =Inf_rate} else{Infuse = 0}
          dC <- -Ke*C + Infuse
      list(c(dC))})
    }
out <- ode(func = OneComp, y = amounts, parms = parameters, times = times)
ggplot(as.data.frame(out)) + geom_line(aes(x = times, y = C))

Here, we have made the variable Infuse take the value of the Inf_rate only when the time is less than 2 hours, and it works!在这里,我们使变量Infuse仅在时间小于 2 小时时才取Inf_rate的值,并且有效! Therefore, I am totally puzzled with these behaviour.因此,我对这些行为完全感到困惑。 It is not a problem of changing the value of a variable, it is not a problem of a comparison between floating-point numbers... Any idea of what could these be?这不是改变变量值的问题,也不是浮点数之间的比较问题......知道这些可能是什么吗? Thanks谢谢

Most solvers of deSolve use an automatic internal time step, that adjusts itself, depending on the roughness or smoothness of the system.大多数deSolve求解器使用自动内部时间步长,该时间步长会根据系统的粗糙度或平滑度自行调整。 The use of if statements or if() functions in the model function is not a good idea for two reasons: (i) the time steps may not be hit exactly and (2) the model function (ie the derivative) should ideally be continuous and differentiable and avoid step-wise behavior, even if the solvers are quite robust in such cases. The use of if statements or if() functions in the model function is not a good idea for two reasons: (i) the time steps may not be hit exactly and (2) the model function (ie the derivative) should ideally be continuous和可微分并避免逐步行为,即使求解器在这种情况下非常稳健。

The deSolve package provides are two approaches for your problem: "forcing functions" and "events". deSolve package 为您的问题提供了两种方法:“强制函数”和“事件”。 Both have their pros and cons, but "events" are especially useful if the time for the "event" (eg an injection) is very short, compared to the integration time step.两者都有其优点和缺点,但如果“事件”(例如注入)的时间与积分时间步相比非常短,则“事件”特别有用。

More about this can be found in the deSolve help pages ?forcings and ?events , in deSolve: Forcing functions and Events from the useR,2017 conference, and in the slides from userR.2014.有关这方面的更多信息,请参见 deSolve帮助页面?forcings forcings 和?eventsdeSolve: Forcing functions and Events from the user,2017 Conference 以及 userR.2014 的幻灯片

Please check if the following works for you:请检查以下是否适合您:

library("deSolve")

OneComp <- function(t, y, parms){
  with(as.list(c(y, parms)),{
    dC <- -Ke * C
    list(c(dC))
  })
}

eventfunc <- function(t, y, parms) {
  with(as.list(c(y, parms)),{
    C + Inf_rate
  })
}

parms <- c(Ke = 0.5, Inf_rate = 5)

y0 <- c(C = 0)            # Initial value for drug is 0

Infusion_times <- c(seq(from =  0, to =  2, by = 0.02), 
                    seq(from = 24, to = 26, by = 0.02), 
                    seq(from = 40, to = 42, by = 0.02))

# time step can be made bigger to speedup simulation
times <- round(seq(from = 0, to = 48, by = .1) , 2)

# check that all events are in 'times', but no duplicates
# this check is also done by the solver and may print a warning
# to ensure that the user is very careful with this
unique_times <- cleanEventTimes(times, Infusion_times)
times        <- sort(c(unique_times, Infusion_times))

out <- ode(func = OneComp, y = y0, parms = parms, times = times, 
           events = list(func = eventfunc, time = Infusion_times))

plot(out)
rug(Infusion_times)

The two cleanEventTimes lines are one possible approach for ensuring that all event times are hit by the simulation.两条cleanEventTimes行是确保模拟命中所有事件时间的一种可能方法。 It is normally done automatically by the solver and may issue a warning to remind the user to be very careful with this.它通常由求解器自动完成,并且可能会发出警告以提醒用户对此要非常小心。

I used "base" plot and rug to indicate injection times.我使用“基础”plot 和rug来指示注射时间。

I wonder somewhat about the terms Infusion_times and Inf_rate .我有点想知道Infusion_timesInf_rate这两个术语。 In an event-based approach, an "amount" is added to the state variable C at discrete time points, while a "rate" would indicate continuous addition within a time interval .在基于事件的方法中,在离散时间点将“数量”添加到 state 变量 C 中,而“速率”表示在时间间隔内连续添加。 This is often called a forcing function.这通常称为强制 function。

A forcing function would be even simpler and is numerically better.强制 function 会更简单并且数值更好。

I have been struggling with the same problem for some time.我一直在为同样的问题苦苦挣扎一段时间。 I was trying to replicate IV infusion followed by PO dosing using the deSolve package rather than the RxODE package used in the original model.我试图复制 IV 输液,然后使用 deSolve package 而不是原始 Z20F35E630DAF44DBFA4C3F68F5399D8 中使用的 RxODE package 进行 PO 给药。 My solution was to make a list of infusion times and later extract a maximal value:我的解决方案是列出输液时间,然后提取最大值:

tmp <- c()
for (i in seq(from = 1, to = Num.Doses, by = 1)) {
tmp[i] <- (i * Tau) - Tau
tmp2 <- seq(from = 0, to = 2, by = 0.1)
Inf.Times <- round(unlist(lapply(tmp, function(x, Add) x + tmp2)), 3)}

Here, Num.Doses is set for 5 IV infusions.在这里, Num.Doses设置为 5 次 IV 输注。 The Tau (dosing interval) is 24 hours, 0 is infusion start time, and 2 is infusion end time in hours. Tau (给药间隔)是 24 小时, 0是输注开始时间, 2是输注结束时间(以小时为单位)。

Next I constructed a model, the full version of which is a multicompartment PKPD model from RxODE ( https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4728294/ ): Next I constructed a model, the full version of which is a multicompartment PKPD model from RxODE ( https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4728294/ ):

Model <- function(time, yini, parameters) {
  with(as.list(c(yini, parameters)), {
    Infusion <- ifelse(time %% Tau < Inf.Time & time <= max(Inf.Times), Dose / Inf.Time, 0)
    C1 <- Central / V1
    dDepot <- - (Ka * Depot)
    dCentral <- (Ka * Depot) - (CL * C1) + Infusion
    list(c(dDepot, dCentral))})}

I got the idea for the Infusion <- line from Andrew Booker as shown at https://github.com/andrewhooker/PopED/issues/11 .我从 Andrew Booker 那里得到了Infusion <-行的想法,如https://github.com/andrewooker/PopED/issues/11所示。 I modified his suggestion from if else to ifelse and incorporated a hard stop after the end of the 5th infusion with the time <= max(Inf.Times) otherwise the model would continually re-infuse.我将他的建议从if else修改为ifelse并在第 5 次输液结束后合并一个硬停止, time <= max(Inf.Times)否则 model 将不断重新输液。 That also allowed me to implement additional non-IV dosing using an events table when running the model using deSolve:这也允许我在使用 deSolve 运行 model 时使用事件表实现额外的非 IV 给药:

Dose.Events <- data.frame(var = "Depot", time = c(120, 144, 168, 192, 216), value = Dose2, method = "add")
Times <- seq(from = 0, to = Tot.Hours, by = 1)
out <- ode(y = Ini.Con, times = Times, func = Model2, parms = Parms, events = list(data = Dose.Events))

When run using the full model, the output is nearly the same as using the original code in RxODE, which is more straightforward and "clean".当使用完整的 model 运行时,output 与在 RxODE 中使用原始代码几乎相同,更直接和“干净”。 The differences, as judged by AUC, were minimal with sig figs the same out to 6 digits.根据 AUC 判断,差异很小,无花果相同到 6 位数。 I am able to replicate the IV infusions (first set of 5 peaks) and also recapitulate the PO dosing (second set of 5 peaks).我能够复制 IV 输液(第一组 5 个峰),还可以概括 PO 给药(第二组 5 个峰)。 在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM