Skip to content
Prev 378439 / 398502 Next

[FORGED] Newbie Question on R versus Matlab/Octave versus C

Your code seems to be attempting to modify global variables from within 
functions... R purposely makes this hard to do. Don't fight it. Instead, 
use function arguments and produce function outputs with your functions.

Also, the ifelse function does not control flow of execution of code... it 
selects values between two vectors according to the state of the logical 
input vector. Note that all values in both possible input values must be 
computed when using ifelse before it can do its magic, so ifelse can be 
significantly slower than assigning into an indexed vector if a small 
fraction of the vector will be changing.

Below is some proof-of-concept code. It mostly modifies values in-place 
within the data frame rather than using ifelse.

You might want to read the Intro to R document available through the R 
console via:

RShowDoc("R-intro")

to look up numeric indexing and logical indexing syntax while reading 
through this.

#####################
makeNewWomen <- function( nWomen ) {
   data.frame( isAlive = rep_len( TRUE, nWomen )
             , isPregnant = rep_len( FALSE, nWomen )
             , nChildren = rep_len( 0L, nWomen )
             , age = rep_len( 0, nWomen )
             , dateOfPregnancy = rep_len( 0, nWomen )
             , endDateLastPregnancy = rep_len( 0.0, nWomen )
             )
}

updateWomen <- function( DF
                        , jd
                        , maxAge
                        , timeStep
                        , pregProb
                        , gestation
                        , minBirthAge
                        , maxBirthAge
                        ) {
   DF$isAlive[ maxAge <= DF$age ] <- FALSE
   fertileIdx <- with( DF, isAlive & !isPregnant & minBirthAge <= age & age <= maxBirthAge )
   conceiveIdx <- fertileIdx
   conceiveIdx[ conceiveIdx ] <- sample( c( FALSE, TRUE )
                                       , size = sum( fertileIdx )
                                       , replace = TRUE
                                       , prob = c( 1-pregProb, pregProb )
                                       )
   DF$isPregnant[ conceiveIdx ] <- TRUE
   DF$dateOfPregnancy[ conceiveIdx ] <- jd
   birthIdx <- with( DF, isAlive & isPregnant & ( dateOfPregnancy + gestation ) <= jd )
   femalechild <- sample( c( FALSE, TRUE )
                        , size = sum( birthIdx )  # random within birthing group
                        , replace = TRUE
                        , prob = c( 0.5, 0.5 )
                        )
   DF$isPregnant[ birthIdx ] <- FALSE # pregnancy over
   birthIdx[ birthIdx ] <- femalechild # track births further only where female
   # DF$age <- ifelse( DF$isAlive
   #                 , DF$age + timeStep
   #                 , DF$age
   #                 )
   DF$age[ DF$isAlive ] <- DF$age[ DF$isAlive ] + timeStep
   numNotAlive <- sum( !DF$isAlive )
   numBirths <- sum( birthIdx )
   if ( 0 < numBirths ) { # if needed, start female babies in existing or new rows
     if ( 0 < numNotAlive ) {
       reuseidx <- which( !DF$isAlive )
       if ( numBirths <= numNotAlive ) {
         # can fit all new births into existing DF
         reuseidx <- reuseidx[ seq.int( numBirths ) ]
         DF[ reuseidx, ] <- makeNewWomen( numBirths )
       } else {
         DF[ reuseidx, ] <- makeNewWomen( length( reuseidx ) )
         DF <- rbind( DF
                    , makeNewWomen( numBirths - length( reuseidx ) )
                    )
       }
     } else { # no empty rows in DF
       DF <- rbind( DF
                  , makeNewWomen( numBirths )
                  )
     }
   }
   DF  # return the updated data frame to the caller
}

calculatePopulation <- function( nWomen
                                , maxDate
                                , dpy
                                , pregProb
                                , maxAge
                                , timeStep
                                , gestation
                                , minBirthAge
                                , maxBirthAge
                                , prealloc
                                ) {
   jd <- 0
   nextSampleJd <- jd + dpy
   numSamples <- maxDate %/% dpy
   result <- data.frame( jd = rep( NA, numSamples )
                       , NAlive = rep( NA, numSamples )
                       , NPreg = rep( NA, numSamples )
                       , NNotAlive = rep( NA, numSamples )
                       )
   i <- 1L
   DF <- makeNewWomen( prealloc )
   DF$isAlive <- seq.int( prealloc ) <= nWomen # leave most entries "dead"
   while( jd < maxDate ) {
     DF <- updateWomen( DF
                      , jd
                      , maxAge
                      , timeStep
                      , pregProb
                      , gestation
                      , minBirthAge
                      , maxBirthAge
                      )
     if ( nextSampleJd <= jd ) {
       result$jd[ i ] <- jd
       result$NAlive[ i ] <- sum( DF$isAlive )
       result$NPreg[ i ] <- sum( DF$isPregnant )
       result$NNotAlive <- sum( !DF$isAlive )
       nextSampleJd <- nextSampleJd + dpy
       i <- i + 1L
     }
     # Do various other things
     jd <- jd + timeStep
   }
   result
}

nWomen <- 5
numberOfYears <- 30
maxDate <- 300 * 365
dpy <- 365
pregProb <- 0.01
maxAge <- 50 * 365
minBirthAge <- 18 * 365
maxBirthAge <- 45 * 365
timeStep <- 30
gestation <- 30 * 9
prealloc <- 10000
set.seed(42)
simresult <- calculatePopulation( nWomen
                                 , maxDate
                                 , dpy
                                 , pregProb
                                 , maxAge
                                 , timeStep
                                 , gestation
                                 , minBirthAge
                                 , maxBirthAge
                                 , prealloc
                                 )

plot( simresult$jd/365, simresult$NAlive )
plot( simresult$jd/365, simresult$NNotAlive )
plot( simresult$jd/365, simresult$NPreg )
#####################
On Sat, 2 Feb 2019, Alan Feuerbacher wrote:

            
---------------------------------------------------------------------------
Jeff Newmiller                        The     .....       .....  Go Live...
DCN:<jdnewmil at dcn.davis.ca.us>        Basics: ##.#.       ##.#.  Live Go...
                                       Live:   OO#.. Dead: OO#..  Playing
Research Engineer (Solar/Batteries            O.O#.       #.O#.  with
/Software/Embedded Controllers)               .OO#.       .OO#.  rocks...1k
---------------------------------------------------------------------------