Vimos en el concepto Vectors que un vector se puede usar en una expresión condicional, lo que da como resultado un vector lógico.
Esto, a su vez, se puede usar en funciones como all() y 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
Esta técnica es mucho más poderosa que esto.
Se pueden extraer elementos seleccionados de un vector con un número de índice o con un vector de índices:
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
Como alternativa, usa un vector lógico para filtrar el vector original, lo que devuelve un subconjunto de entradas que coinciden con un valor TRUE:
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
Desde ahí hay un pequeño paso hasta generar el vector Boolean con una expresión condicional:
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
Estas expresiones condicionales pueden ser tan complejas como quieras, e involucrar varios vectores o funciones:
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
Llamamos a este concepto «Filtrado de vectores», con la esperanza de que tenga sentido para la mayor variedad posible de programadores.
Dentro de la comunidad de R, el término «Subsetting» es probablemente más común.
Por simplicidad, los ejemplos anteriores comparaban vectores de la misma longitud. ¿Qué pasa si las longitudes no coinciden?
La respuesta es algo poderoso si se usa a propósito, pero confuso si no lo esperas.
Un ejemplo:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
¿Qué pasó ahí? Indexamos un vector de longitud 6 con un vector Boolean de longitud 2, pero obtuvimos como resultado un vector de longitud 3.
Si un vector es de algún modo «demasiado corto» para la expresión en la que se usa, R lo repetirá hasta alcanzar la longitud deseada: a esto se le llama «reciclaje».
Esto convirtió nuestro vector c(TRUE, FALSE) en c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE), y v[cond] devolvió entonces los valores TRUE de v: en este caso, los números impares.
Entonces, ¿qué pasa en el siguiente ejemplo?
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
A primera vista parece una multiplicación por un escalar y una suma por un escalar (un poco rara), pero en R no existen los escalares.
En su lugar, los números se reciclan para obtener un vector que coincida con la longitud de v, y luego la operación aritmética se aplica elemento por elemento usando c(0.5, 0.5, 0.5) o c(2, 2, 2).
El reciclaje es muy, muy útil, pero asegúrate de que esto sea lo que quieres hacer.
Una pequeña diferencia en las longitudes de los vectores (por ejemplo, 99 frente a 100) puede causar bugs confusos.
Algunos autores recomiendan que el reciclaje solo se use cuando uno de los vectores tiene longitud 1.
La función which() recibe un argumento Boolean y devuelve un vector de índices que dan TRUE.
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
Las secciones anteriores trataban de encontrar entradas que cumplieran algún criterio.
Si solo necesitas saber si el vector contiene un elemento específico, se usa el operador %in%.
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
Esto es funcionalmente equivalente al siguiente código, pero mucho más cómodo:
length(v[v == 3]) > 0
#> [1] TRUE
También existe un operador %notin%, que da el not lógico de %in%.