Label a return with if, else if and else, then label a whole vector at once with ifelse.
if runs a block of code when a condition is TRUE. else catches everything the tests missed, and else if adds another test in between.
In this lesson I label one return as up, flat or down, then swap to ifelse() to label a whole vector of returns at once, and I check for NA before I compare anything.
Step 1. One return, three labels
The condition sits in the round brackets and the code that runs sits in the curly brackets. R reads the tests from the top down and stops at the first one that is TRUE.
r <-0.014# one day's return, up 1.4 percentif (r >0.005) { label <-"up"} elseif (r <-0.005) { # only reached when the first test is FALSE label <-"down"} else { # everything the two tests missed label <-"flat"}print(label) # -> [1] "up"
[1] "up"
Change the return and a different branch runs. -0.021 fails the first test, passes the second, and never reaches the else.
First match wins, so the order you write the tests in decides the answer. Here both tests are true and only the top one runs.
r <-0.030# up 3 percentif (r >0.005) { size <-"up"} elseif (r >0.02) { # true as well, but R never gets here size <-"big up"}print(size) # -> [1] "up"
[1] "up"
Step 2. if takes one value, not a vector
if wants a single TRUE or a single FALSE. Hand it a vector of three and R stops. try() here just lets the page carry on past the error.
rets <-c(0.014, -0.021, 0.001) # three returns in one vectortry(if (rets >0.005) print("up")) # three answers, and if() can only use one
Error in if (rets > 0.005) print("up") : the condition has length > 1
# -> Error in if (rets > 0.005) print("up") : the condition has length > 1
rets > 0.005 is TRUE FALSE FALSE. R cannot pick which of the three to act on, so it refuses.
Step 3. ifelse() labels the whole vector
ifelse(test, yes, no) takes three arguments: the test, the value to use where the test is TRUE, and the value to use where it is FALSE. It runs element by element and gives you back a vector the same length as the test.
rets <-c(0.014, -0.021, 0.001, 0.008, -0.002) # five daily returnslabels <-ifelse(rets >0, "up", "down") # test, value if TRUE, value if FALSEprint(labels) # -> [1] "up" "down" "up" "up" "down"
[1] "up" "down" "up" "up" "down"
print(length(labels)) # -> [1] 5
[1] 5
Five returns in, five labels out. No loop and no error.
Step 4. Three labels from two tests
ifelse() gives two answers. For three, put a second ifelse() in the no slot: it only sees the elements the first test rejected.
Below are six daily returns. I call a move above 2 percent big up, a move below -2 percent big down, and everything else quiet.
The day of exactly 0.020 came out quiet. > is strict, so 0.02 is not above 0.02. Move the boundary with >= if you want it counted as a big up.
Step 5. Check for NA before you compare
A missing value in R is NA. Comparing anything with NA gives NA, not TRUE and not FALSE.
print(NA>0) # -> [1] NA
[1] NA
print(NA+1) # -> [1] NA
[1] NA
So an NA in the vector produces an NA in the labels. ifelse() has nothing to choose from and passes the gap through.
rets <-c(0.014, NA, -0.021, 0.001) # the second day never arrivedprint(ifelse(rets >0, "up", "down")) # -> [1] "up" NA "down" "up"
[1] "up" NA "down" "up"
is.na(x) is the test that does work on NA, and it returns TRUE or FALSE. Put it first, so the missing day is caught before any comparison runs.
lab <-ifelse(is.na(rets), "missing", # catch the gap firstifelse(rets >0, "up", # then the ordinary tests"down"))print(lab) # -> [1] "up" "missing" "down" "up"
[1] "up" "missing" "down" "up"
The same holds for if. An NA condition is neither TRUE nor FALSE, so R stops rather than guess.
r <-NA# today's return did not arrivetry(if (r >0) print("up")) # the condition is NA, not TRUE or FALSE
Error in if (r > 0) print("up") : missing value where TRUE/FALSE needed
# -> Error in if (r > 0) print("up") : missing value where TRUE/FALSE neededif (is.na(r)) { # the guard goes on top label <-"missing"} elseif (r >0) { label <-"up"} else { label <-"down"}print(label) # -> [1] "missing"
[1] "missing"
Your turn
Take vals <- c(-0.04, 0.0, NA, 0.03, -0.012). Label each one "big up" above 0.01, "big down" below -0.01, "small" in between, and "missing" where the value is NA. Then count the missing ones.
TipShow answer
vals <-c(-0.04, 0.0, NA, 0.03, -0.012)out <-ifelse(is.na(vals), "missing", # the NA test comes firstifelse(vals >0.01, "big up",ifelse(vals <-0.01, "big down","small")))print(out) # -> [1] "big down" "small" "missing" "big up" "big down"print(sum(out =="missing")) # -> [1] 1