bell
bell

Reputation: 311

Adding rows in a list in R

Consider the following code, that solves a system of DE such as Lotka–Volterra. It stores the solution in a list called "out" for 4 different cases. I have 2 questions:

(1) How can I calculate the sum of rows at each time step for 4 cases and store into a vector? I tried to use, apply(out[[i]][,1:2],1,sum) but it produces an error as follows

Error in FUN(newX[, i], ...) : invalid 'type' (list) of

(2) Can you please explain the structure of output? and why it is not working while it works with matrices. I tried to create a similar list using random numbers, but could no reproduce the similar structure.

Thanks.

rm(list=ls())
library("foreach")
library("deSolve")
library(doParallel)
cl<-makeCluster(2, outfile = "debug.txt")
registerDoParallel(cl)

CASES = 4

LVmod <- function(Time, State, Pars) {
   with(as.list(c(State, Pars)), {
         Ingestion    <- rIng  * Prey * Predator
         GrowthPrey   <- rGrow * Prey * (1 - Prey/K)
         MortPredator <- rMort * Predator

        dPrey        <- GrowthPrey - Ingestion
        dPredator    <- Ingestion * assEff - MortPredator

        return(list(c(dPrey, dPredator)))
   })
   }

   solveODE<-function(times, LVmod, pars, yini, CASES){
   output<-foreach(x=1:CASES, .packages=c('deSolve'))  %dopar%
{
      if (x<CASES) ode(yini[[x%%CASES]], times, LVmod, pars)
      else         ode(yini[[CASES]], times, LVmod, pars)

 }
  }
     pars  <- c(rIng   = 0.2,    # /day, rate of ingestion
       rGrow  = 1.0,    # /day, growth rate of prey
       rMort  = 0.2 ,   # /day, mortality rate of predator
       assEff = 0.5,    # -, assimilation efficiency
       K      = 10)     # mmol/m3, carrying capacity

yini<-list()
yini[[1]]<-c(Prey = 1, Predator = 2)
yini[[2]]<-c(Prey = 5, Predator = 5)
yini[[3]]<-c(Prey = 2, Predator = 3)
yini[[4]]<-c(Prey = 4, Predator = 2)


out<-list()
for (i in 1:CASES) out[[i]]<-list()


for (i in 0:20){
   times <- seq(10*i, 10*(i+1), by = 1)

 output<-solveODE(times, LVmod, pars, yini, CASES)

 for (j in 1:CASES){
  out[[j]]<-rbind(out[[j]],output[[j]])
  ic11<-out[[j]][length(out[[j]][,1]),2]
  ic12<-out[[j]][length(out[[j]][,1]),3]
  yini[[j]]  <- c(Prey = as.numeric(ic11), Predator = as.numeric(ic12))
 }

}

  stopCluster(cl)

Upvotes: 0

Views: 106

Answers (1)

Maurits Evers
Maurits Evers

Reputation: 50668

  1. Use lapply instead of apply to loop through list elements; for example, to calculate the sum of values in the second column Prey for every list element you can do:

    lapply(out, function(x) sum(as.numeric(x[, 2])))
    #[[1]]
    #[1] 468.3304
    #
    #[[2]]
    #[1] 461.0533
    #
    #[[3]]
    #[1] 464.6915
    #
    #[[4]]
    #[1] 469.421
    
  2. As for your second question: "Can you please explain the structure of output?" Well, considering that this is your code, that really is something you should know, isn't it. According to your code, you are constructing a list of lists:

    out<-list()
    for (i in 1:CASES) out[[i]]<-list()
    

PS. You really should format your code properly. Use proper indentations, align matching brackets, etc. It will make your code a lot easier to read!


Update

If you want to calculate the sum of Prey and Predator for every time, you can use rowSums inside lapply:

lst <- lapply(out, function(x) rowSums(as.data.frame(matrix(unlist(x), ncol = 3))[, 2:3]))

as.data.frame(matrix(unlist(x), ncol = 3)) converts your list into a data.frame.

You can column-bind the list entries using cbind:

df <- do.call(cbind, lst));
head(df);
#         [,1]      [,2]     [,3]     [,4]
#[1,] 3.000000 10.000000 5.000000 6.000000
#[2,] 3.490136  9.051351 5.445284 7.034600
#[3,] 4.359623  8.130653 5.915932 7.644949
#[4,] 5.467660  7.246250 6.317925 7.842548
#[5,] 6.517990  6.444182 6.589044 7.730863
#[6,] 7.255411  5.759818 6.711389 7.420815

The rows correspond to the different time values, the columns to the different CASES.

Upvotes: 2

Related Questions