Truth, missing values and choices
Comparisons that answer TRUE or FALSE, tests over a whole vector, if and else, and NA — the third answer that means "not known".
Comparisons answer a question
n > 10 is not a statement about the program, it is a value: TRUE or FALSE. You can store it, print it and count it. Because TRUE counts as one and FALSE counts as zero, sum(warm) is the number of TRUEs, which is a trick worth knowing on day one.
R also has a third answer. NA means "not known", and it is not the same as FALSE or as zero. A comparison with a missing value comes back as NA, because the honest answer to "is this unknown number bigger than ten" is that nobody knows yet. NA is not the string "NA" and not NaN either: NaN is a number that could not be computed, while NA is a value that was never known.
One test, every value
Because a comparison works element by element, temps > 24 gives a vector of TRUE and FALSE, one per temperature. That vector is a tool: R can count it, find positions in it, or use it to pick rows out of a table, which is exactly what the next chapters do.
which(test) turns it into positions. any(test) and all(test) collapse it to a single answer. And %in% asks a different kind of question: is this value one of those, as in day %in% c("Mon", "Tue"). which drops the NA positions rather than reporting them, so the count it gives can be smaller than the vector suggests; sum(test, na.rm = TRUE) is the count of every TRUE.
Deciding with if and else
if (n > 10) { ... } else { ... } runs one block or the other. The braces hold a group of lines; the condition must be a single TRUE or FALSE, so a whole vector will not do. Add else if when there are three or more cases.
R will not police your style here, but the shape above is the one that scales: braces on the same line as the keyword, and the branch you expect most often first.
A test, a count, and a branch
temps <- c(19, 25, 31, 18, 27)
warm <- temps > 24
sum(warm)
which(warm)
any(warm)
all(warm)
if (sum(warm) > 3) {
cat("a warm week\n")
} else {
cat("a mixed week\n")
}
# Missing values, and the test for them.
ages <- c(10, NA, 30)
is.na(ages)
sum(ages, na.rm = TRUE)A week of temperatures, asked properly
Run it and follow the five flags. Then change the cut-off from 24 to 26 and predict both the count and the which() line before you press Run again.
A missing value spreads through arithmetic: sum(ages) is NA if any value is unknown, because one unknown makes the total unknown. Ask for na.rm = TRUE to drop the missing values for that one calculation, and use is.na(x) to see where they are.
Two kinds of and
R has two pairs of logical operators and they are not interchangeable. & and | work element by element, so temps > 24 & temps < 30 gives one flag per temperature. && and || look only at the first element and stop as soon as the answer is decided; they are for a single condition, such as the test in an if.
Confusing them is a quiet bug. A filter written with && keeps or drops the whole vector based on its first value, and recent versions of R warn about it. If the thing on either side has a length above one, use the single-character form.
There is one more spelling worth knowing: xor(x, y) is TRUE when exactly one side is true. It is rare in filters, but it is the honest operator for "one or the other, not both", and seeing it once saves you from building the same thing out of two & tests.
The third answer
NA makes logic three-valued. TRUE & NA is NA, because the result depends on the unknown; FALSE & NA is FALSE, because false and anything is false. TRUE | NA is TRUE for the same reason, while FALSE | NA is NA. That is why is.na exists: it is the only test that answers TRUE or FALSE about a missing value.
ifelse(test, yes, no) is the vectorised branch. It takes a logical vector and returns a value for every position, which is what you want for a column. A plain if is for one decision, and its condition has to be length one; when you have a vector, reduce it with any, all or sum first.
Three mistakes worth naming. if (x == NA) never does what you want, because the comparison is NA and if refuses it; write is.na(x). if given a vector uses only its first value and warns, so reduce the vector first. And ifelse evaluates both of its value arguments in full, so a branch that would fail on some rows can still be built before it is thrown away.
Try these next
- Count how many values in a vector are above their mean, using
sum(x > mean(x)). - Replace the missing values in a vector with zero using
ifelse(is.na(x), 0, x). - Write a branch that prints "all warm" only when
all(temps > 20, na.rm = TRUE)is true.
A class list with one missing mark
Run it and find the row that has no verdict. Then replace the missing mark with 80, predict how many passes and whether all() changes, and Run again.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What is sum(warm) when warm is c(FALSE, TRUE, TRUE, FALSE)?
