Функция R-ode (пакет deSolve): изменение значения параметра как функции времени - PullRequest
0 голосов
/ 28 апреля 2020

Я пытаюсь решить дифференциальное уравнение первого порядка, используя функцию ode из пакета deSolve. Проблема заключается в следующем: лекарство вводят с постоянной скоростью инфузии в несколько раз (время инфузии) и устраняют в скорости первого порядка. Таким образом, процесс может быть описан следующим образом:

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

, где t - время, Infusion_times - вектор, содержащий время, в которое препарат вводится, C - количество препарата. , Ke - это ее постоянная элиминации, а Infusion - это переменная, принимающая значение скорости инфузии, когда есть инфузия, и значение 0 в противном случае. Итак, допустим, мы хотим назначить 3 дозы, начиная с 0, 24 и 40, при этом каждая инфузия длится два часа, и, скажем, мы хотим, чтобы deSolve вычислял ответ каждые 0,02 единицы времени. Мы хотим, чтобы deSolve решил дифференциальное уравнение для времен от 0 до 48, например, с шагом 0,02. Таким образом, наш вектор, определяющий время для функции ode, будет иметь вид:

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

Время инфузии определяется следующим образом:

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))

Сначала я был обеспокоен тем, что проблема может быть в сравнении с плавающей точкой. Чтобы предотвратить это, я округлил оба вектора до двух десятичных разрядов:

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

Так что теперь, надеюсь, все Infusion_times включены в вектор times:

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

As Вы можете видеть, что все значения в Infusion_times (100%) содержатся в векторе times, и поэтому переменная Infusion должна принимать значение Infusion_rate в указанное время. Однако, когда мы решаем уравнение, оно не работает. Давайте докажем это, но сначала давайте определим другие значения, необходимые для функции ode:

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

А теперь давайте напишем функцию, определяющую скорость изменений, так же, как требуется:

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))})
}

Для тех, кто не знаком с пакетом deSolve, аргумент t функции будет указывать время, за которое должно быть интегрировано уравнение, amounts будет указывать начальное значение C и parameters даст значение параметров (в данном случае просто Ke). А теперь давайте решим уравнение:

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

Давайте построим результаты:

library(ggplot2)

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

Это точно такой же результат, который мы получили бы, если бы Infusion всегда было равно 0. Тем не менее, обратите внимание, что если бы мы имитировали c только одну дозу, и мы должны были попробовать подобный подход, он бы работал:

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))

Здесь мы сделали переменную Infuse take значение Inf_rate только тогда, когда время меньше 2 часов, и оно работает! Поэтому я совершенно озадачен этим поведением. Это не проблема изменения значения переменной, это не проблема сравнения чисел с плавающей запятой ... Есть идеи, что это может быть? Спасибо

1 Ответ

0 голосов
/ 28 апреля 2020

Большинство решателей deSolve используют автоматический внутренний временной шаг c, который настраивается в зависимости от шероховатости или гладкости системы. Использование if операторов или if() функций в модельной функции не является хорошей идеей по двум причинам: (i) временные шаги не могут быть точно достигнуты и (2) модельная функция (т.е. производная) должна избегать пошаговое поведение, даже если решатели достаточно устойчивы в таких случаях.

Пакет deSolve предоставляет два подхода к вашей проблеме: «форсирование функций» и «события». Оба имеют свои плюсы и минусы, но «события» особенно полезны, если время «события» (например, инъекции) очень мало по сравнению с шагом времени интеграции.

Подробнее об этом можно узнать в справочных страницах deSolve ?forcings и ?events, в deSolve: принудительное использование функций и событий из конференции useR! 2017 и в слайдах из userR! 2014.

Пожалуйста, проверьте, работает ли для вас следующее:

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)

Две строки cleanEventTimes являются одним из возможных подходов, гарантирующих, что симуляция затронет все времена события. Обычно он выполняется автоматически решающим устройством и может выдавать предупреждение, напоминающее пользователю, что нужно быть очень осторожным с этим.

Я использовал «базовый» график и rug, чтобы указать время впрыска.

Интересно несколько о терминах Infusion_times и Inf_rate. В подходе, основанном на событиях, «сумма» добавляется к переменной состояния C в отдельные моменты времени, в то время как «скорость» будет указывать на непрерывное добавление в течение интервала времени . Это часто называют функцией принуждения.

Функция принуждения была бы еще проще и численно лучше.

...