Vimos en el concepto Vectors que se puede usar un vector en una expresión condicional, lo que da 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 potente 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 y obtener 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 de tipo 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 arbitrariamente complejas e implicar 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, todos los ejemplos anteriores comparaban vectores de la misma longitud. ¿Qué ocurre si las longitudes no coinciden?
La respuesta es algo potente si se usa deliberadamente, pero confuso si no se espera.
Un ejemplo:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
¿Qué ocurrió ahí? Indexamos un vector de longitud 6 con un vector de tipo 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é ocurre 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 con un escalar (un poco rara), pero en R no hay escalares.
En su lugar, los números se reciclan para obtener un vector que coincida con la longitud de v; después, la operación aritmética se aplica elemento a 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 es lo que pretendes.
Una ligera discrepancia en las longitudes de los vectores (por ejemplo, 99 frente a 100) puede provocar bugs confusos.
Algunos autores recomiendan que el reciclaje solo se use cuando uno de los vectores tiene longitud 1.
La función which() toma un argumento de tipo 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 coinciden con 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%.