0

Very new to coding. I have been attempting to input values from a for loop iteration into a dataframe. I have looked at many asked questions however they did not seem to help my situation. Once my for loop is complete, my cat() outputs all values of the iteration. I have tried to create a dataframe using the names of the variables as you can see in the code however it only outputs one row with the last iteration. I have been told to create a dataframe before my for-loop in order to store the output values, but I cannot seem to figure it out. If anyone can show me the correct code that would be very greatly appreciated! Thank you

Numyears<- 0
NumLynxes<-0
NumHares<-0
flag<- F
NumBabyLynxes<-0
NumBabyHares<-0
NumDeadHares<-0
NumDeadLynxes<-0
NumHaresEaten<-0

while(flag !=T) {

ask <-readline(prompt = "Simulate the population of Lynx and Hares in Canada?
Type 1 for yes, Type anything else to exit:")
response<- as.integer(ask)

 if (response ==1) {
 variable1 <- readline(prompt = "Input the number of years for the simulation sample:")
 Numyears <- as.integer(variable1)
 variable2 <- readline(prompt="Enter the number of Lynxes in this simulation:")
 NumLynxes <- as.integer(variable2)
 variable3 <-readline("Enter the number of Hares in this simulation:")
 NumHares <-as.integer(variable3)

  DF <- data.frame(Numyears,NumHares,
                 NumLynxes,NumBabyHares,
                 NumBabyLynxes,
                 NumDeadHares,
                 NumDeadLynxes,
                 NumHaresEaten,stringsAsFactors = F)

  for (years in 1:Numyears) {

   if ( (NumLynxes/NumHares) < 0.20) {

    NumDeadLynxes<-round(NumLynxes * 0.02)
    NumBabyLynxes <- round(0.15*NumLynxes)
   }

   if ((NumLynxes/NumHares) >= 0.20) {
    NumDeadLynxes <- round(0.50*NumLynxes)
    NumBabyLynxes <- round(0.15*NumLynxes)
   }


   NumBabyHares<- round(0.75*NumHares)
   NumDeadHares <- round(NumDeadHares*0.01)
   NumHaresEaten <- round((NumLynxes*NumHares)*0.025)

   years <- years 
   NumLynxes <- ((NumLynxes + NumBabyLynxes) - NumDeadLynxes)
   NumHares <- ((NumHares + NumBabyHares) - (NumHaresEaten + NumDeadHares))

   if ((NumLynxes/NumHares) < 0){
    break
   }

   cat("Year  #Hares  #Lynxes  babyH  babyL  deadHare  deadLynx","\n",
      years," ","\t",NumHares," ","\t",NumLynxes, " ","\t",NumBabyHares, 
      " ","\t",NumBabyLynxes,"   ","\t",NumDeadHares," ","\t",NumDeadLynxes,
      " ", "\t",NumHaresEaten) 

  } 

 } else{
   flag <- T
 }
}
 DF
  • You assign to `DF` very early *and then never update it*. To R, `DF` is a frame that may have used `NumHares` as part of its definition, but once defined the two variables (`DF` and `NumHares`) have nothing to do with each other: changing one has no effect on the other. – r2evans Apr 08 '20 at 15:39
  • Yes! that's exactly the issue. When I write create the dataframe in the for-loop it outputs only the last row (at 20 years input). Now that I am assigning it before the loop, I am not sure how to write the code to update it and store the values. If you can post the code you would write at the end of the for-loop I would really appreciate that. – TheLearner Apr 08 '20 at 15:44
  • (1) `DF$somevariable – r2evans Apr 08 '20 at 16:03
  • Thank you for the feedback! I'm going to look at these and get back to you. Hopefully, this will help me figure it out. – TheLearner Apr 08 '20 at 16:07

1 Answers1

1

Look at this method instead.

  1. I pre-allocate the entire DF with as many rows as needed. This prevents the "growing objects" problem referenced in chapter 2 of the R Inferno.

    I include the starting conditions in the data.frame. While not completely necessary, this makes calculation later on that much easier. Without it, we would need to calculate the next populations with if (row == 1) { calc_one_way; } else { calc_another_way; }, which seems easy enough but just reads poorly. If you don't like having the starting conditions in the first row, then you can always remove it at the very end with DF[-1,].

  2. While we could technically use attach(DF) to prevent needing to reference DF$ every time, I strongly discourage it: it encourages some sloppy habits, and can and will cause problems at times if you are not very careful.

  3. I'm inferring that we are growing (or eating) the population year-by-year, so we set next year's population based on this year's population and behavior. With that, I reference this year's numbers with [row] and set next year's numbers with [row+1]. Whenever indexing like that, care must be taken to never go too low ([0] is an error in R) or too high. In this case, since we have NumYears + 1 number of rows (I start with year 0 in row 1), and we only iterate from row 1 (year 0) to NumYears (nrow(DF) - 1), we're good with using [row] and [row+1] for row indices.

    Note: we start with row of 1 which reflects year 0 (starting conditions). That was my guess and is completely up to you.

flag <- FALSE

while(flag != TRUE) {

  ask <-readline(prompt = "Simulate the population of Lynx and Hares in Canada?
Type 1 for yes, Type anything else to exit:")
  response <- as.integer(ask)

  if (response == 1) {

    variable1 <- readline(prompt = "Input the number of years for the simulation sample:")
    NumYears <- as.integer(variable1)
    variable2 <- readline(prompt="Enter the number of Lynxes in this simulation:")
    NumLynxes <- as.integer(variable2)
    variable3 <- readline("Enter the number of Hares in this simulation:")
    NumHares <- as.integer(variable3)

    zeroes <- rep(0, NumYears + 1)
    DF <- data.frame(
      Year = c(0, seq_len(NumYears)),
      NumHares = c(NumHares, zeroes[-1]),
      NumLynxes = c(NumLynxes, zeroes[-1]),
      NumBabyHares = zeroes, NumBabyLynxes = zeroes,
      NumDeadHares = zeroes, NumDeadLynxes = zeroes,
      NumHaresEaten = zeroes)

    for (row in seq_len(NumRows)) {

      if ( (DF$NumLynxes[row] / DF$NumHares[row]) < 0.20) {
        DF$NumDeadLynxes[row + 1] <- round(DF$NumLynxes[row] * 0.02)
        DF$NumBabyLynxes[row + 1] <- round(0.15 * DF$NumLynxes[row])
      } else {
        DF$NumDeadLynxes[row + 1] <- round(0.50 * DF$NumLynxes[row])
        DF$NumBabyLynxes[row + 1] <- round(0.15 * DF$NumLynxes[row])
      }


      DF$NumBabyHares[row] <- round(0.75 * DF$NumHares[row])
      DF$NumDeadHares[row] <- round(DF$NumDeadHares[row] * 0.01)
      DF$NumHaresEaten[row] <- round((DF$NumLynxes[row] * DF$NumHares[row]) * 0.025)

      DF$NumLynxes[row + 1] <- ((DF$NumLynxes[row] + DF$NumBabyLynxes[row]) - DF$NumDeadLynxes[row])
      DF$NumHares[row + 1] <- ((DF$NumHares[row] + DF$NumBabyHares[row]) - (DF$NumHaresEaten[row] + DF$NumDeadHares[row]))

      if (DF$NumLynxes[row] <= 0) {
        break
      }

      print(DF[row + 1,])

    }

  } else{
    flag <- TRUE
  }
}

In the end, there might be other things going on: when I start with 10 of each Hares/Lynxes, then the growth is a little explosive ...

DF
#    Year NumHares NumLynxes NumBabyHares NumBabyLynxes NumDeadHares NumDeadLynxes NumHaresEaten
# 1     0       10        10            8             0            0             0             2
# 2     1       16        10           12             2            0             5             4
# 3     2       24         7           18             2            0             5             4
# 4     3       38         4           28             1            0             4             4
# 5     4       62         1           46             1            0             0             2
# 6     5      106         2           80             0            0             0             5
# 7     6      181         2          136             0            0             0             9
# 8     7      308         2          231             0            0             0            15
# 9     8      524         2          393             0            0             0            26
# 10    9      891         2          668             0            0             0            45
# 11   10     1514         2         1136             0            0             0            76
# 12   11     2574         2         1930             0            0             0           129
# 13   12     4375         2         3281             0            0             0           219
# 14   13     7437         2         5578             0            0             0           372
# 15   14    12643         2         9482             0            0             0           632
# 16   15    21493         2        16120             0            0             0          1075
# 17   16    36538         2        27404             0            0             0          1827
# 18   17    62115         2        46586             0            0             0          3106
# 19   18   105595         2        79196             0            0             0          5280
# 20   19   179511         2       134633             0            0             0          8976
# 21   20   305168         2            0             0            0             0             0

Have fun.

r2evans
  • 77,184
  • 4
  • 55
  • 96
  • 1
    Wow, first of all thank you for taking time out to re-create the code thats awesome! The input I am supposed to follow is 20 years, 30 lynxes and 200 hares to start with. Thank you! – TheLearner Apr 08 '20 at 17:24
  • Does this answer your question? If so, please [accept it](https://stackoverflow.com/help/someone-answers). Thanks! – r2evans Apr 14 '20 at 22:44