Home > front end >  Parameter values as a function of another vector. deSolve
Parameter values as a function of another vector. deSolve

Time:12-22

I'm looking to set up a population dynamics model where each parameter value corresponds to the temperature of that day. e.g.

Simple model

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

I now want to use a sequence of daily temperatures DailyTemperature<-floor(runif(50,0,40))

and make the parameter values functions of temperatures

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

So that for each time step iterated over, it looks at the daily temperature and then finds the corresponding temperature values in the parameter data frame.

Looking back through the archives i've seen if/else statements used when wanting to alter single parameters at particular time steps and the use of forcing functions but I don't think they apply here.

I hope this makes sense, I'm interesting in ideas on how to make it work. So far i've also attempted using a for loop to iterate through the daily temperature list and then the match function to identify values but this didn't tap into the daily time steps.

CodePudding user response:

Here a possible approach using DailyTemperature as forcing and then the parameters data frame as lookup table. It does not need match here as long as the temperatures are integers and the temperatures in the data frame correspond to the row index of the data frame.

I want to add that, in principle, discontinuous forcings make the model slow, because an ODE is a continuous by definition. Fortunately, as the solvers are quite robust, it should for practical applications:

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)

Temperature dependend parameters

Parameters as a list

In the example above, the pars variable passed from ode is just overwritten with pars derived from the global variables parameters and DailyTemperature. This works, but one may also consider to pass both as a list to the deriv function.

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

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

and then:

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

CodePudding user response:

This seems to work for indexing using the current reproducible code:

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)

but if I increase the resolution of the values e.g.


# 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

Then I get NA's at certain time steps

CodePudding user response:

The OP extended her/his question, so it may be the preferred way to start a new thread, but in order to give quick feedback, I try give another answer.

Several methods exist for interpolation or indexing in 2D (time and temperature). My preferred way would be to create a 2D model and then tu use a 2D interpolation method. This works best if the parameters surface would be smooth and not just random as in the given example. However, to make it simple, one can also use rounding and table lookup. If values are not integers, exact comparison does often not work due to rounding effects and limited precision (IEEE number formats), so instead of match, one can use 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)
  • Related