miércoles, 2 de septiembre de 2015

Selección de elementos de un vector de acuerdo a un patrón

[Lenguajes: R]

Problema

Se requiere seleccionar algunos elementos de un vector de acuerdo con un patrón repetitivo. Como ejemplo, se quiere seleccionar el tercero y cuarto elementos de cada diez elementos en secuencia.

Para ilustrar el problema, supongamos que se tiene un vector que consiste en 55 numeros, empezando con el 101, como sigue:

(v <- 101:155)
##  [1] 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
## [18] 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
## [35] 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
## [52] 152 153 154 155

De ahí se quieren seleccionar, como se dijo, el tercer y cuarto elementos de cada diez elementos en la secuencia, de modo que se produzca una salida como la que se muestra a continuación:

##  [1] 103 104 113 114 123 124 133 134 143 144 153 154

Solución

En R, los vectores de lógicos se pueden utilizar para indexar otro vector, a manera de una máscara que prende o apaga los elementos del vector que se seleccionan a la salida. Así, por ejemplo, si queremos seleccionar los elementos impares en el vector v, se puede hacer como se muestra:

# Los elementos de v que no son divisibles por 2:
(ii <- v %% 2) # operación módulo
##  [1] 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
## [36] 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
# Convertimos ii a lógico
ii <- as.logical(ii)
# Y los impares son:
v[ii]
##  [1] 101 103 105 107 109 111 113 115 117 119 121 123 125 127 129 131 133
## [18] 135 137 139 141 143 145 147 149 151 153 155

Cuando el vector indexador de otro vector en R es lógico, y es de un tamaño menor al vector indexado, R lo recicla, o sea, repite la secuencia y esto es algo de lo que podemos sacar ventaja para resolver nuestro problema original; pero antes, veamos que la salida anterior, si, como es el caso, v es una secuencia de números enteros conocida, se puede producir de manera muy simple, como sigue:

v[c(TRUE, FALSE)]
##  [1] 101 103 105 107 109 111 113 115 117 119 121 123 125 127 129 131 133
## [18] 135 137 139 141 143 145 147 149 151 153 155

Si usamos este mismo comportamiento, podemos producir la máscara repetitiva del patrón, de la manera siguiente:

# Un vector lógico de 10 elementos inicializados como FALSE
ii <- logical(10)
# "Prendemos" los elementos 3 y 4 de la máscara
ii[c(3,4)] <- TRUE
# La "máscara"" es:
ii
##  [1] FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE

Ahora, simplemente se usa esa máscara como un índice del vector original v:

v[ii]
##  [1] 103 104 113 114 123 124 133 134 143 144 153 154

Y el problema ha quedado resuelto!

Notemos que no hay restricción en la clase de elementos de vector que se indexa. Hagamos, por ejemplo, un vector de 55 letras seleccionadas aleatoreamente:

(aa <- sample(letters, 55, replace=TRUE))
##  [1] "l" "s" "k" "i" "t" "f" "s" "d" "g" "d" "g" "b" "q" "w" "u" "u" "l"
## [18] "k" "v" "p" "r" "j" "h" "z" "q" "f" "d" "m" "y" "p" "z" "t" "j" "l"
## [35] "d" "a" "s" "c" "l" "q" "z" "m" "m" "e" "t" "l" "n" "f" "f" "p" "o"
## [52] "c" "a" "q" "y"

Seleccionemos de ese vector, igual, el tercero y cuarto elementos de cada diez; para ello, podemos utilizar la misma máscara generada previamente (ii):

aa[ii]
##  [1] "k" "i" "q" "w" "h" "z" "j" "l" "m" "e" "a" "q"

Si quieres ver este mismo post en mis publicaciones de Rpubs: