我們在 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。