Un vector (técnicamente un atomic vector) en R es una colección de valores del mismo type: habitualmente integer, numeric, character, logical; rara vez complex o raw.
Se almacenan de forma contigua en memoria, de manera similar a un array de C.
Si se le dan entradas mezcladas, R las convertirá silenciosamente a un tipo común, normalmente character, lo que puede dar lugar a resultados inesperados.
v <- c(2, 5.4, "txt")
v
#> [1] "2" "5.4" "txt"
typeof(v)
#> [1] "character"
Si ya tienes todos los valores, usa c() (abreviatura de «concatenate»):
x <- 10
v <- c(4, 7, x)
La función c() es muy flexible, y sus entradas pueden incluir otros vectores y rangos.
c() acepta cualquier número y variedad de entradas, y las aplana en un único vector.
Si el vector empieza siendo pequeño y crece mucho durante la ejecución (por ejemplo, al añadir valores en un bucle), puede ser mucho más rápido preasignar un vector largo que luego se pueda rellenar con datos reales:
v <- vector(length = 100)
Esto reduce al mínimo la necesidad de copiar el objeto: una operación lenta y que consume mucha memoria para vectores largos.
Para un rango de valores consecutivos (crecientes o decrecientes) puedes usar la notación :
v <- 1:5 # equivalent to c(1, 2, 3, 4, 5)
w <- 10:7 # c(10, 9, 8, 7)
En R, los rangos no son un tipo aparte: solo una forma abreviada de construir un vector.
La función seq() ofrece un control más preciso:
# specify step size
seq(2, 11, by = 3) # c(2, 5, 8, 11)
# specify number of values, evenly-spaced
seq(2.5, 4, length.out = 5) # c(2.500, 2.875, 3.250, 3.625, 4.000)
La función rep() facilita la creación de patrones repetidos:
count <- 3
rep(7, count)
#> [1] 7 7 7
rep(1:3, count)
#> [1] 1 2 3 1 2 3 1 2 3
rep(1:3, each = 2) # the "each" keyword is required in this case
#> [1] 1 1 2 2 3 3
Algunos rangos están predefinidos como constantes integradas:
letters # LETTERS is the UPPERCASE equivalent
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
month.name
#> [1] "January" "February" "March" "April" "May" "June" "July" "August"
#> [9] "September" "October" "November" "December"
month.abb
#> [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
Las operaciones se aplican automáticamente elemento a elemento a lo largo del vector. Los bucles, las comprensiones de listas y las recursiones habituales en otros lenguajes no son necesarios y, por razones de rendimiento, conviene evitarlos siempre que sea posible.
2 + 1:3 # c(3, 4, 5)
2 * 1:3 # c(2, 4, 6)
12 / 1:3 # c(12 6 4)
2 ^ (1:3) # c(2, 4, 8) : exponentiation, operator precedence order here needs ()
Los pares de vectores también funcionan, con los elementos tratados por pares:
v <- 1:3
w <- 5:7
v + w
#> [1] 6 8 10
v * w
#> [1] 5 12 21
v ^ w
#> [1] 1 64 2187
Esto es más potente de lo que parece a primera vista, y habrá más cosas que explorar en conceptos posteriores.
Como en muchos lenguajes, podemos usar el índice entre corchetes:
v <- 4:7
v[2]
#> [1] 5
v[2:4]
#> [1] 5 6 7
Pero cuidado: R usa de forma predeterminada una indexación basada en 1. Esto es habitual en los lenguajes científicos (Fortran, Matlab, Julia), pero difiere de la indexación basada en cero de C y sus muchos sucesores.
Se pueden usar rangos, como en el ejemplo anterior, pero también en la muy flexible función c():
v <- c(4, 7, 10)
w <- c(2, v[2:3], 11) # c(2, 7, 10, 11)
Los vectors (como casi todo en R) pueden tratarse como mutables, usando una sintaxis flexible similar para escribir en uno o varios elementos:
v <- 1:10
v[2] <- 42
v[4:6] <- c(11, 12, 13)
v
#> [1] 1 42 3 11 12 13 7 8 9 10
Quienes programan en otros lenguajes podrían pensar que v[-1] es una forma de acceder al último elemento de un vector.
¡Error!
Para eso se usa tail().
v <- c(2, 4, 6) # defaults to last 6 entries
tail(v)
#> [1] 2 4 6
tail(v, n=1) # just the last entry
#> [1] 6
En realidad, los índices negativos son una forma de eliminar un elemento:
v <- c(2, 4, 6)
v[-2]
#> [1] 2 6 # second element gone
Esto devuelve una copia más corta del vector. El propio v no cambia.
Aplicar comparaciones a un vector devuelve otro vector de la misma longitud con valores Boolean.
Esto puede resultar muy útil dentro de 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
Para expresiones lógicas más complicadas, es importante usar los operadores & y |, que funcionan con vectores.
Los operadores de doble carácter && y || solo están pensados para comparar valores individuales, como se explica en el [concepto de Boolean][concept-boolean].
v <- c(4, 7, 10)
v %% 2 == 0 & v > 5
#> [1] FALSE FALSE TRUE
Este será un tema al que volveremos en temas posteriores. Por ahora, fíjate solo en el formato de la salida en la consola:
1:20
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#> [18] 18 19 20
Las líneas empiezan con números entre [ ] que indican el índice del primer elemento (útil para vectores largos que se reparten en varias líneas).
Pero, ¿por qué los valores individuales van precedidos de [1]?
¡En R no existen los valores realmente escalares!
Lo que parece un único número o letra es en realidad un vector de longitud 1.
Ya hemos visto indicios de esto en los conceptos básicos y Boolean, con los tipos numérico y lógico.
Los vectores descritos antes se conocen más formalmente como atomic vectors. Esto se debe a que solo pueden contener valores simples e indivisibles (de ahí lo de «atómicos») como numeric, character, boolean. Las estructuras más complejas pueden formar recursive vectors, que se presentarán cuando se traten las lists.