我们在Vectors概念中看到,向量可以用在条件表达式里,得到一个逻辑向量。
而逻辑向量又可以用在诸如all()和any()这样的函数中。
v <- c(4, 7, 10)
v >= 6
#> [1] FALSE TRUE TRUE
all(v > 6)
#> [1] FALSE # not all elements match this condition
any(v > 6)
#> [1] TRUE # at least one element matches
这项技术的作用远不止于此。
可以用一个下标数字或一个下标向量,把向量中选定的元素取出来:
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
另一种做法是用逻辑向量筛选原向量,返回其中与TRUE值匹配的那些元素的子集:
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
从这里再走一小步,就可以用条件表达式生成这个布尔向量:
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
这些条件表达式可以任意复杂,可以涉及多个向量或函数:
v <- 1:10
w <- 10:1
# pairwise comparison
v > w
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
# shorter form
v[v > w]
#> [1] 6 7 8 9 10
我们把这个概念命名为“向量筛选”,希望这样能让尽可能多的程序员都看得懂。
在 R 社区里,“子集选取”这个说法可能更常见。
为简单起见,上面的例子比较的都是长度相同的向量。 如果长度对不上,会怎样?
结果会是一种刻意使用时非常强大、意外出现时却让人困惑的机制。
举个例子:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
这里发生了什么? 我们用一个长度为 2 的布尔向量给长度为 6 的向量取下标,结果却得到一个长度为 3 的向量。
如果某个向量对于它所在的表达式来说“太短”了,R 就会重复它,直到达到所需长度:这叫做“循环补齐”。
这就把我们的c(TRUE, FALSE)向量变成了c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE),接着v[cond]返回了v中值为TRUE的那些元素:在这里就是奇数。
那么下面这个例子里又发生了什么?
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
乍一看像是标量乘法,以及(略显奇怪的)标量加法,但 R 里根本没有标量。
实际上,这些数字会被循环补齐,得到一个与v长度相同的向量,然后使用c(0.5, 0.5, 0.5)或c(2, 2, 2)逐元素地进行运算。
循环补齐非常非常有用,但一定要确认这就是你想要的。
向量长度稍有出入(例如 99 与 100),就可能引发让人困惑的 bug。
有些作者建议,只有当其中一个向量的长度为 1 时,才应该使用循环补齐。
which()函数接受一个布尔实参,返回一个由结果为TRUE的下标组成的向量。
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
前面几节讲的是找出符合某个条件的元素。
如果你只是想知道向量里是否包含某个特定元素,可以用%in%运算符。
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
它在功能上等同于下面这段代码,但用起来方便得多:
length(v[v == 3]) > 0
#> [1] TRUE
此外还有一个%notin%运算符,它给出%in%的逻辑not。