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
여기서 무슨 일이 일어난 걸까요? 길이가 6인 벡터를 길이가 2인 불리언으로 인덱싱했는데, 결과로는 길이가 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) 헷갈리는 버그가 생길 수 있어요.
일부 저자는 벡터 중 하나의 길이가 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을 나타내요.