Home > other >  R 1:nrow() that loops ok but ruturning NULL
R 1:nrow() that loops ok but ruturning NULL

Time:03-23

** Problem Loop **

A loop that only returns [1] FALSE result, yet loops 1:nrow(dataset). This should set FALSE when year equals 1987 and month equals 3rd month.

souvenir_festival<- c(n)
for(i in 1:nrow(souvenirs)){
  if (as.numeric(year(yearmonth(souvenirs$Month))) == 1987) {
      souvenir_festival[i] <- FALSE
    }
    else if (!as.numeric(month(souvenirs$Month)) == 3) {
      souvenir_festival[i] <- TRUE
    }
}

Greg's suggestion code:

    souvenir_festival <- !(month(souvenirs$Month) == 3 & year(yearmonth(souvenirs$Month)) == 1987)

length(souvenir_festival)

I had the inner loop test month = 3 and still the sf is 1 row, e.g, length(souvenir_festival)

** DPUT data **


    structure(list(Month = structure(c(6209, 6240, 6268, 6299, 6329, 
    6360, 6390, 6421, 6452, 6482, 6513, 6543, 6574, 6605, 6634, 6665, 
    6695, 6726, 6756, 6787, 6818, 6848, 6879, 6909, 6940, 6971, 6999, 
    7030, 7060, 7091, 7121, 7152, 7183, 7213, 7244, 7274, 7305, 7336, 
    7364, 7395, 7425, 7456, 7486, 7517, 7548, 7578, 7609, 7639, 7670, 
    7701, 7729, 7760, 7790, 7821, 7851, 7882, 7913, 7943, 7974, 8004, 
    8035, 8066, 8095, 8126, 8156, 8187, 8217, 8248, 8279, 8309, 8340, 
    8370, 8401, 8432, 8460, 8491, 8521, 8552, 8582, 8613, 8644, 8674, 
    8705, 8735), class = c("yearmonth", "vctrs_vctr")), Sales = c(1664.81, 
    2397.53, 2840.71, 3547.29, 3752.96, 3714.74, 4349.61, 3566.34, 
    5021.82, 6423.48, 7600.6, 19756.21, 2499.81, 5198.24, 7225.14, 
    4806.03, 5900.88, 4951.34, 6179.12, 4752.15, 5496.43, 5835.1, 
    12600.08, 28541.72, 4717.02, 5702.63, 9957.58, 5304.78, 6492.43, 
    6630.8, 7349.62, 8176.62, 8573.17, 9690.5, 15151.84, 34061.01, 
    5921.1, 5814.58, 12421.25, 6369.77, 7609.12, 7224.75, 8121.22, 
    7979.25, 8093.06, 8476.7, 17914.66, 30114.41, 4826.64, 6470.23, 
    9638.77, 8821.17, 8722.37, 10209.48, 11276.55, 12552.22, 11637.39, 
    13606.89, 21822.11, 45060.69, 7615.03, 9849.69, 14558.4, 11587.33, 
    9332.56, 13082.09, 16732.78, 19888.61, 23933.38, 25391.35, 36024.8, 
    80721.71, 10243.24, 11266.88, 21826.84, 17357.33, 15997.79, 18601.53, 
    26155.15, 28586.52, 30505.41, 30821.33, 46634.38, 104660.67)), row.names = c(NA, 
    -84L), key = structure(list(.rows = structure(list(1:84), ptype = integer(0), class = c("vctrs_list_of", 
    "vctrs_vctr", "list"))), row.names = c(NA, -1L), class = c("tbl_df", 
    "tbl", "data.frame")), index = structure("Month", ordered = TRUE), index2 = "Month", interval = structure(list(
        year = 0, quarter = 0, month = 1, week = 0, day = 0, hour = 0, 
        minute = 0, second = 0, millisecond = 0, microsecond = 0, 
        nanosecond = 0, unit = 0), .regular = TRUE, class = c("interval", 
    "vctrs_rcrd", "vctrs_vctr")), class = c("tbl_ts", "tbl_df", "tbl", 
    "data.frame"))

CodePudding user response:

Here's a fixed version of your code:

sf <- logical(nrow(souvenirs))
for(i in 1:nrow(souvenirs)){
  if (year(souvenirs$Month[i]) == 1987 & month(souvenirs$Month[i]) == 3) {
      sf[i] <- FALSE
  } else {
    sf[i] <- TRUE
  }
}
sf
#  [1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
# [17]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
# [33]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
# [49]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
# [65]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
# [81]  TRUE  TRUE  TRUE  TRUE

A more concise and efficient version for the same result:

sf <- !(month(souvenirs$Month) == 3 & year(souvenirs$Month) == 1987)

Here's a breakdown of problems:

sf <- c(n) 
## n is not defined. 
## If we assume n <- nrow(souvenirs), this is still bad because then we 
## have basically sf <- 84. The goal for `sf` should be for it to be 
## class logical and have length equal to the number of rows, hence
## sf <- logical(nrow(souvenirs))  # is good
for(i in 1:nrow(souvenirs)){
  # your `Month` column is already yearmonth class.
  # year(souvenirs$Month) returns a numeric year, the as.numeric()` and yearmonth()` are pointless
  # ditto below, month(souvenirs$Month) returns the numeric month - nothing more needed
  
  # If you have a loop over `i`, you should expect to use `i` in the loop.
  # You do use it for the ouptut, but not for the input. 
  # souvenirs$Month is the whole column. But in a loop you're trying to test
  # one value at a time, so you need to use `souvenirs$Month[i]`
  
  if (as.numeric(year(yearmonth(souvenirs$Month))) == 1987) {
   if (as.numeric(month(souvenirs$Month)) == 3) {
      sf[i] <- FALSE
    }
    else {
      sf[i] <- TRUE
    }
  }
  # Your nesting structure is messing you up here.
  # The else{} only pertains to the *inner* if(month == 3).
  # This inner if(month == 3) only is run if the outer if(year == 1987) is true.
  # So if the year isn't 1987, the inner if(){}else{} is never run, and 
  # the TRUE result is never assigned.
  # If you want to check two conditions simultaneously, the AND operator `&`
  # is better than two if statements.
  # You could fix the nesting, e.g. (pseudocode)
  # if(year == 1987) {
  #   if(month == 3) {
  #     sf[i] <- TRUE
  #   }
  # }  ## Note that we close both inner AND outer if(){} before the else{} 
  # else {
  #   sf[i] <- FALSE
  # }
  # But the `&` is much cleaner
}
nrow(sf)
## sf is a vector, it doesn't have rows. Use `length()`

More generally, if your code is if(X is TRUE) {TRUE} else {FALSE}, that's a really long way to write X--you want something that is identical to X. Here, your code is if(X is TRUE) {FALSE} else {TRUE}, which can be written !X. Thanks to R's vectorization, that's how my version works: sf <- !(month(souvenirs$Month) == 3 & year(souvenirs$Month) == 1987).

  • Related