Vectorised thinking is the single most important idea for writing correct, fast R.
R has no scalars: 5 is a numeric vector of length one. An atomic vector holds elements of a single type: logical (TRUE/FALSE), integer (5L), double (5, the default for numbers), character ("a") and the rarer complex and raw.
Create vectors with c(1, 2, 3), sequences with 1:10 or seq(0, 1, by = 0.25), and repetitions with rep("a", 3). length(x) gives the size.
Combining different types coerces to the most flexible one: logical < integer < double < character. c(1, "a", TRUE) becomes all character. Check types with typeof() and convert with as.numeric(), as.character() and friends (invalid conversions give NA with a warning).
Arithmetic and comparison are vectorised: x * 2 doubles every element, x > 3 returns a logical vector, and sum, mean, max, cumsum work on whole vectors. When two vectors differ in length, the shorter is recycled: c(1, 2, 3, 4) + c(10, 20) gives 11 22 13 24. Recycling a length-1 vector is everyday R; recycling anything else is usually a bug, and R warns when the lengths don't divide evenly.
sum(x > 3) counts matches and mean(x > 3) gives the proportion, because TRUE counts as 1.
Example
x <- c(4, 8, 15, 16, 23, 42)
print(x * 2) #> [1] 8 16 30 32 46 84
print(x > 15) #> [1] FALSE FALSE FALSE TRUE TRUE TRUE
print(sum(x > 15)) #> [1] 3
print(mean(x)) #> [1] 18
print(typeof(1:3)) #> [1] "integer"
print(typeof(c(1, 2))) #> [1] "double"
print(c(1, "a", TRUE)) #> [1] "1" "a" "TRUE"
print(c(1, 2, 3, 4) + c(10, 20)) #> [1] 11 22 13 24
print(seq(0, 1, by = 0.25)) #> [1] 0.00 0.25 0.50 0.75 1.00
print(as.numeric("3.5") + 1) #> [1] 4.5Remember
- Everything is a vector; a single value is a length-1 vector
- Mixed types coerce: logical < integer < double < character
- Operations are vectorised and shorter vectors are recycled
sum(cond)counts,mean(cond)gives a proportion
Watch out for
- Relying on recycling with unequal lengths by accident; it silently produces wrong results when lengths divide evenly.