6 Contenedores de datos

Hasta ahora hemos creado objetos simples que contienen solo un valor. Sin embargo, pudimos ver que un objeto tenía atributos diferentes, como su valor, pero también el tipo de datos contenidos (e.g., numeric, character). Ahora vamos a ver que hay diferentes tipos de contenedores para almacenar datos múltiples.

6.1 El contenedor vector

En R, un vector es una combinación de datos con la particularidad de que todos los datos contenidos en un vector son del mismo tipo. Podemos almacenar por ejemplo múltiples elementos del tipo character o numeric en un vector, pero no ambos. El contenedor vector es importante porque es el elemento básico de R.

6.1.1 Crear un vector

Para crear un vector utilizaremos la función c() que permite combinar elementos en un vector. Los elementos para combinar deben estar separados por comas.

miVec01 <- c(1, 2, 3, 4) # un vector de 4 elementos de tipo numeric ; double
print(miVec01)
## [1] 1 2 3 4
typeof(miVec01)
## [1] "double"
is.vector(miVec01)
## [1] TRUE

La funcion is.vector() permite verificar el tipo de contenedor.

miVec02 <- c("a", "b", "c") 
print(miVec02)
## [1] "a" "b" "c"
typeof(miVec02)
## [1] "character"
is.vector(miVec02)
## [1] TRUE
miVec03 <- c(TRUE, FALSE, FALSE, TRUE)
print(miVec03)
## [1]  TRUE FALSE FALSE  TRUE
typeof(miVec03)
## [1] "logical"
is.vector(miVec03)
## [1] TRUE
miVecNA <- c(1, NA, 3, NA, 5)
print(miVecNA)
## [1]  1 NA  3 NA  5
typeof(miVecNA)
## [1] "double"
is.vector(miVecNA)
## [1] TRUE
miVec04 <- c(1, "a")
print(miVec04)
## [1] "1" "a"
typeof(miVec04)
## [1] "character"
is.vector(miVec04)
## [1] TRUE

Si combinamos diferentes tipos de datos, R intentará transformar los elementos en un tipo de forma predeterminada. Si como aquí en el objeto miVec03 tenemos los tipos character y numeric, R convertirá todos los elementos en character.

miVec05 <- c(factor("abc"), "def")
print(miVec05)
## [1] "1"   "def"
typeof(miVec05)
## [1] "character"
miVec06 <- c(TRUE, "def")
print(miVec06)
## [1] "TRUE" "def"
typeof(miVec06)
## [1] "character"
miVec07 <- c(factor("abc"), 55)
print(miVec07)
## [1]  1 55
typeof(miVec07)
## [1] "double"
miVec08 <- c(TRUE, 55)
print(miVec08)
## [1]  1 55
typeof(miVec08)
## [1] "double"

También podemos combinar objetos existentes dentro de un vector.

miVec09 <- c(miVec02, "d", "e", "f")
print(miVec09)
## [1] "a" "b" "c" "d" "e" "f"
miVec10 <- c("aaa", "aa", miVec09, "d", "e", "f")
print(miVec10)
##  [1] "aaa" "aa"  "a"   "b"   "c"   "d"   "e"   "f"   "d"   "e"   "f"
miVec11 <- c(789, miVec01 , 564)
print(miVec11)
## [1] 789   1   2   3   4 564

6.1.2 Hacer operaciones con un vector

También podemos realizar operaciones en un vector.

print(miVec01)
## [1] 1 2 3 4
miVec01 + 1
## [1] 2 3 4 5
miVec01 - 1
## [1] 0 1 2 3
miVec01 * 2
## [1] 2 4 6 8
miVec01 /10
## [1] 0.1 0.2 0.3 0.4

Las operaciones de un vector a otro también son posibles, pero se debe tener cuidado para asegurar que el número de elementos en un vector sea el mismo que el otro, de lo contrario R realizará el cálculo comenzando desde el inicio del vector mas pequeño. Aquí hay un ejemplo para ilustrar lo que R hace:

miVec12 <- c(1, 1, 1, 1, 1, 1, 1, 1, 1)
print(miVec12)
## [1] 1 1 1 1 1 1 1 1 1
miVec13 <- c(10, 20, 30)
print(miVec13)
## [1] 10 20 30
miVec12 + miVec13 # vectores de diferentes tamaños: atención al resultado
## [1] 11 21 31 11 21 31 11 21 31
miVec14 <- c(10, 20, 30, 40, 50, 60, 70, 80, 90)
print(miVec14)
## [1] 10 20 30 40 50 60 70 80 90
miVec12 + miVec14 # los vectores tienen el mismo tamaño
## [1] 11 21 31 41 51 61 71 81 91
miVec15 <- c(1, 1, 1, 1)
print(miVec15)
## [1] 1 1 1 1
miVec15 + miVec13 # vectores de diferentes tamaños y no múltiples
## Warning in miVec15 + miVec13: la taille d'un objet plus long n'est pas multiple
## de la taille d'un objet plus court
## [1] 11 21 31 11

6.1.3 Acceder a los valores de un vector

Suele pasar que sea necesario poder acceder a los valores de un vector, es decir, recuperar un valor o un grupo de valores dentro de un vector. Para acceder a un elemento de un vector usamos los corchetes []. Entre los corchetes, podemos usar un número correspondiente al número del elemento en el vector.

miVec20 <- c(10, 20, 30, 40, 50, 60, 70, 80, 90)
miVec21 <- c("a", "b", "c", "d", "e", "f", "g", "h", "i")
print(miVec20)
## [1] 10 20 30 40 50 60 70 80 90
print(miVec21)
## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i"
print(miVec20[1])
## [1] 10
print(miVec21[3])
## [1] "c"

También podemos usar la combinación de diferentes elementos (otro vector).

print(miVec20[c(1, 5, 9)])
## [1] 10 50 90
print(miVec21[c(4, 3, 1)])
## [1] "d" "c" "a"
print(miVec21[c(4, 4, 3, 4, 3, 2, 5)])
## [1] "d" "d" "c" "d" "c" "b" "e"

También podemos seleccionar elementos usando un operador de comparación o un operador lógico.

print(miVec20[miVec20 >= 50])
## [1] 50 60 70 80 90
print(miVec20[(miVec20 >= 50) & ((miVec20 < 80))])
## [1] 50 60 70
print(miVec20[miVec20 != 50])
## [1] 10 20 30 40 60 70 80 90
print(miVec20[miVec20 == 30])
## [1] 30
print(miVec20[(miVec20 == 30) | (miVec20 == 50)])
## [1] 30 50
print(miVec21[miVec21 == "a"])
## [1] "a"

Otra característica interesante es la posibilidad de condicionar los elementos a seleccionar en base a otro vector.

print(miVec21[miVec20 >= 50])
## [1] "e" "f" "g" "h" "i"
print(miVec21[(miVec20 >= 50) & ((miVec20 < 80))])
## [1] "e" "f" "g"
print(miVec21[miVec20 != 50])
## [1] "a" "b" "c" "d" "f" "g" "h" "i"
print(miVec21[miVec20 == 30])
## [1] "c"
print(miVec21[(miVec20 == 30) | (miVec20 == 50)])
## [1] "c" "e"
print(miVec21[(miVec20 == 30) | (miVec21 == "h")])
## [1] "c" "h"

También es posible excluir ciertos elementos en lugar de seleccionarlos.

print(miVec20[-1])
## [1] 20 30 40 50 60 70 80 90
print(miVec21[-5])
## [1] "a" "b" "c" "d" "f" "g" "h" "i"
print(miVec20[-c(1, 2, 5)])
## [1] 30 40 60 70 80 90
print(miVec21[-c(1, 2, 5)])
## [1] "c" "d" "f" "g" "h" "i"

Los elementos de un vector también se pueden seleccionar sobre la base de un vector tipo logical. En este caso, solo se seleccionarán elementos con un valor TRUE.

miVec22 <- c(TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE)
print(miVec21[miVec22])
## [1] "a" "b" "d" "f" "h" "i"

6.1.4 Dar nombres a los elementos de un vector

Los elementos de un vector se pueden nombrar para referenciarlos y luego selectionarlos. La función names() recupera los nombres de los elementos de un vector.

miVec23 <- c(aaa = 10, bbb = 20, ccc = 30, ddd = 40, eee = 50)
print(miVec23)
## aaa bbb ccc ddd eee 
##  10  20  30  40  50
print(miVec23["bbb"])
## bbb 
##  20
print(miVec23[c("bbb", "ccc", "bbb")])
## bbb ccc bbb 
##  20  30  20
names(miVec23)
## [1] "aaa" "bbb" "ccc" "ddd" "eee"

6.1.5 Editar los elementos de un vector

Para modificar un vector, operamos de la misma manera que para modificar un objeto simple, con el signo <- y el elemento o los elementos a modificar entre corchetes.

print(miVec21)
## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i"
miVec21[3] <- "zzz"
print(miVec21)
## [1] "a"   "b"   "zzz" "d"   "e"   "f"   "g"   "h"   "i"
miVec21[(miVec20 >= 50) & ((miVec20 < 80))] <- "qwerty"
print(miVec21)
## [1] "a"      "b"      "zzz"    "d"      "qwerty" "qwerty" "qwerty" "h"     
## [9] "i"
print(miVec23)
## aaa bbb ccc ddd eee 
##  10  20  30  40  50
miVec23["ccc"] <- miVec23["ccc"] + 100
print(miVec23)
## aaa bbb ccc ddd eee 
##  10  20 130  40  50

También podemos cambiar los nombres asociados con los elementos de un vector.

print(miVec23)
## aaa bbb ccc ddd eee 
##  10  20 130  40  50
names(miVec23)[2] <- "bb_bb"
print(miVec23)
##   aaa bb_bb   ccc   ddd   eee 
##    10    20   130    40    50

Podemos hacer mucho más con un vector y volveremos a su manejo y operaciones posibles en el capítulo sobre funciones.

6.2 El contenedor list

El segundo tipo de contenedor que vamos a presentar es el contenedor list, que es también el segundo contenedor después del tipovector debido a su importancia en la programación con R. El contenedor de tipo list le permite almacenar listas de elementos. Contrariamente a lo que vimos antes con el tipo vector, los elementos del tipo list pueden ser diferentes (por ejemplo, un vector de tipo numeric, luego un vector de tipo character). Los elementos del tipo list también pueden ser contenedores diferentes (por ejemplo, un vector, luego una list). El tipo de contenedor list tendrá mas sentido cuando hayamos estudiado los bucles y funciones de la familia apply.

6.2.1 Crear una list

Para crear una list usaremos la función list(), que toma elementos (objetos) como argumentos.

miList01 <- list()
print(miList01)
## list()
miList02 <- list(5, "qwerty", c(4, 5, 6), c("a", "b", "c"))
print(miList02)
## [[1]]
## [1] 5
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "a" "b" "c"
miList03 <- list(5, "qwerty", list(c(4, 5, 6), c("a", "b", "c")))
print(miList03)
## [[1]]
## [1] 5
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [[3]][[1]]
## [1] 4 5 6
## 
## [[3]][[2]]
## [1] "a" "b" "c"

La función is.list() se usa para probar si hemos creado un objeto de tipo list.

is.list(miList02)
## [1] TRUE
typeof(miList02)
## [1] "list"

6.2.2 Acceder a los valores de una list

Los elementos del contenedor list son identificables por los corchetes dobles [[ ]].

print(miList02)
## [[1]]
## [1] 5
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "a" "b" "c"

En el objeto de tipo list miList02, hay cuatro elementos identificables con [[1]], [[2]], [[3]] y [[4]]. Cada uno de los elementos es de tipo vector. El primer elemento tiene un tamaño de 1 con elementos del tipo double, el segundo elemento tiene un tamaño de 1 con elementos del tipo character, el tercero elemento tiene un tamaño de 3 con elementos del tipo double, y el cuarto elemento tiene un tamaño de 3 con elementos del tipo character.

typeof(miList02)
## [1] "list"
print(miList02[[1]])
## [1] 5
typeof(miList02[[1]])
## [1] "double"
print(miList02[[2]])
## [1] "qwerty"
typeof(miList02[[2]])
## [1] "character"
print(miList02[[3]])
## [1] 4 5 6
typeof(miList02[[3]])
## [1] "double"
print(miList02[[4]])
## [1] "a" "b" "c"
typeof(miList02[[4]])
## [1] "character"

El acceso al segundo elemento del vector ubicado en la cuarta posición de la list se hace con miList02[[4]][2]. Usamos doble corchetes para el cuarto elemento de la list, luego corchetes simples para el segundo elemento del vector.

print(miList02[[4]][2])
## [1] "b"

Como una list puede contener una o más list, podemos acceder a la información buscada combinando corchetes dobles. El objeto miList04 es una list de dos elementos: la list miList02 y la list miList03. El objeto miList03 en sí contiene una list como tercer elemento. Para acceder al primer elemento del vector en la primera posición del elemento en la tercera posición del segundo elemento del list miList04, podemos usar miList04[[2]][[3]][[1]][1]. No hay límite en cuanto a la profundidad de list pero en la práctica raramente hay necesidad de hacer list de list de list.

miList04 <- list(miList02, miList03)
print(miList04)
## [[1]]
## [[1]][[1]]
## [1] 5
## 
## [[1]][[2]]
## [1] "qwerty"
## 
## [[1]][[3]]
## [1] 4 5 6
## 
## [[1]][[4]]
## [1] "a" "b" "c"
## 
## 
## [[2]]
## [[2]][[1]]
## [1] 5
## 
## [[2]][[2]]
## [1] "qwerty"
## 
## [[2]][[3]]
## [[2]][[3]][[1]]
## [1] 4 5 6
## 
## [[2]][[3]][[2]]
## [1] "a" "b" "c"
print(miList04[[2]][[3]][[1]][1])
## [1] 4

Para concretar el ejemplo anterior, podemos imaginar especies de barrenadores del maíz (Sesamia nonagrioides y Ostrinia nubilalis), muestreados en diferentes sitios, con diferentes abundancias en cuatro fechas. Aquí daremos nombres a los elementos de las list.

bddInsect <- list(Snonagrioides = list(site01 = c(12, 5, 8, 7), site02 = c(5, 23, 4, 41), site03 = c(12, 0, 0, 0)), Onubilalis = list(site01 = c(12, 1, 2, 3), site02 = c(0, 0, 0, 1), site03 = c(1, 1, 2, 3)))
print(bddInsect)
## $Snonagrioides
## $Snonagrioides$site01
## [1] 12  5  8  7
## 
## $Snonagrioides$site02
## [1]  5 23  4 41
## 
## $Snonagrioides$site03
## [1] 12  0  0  0
## 
## 
## $Onubilalis
## $Onubilalis$site01
## [1] 12  1  2  3
## 
## $Onubilalis$site02
## [1] 0 0 0 1
## 
## $Onubilalis$site03
## [1] 1 1 2 3

Leer una larga línea de código como la línea para crear el objeto bddInsect resulta difícil porque la profundidad de los elementos solo se puede deducir de los paréntesis. Es por eso que vamos a reorganizar el código para que sea más legible mediante el margen adicional. El margen adicional implica poner información en diferentes niveles para que podamos identificar rápidamente los diferentes niveles de un código. Para aplicar el margen adicional se presiona la tecla de tabulación. Volveremos al margen adicional con más detalles en el capítulo sobre bucles. Recordemos por el momento que si una línea de código es demasiado larga, podemos saltar de línea y usar el margen adicional. R leerá todo como una sola línea de código.

bddInsect <- list(
  Snonagrioides = list(
    site01 = c(12, 5, 8, 7), 
    site02 = c(5, 23, 4, 41), 
    site03 = c(12, 0, 0, 0)
  ), 
  Onubilalis = list(
    site01 = c(12, 1, 2, 3), 
    site02 = c(0, 0, 0, 1), 
    site03 = c(1, 1, 2, 3)
  )
)

Podemos seleccionar los datos de abundancia del segundo sitio de la primera especie como previamente bddInsect[[1]][[2]], o alternativamente usando los nombres de los elementos bddInsect$Snonagrioides$site02. Para hacer esto usamos el signo $, o como alternativa el nombre de los elementos con comillas simples o dobles bddInsect[['Snonagrioides']][['sitio02']].

print(bddInsect[[1]][[2]])
## [1]  5 23  4 41
print(bddInsect$Snonagrioides$site02)
## [1]  5 23  4 41
print(bddInsect[['Snonagrioides']][['site02']])
## [1]  5 23  4 41

En cuanto a los vectores, podemos recuperar los nombres de los elementos con la función names().

names(bddInsect)
## [1] "Snonagrioides" "Onubilalis"
names(bddInsect[[1]])
## [1] "site01" "site02" "site03"

Cuando usamos los corchetes dobles [[]] o el signo $, R devuelve el contenido del elemento seleccionado. En nuestro ejemplo, los datos de abundancia están contenidos como un vector, por lo que R devuelve un elemento del tipo vector. Si queremos seleccionar un elemento de una list pero manteniendo el formato list, entonces podemos usar corchetes simples [].

print(bddInsect[[1]][[2]])
## [1]  5 23  4 41
typeof(bddInsect[[1]][[2]])
## [1] "double"
is.list(bddInsect[[1]][[2]])
## [1] FALSE
print(bddInsect[[1]][2])
## $site02
## [1]  5 23  4 41
typeof(bddInsect[[1]][2])
## [1] "list"
is.list(bddInsect[[1]][2])
## [1] TRUE

El uso de corchetes simples [] es útil cuando queremos recuperar varios elementos de una list. Por ejemplo, para seleccionar las abundancias de insectos de los primeros dos sitios de la primera especie, usaremos bddInsect [[1]][c(1, 2)] o alternativamente bddInsect[[1]][c("site01", "sitio02")].

print(bddInsect[[1]][c(1, 2)])
## $site01
## [1] 12  5  8  7
## 
## $site02
## [1]  5 23  4 41
print(bddInsect[[1]][c("site01", "site02")])
## $site01
## [1] 12  5  8  7
## 
## $site02
## [1]  5 23  4 41

6.2.3 Editar una list

Una list se puede modificar de la misma manera que para el contenedor vector, es decir, haciendo referencia con corchetes al elemento que queremos modificar.

print(miList02)
## [[1]]
## [1] 5
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "a" "b" "c"
miList02[[1]] <- 12
print(miList02)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "a" "b" "c"
miList02[[4]] <- c("d", "e", "f")
print(miList02)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "d" "e" "f"
miList02[[4]] <- c("a", "b", "c", miList02[[4]], "g", "h", "i")
print(miList02)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i"
miList02[[4]][5] <- "eee"
print(miList02)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 4 5 6
## 
## [[4]]
## [1] "a"   "b"   "c"   "d"   "eee" "f"   "g"   "h"   "i"
miList02[[3]] <- miList02[[3]] * 10 - 1
print(miList02)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1] 39 49 59
## 
## [[4]]
## [1] "a"   "b"   "c"   "d"   "eee" "f"   "g"   "h"   "i"
miList02[[3]][2] <- miList02[[1]] * 100
print(miList02)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1]   39 1200   59
## 
## [[4]]
## [1] "a"   "b"   "c"   "d"   "eee" "f"   "g"   "h"   "i"
print(bddInsect)
## $Snonagrioides
## $Snonagrioides$site01
## [1] 12  5  8  7
## 
## $Snonagrioides$site02
## [1]  5 23  4 41
## 
## $Snonagrioides$site03
## [1] 12  0  0  0
## 
## 
## $Onubilalis
## $Onubilalis$site01
## [1] 12  1  2  3
## 
## $Onubilalis$site02
## [1] 0 0 0 1
## 
## $Onubilalis$site03
## [1] 1 1 2 3
bddInsect[['Snonagrioides']][['site02']] <- c(2, 4, 6, 8)
print(bddInsect)
## $Snonagrioides
## $Snonagrioides$site01
## [1] 12  5  8  7
## 
## $Snonagrioides$site02
## [1] 2 4 6 8
## 
## $Snonagrioides$site03
## [1] 12  0  0  0
## 
## 
## $Onubilalis
## $Onubilalis$site01
## [1] 12  1  2  3
## 
## $Onubilalis$site02
## [1] 0 0 0 1
## 
## $Onubilalis$site03
## [1] 1 1 2 3

Para combinar dos list, simplemente usamos la función c() que hemos usado para crear un vector.

miList0203 <- c(miList02, miList03)
print(miList0203)
## [[1]]
## [1] 12
## 
## [[2]]
## [1] "qwerty"
## 
## [[3]]
## [1]   39 1200   59
## 
## [[4]]
## [1] "a"   "b"   "c"   "d"   "eee" "f"   "g"   "h"   "i"  
## 
## [[5]]
## [1] 5
## 
## [[6]]
## [1] "qwerty"
## 
## [[7]]
## [[7]][[1]]
## [1] 4 5 6
## 
## [[7]][[2]]
## [1] "a" "b" "c"

Un objeto de tipo list se puede transformar en vector con la función unlist() si el formato de los elementos de la lista lo permite (un vector solo puede contener elementos del mismo tipo).

miList05 <- list("a", c("b", "c"), "d")
print(miList05)
## [[1]]
## [1] "a"
## 
## [[2]]
## [1] "b" "c"
## 
## [[3]]
## [1] "d"
miVec24 <- unlist(miList05)
print(miVec24)
## [1] "a" "b" "c" "d"
miList06 <- list(c(1, 2, 3), c(4, 5, 6, 7), 8, 9, c(10, 11))
print(miList06)
## [[1]]
## [1] 1 2 3
## 
## [[2]]
## [1] 4 5 6 7
## 
## [[3]]
## [1] 8
## 
## [[4]]
## [1] 9
## 
## [[5]]
## [1] 10 11
miVec25 <- unlist(miList06)
print(miVec25)
##  [1]  1  2  3  4  5  6  7  8  9 10 11

Para agregar un elemento a una list, podemos usar la función c() o los corchetes dobles [[ ]].

print(miList05)
## [[1]]
## [1] "a"
## 
## [[2]]
## [1] "b" "c"
## 
## [[3]]
## [1] "d"
miList05 <- c(miList05, "e")
print(miList05)
## [[1]]
## [1] "a"
## 
## [[2]]
## [1] "b" "c"
## 
## [[3]]
## [1] "d"
## 
## [[4]]
## [1] "e"
miList05[[5]] <- c("fgh", "ijk")
print(miList05)
## [[1]]
## [1] "a"
## 
## [[2]]
## [1] "b" "c"
## 
## [[3]]
## [1] "d"
## 
## [[4]]
## [1] "e"
## 
## [[5]]
## [1] "fgh" "ijk"

Para eliminar un elemento de una list, la técnica más rápida es establecer NULL en el elemento que deseamos eliminar.

print(miList05)
## [[1]]
## [1] "a"
## 
## [[2]]
## [1] "b" "c"
## 
## [[3]]
## [1] "d"
## 
## [[4]]
## [1] "e"
## 
## [[5]]
## [1] "fgh" "ijk"
miList05[[2]] <- NULL
print(miList05)
## [[1]]
## [1] "a"
## 
## [[2]]
## [1] "d"
## 
## [[3]]
## [1] "e"
## 
## [[4]]
## [1] "fgh" "ijk"

6.3 El contenedor data.frame

El contenedor data.frame se puede comparar a una tabla. Este es en realidad un caso especial de list donde todos los elementos de la list tienen el mismo tamaño.

6.3.1 Crear un data.frame

Para crear un data.frame usamos la función data.frame() que toma como argumentos los elementos de la tabla que queremos crear. Los elementos son del tipo vector y son todos del mismo tamaño. Podemos dar un nombre a cada columna (vector) de nuestra tabla (data.frame).

# crear un data.frame 
miDf01 <- data.frame(
  numbers = c(1, 2, 3, 4), 
  logicals = c(TRUE, TRUE, FALSE, TRUE), 
  characters = c("a", "b", "c", "d")
)
print(miDf01)
##   numbers logicals characters
## 1       1     TRUE          a
## 2       2     TRUE          b
## 3       3    FALSE          c
## 4       4     TRUE          d
# crear vectores, y el data.frame
numbers <- c(1, 2, 3, 4)
logicals <- c(TRUE, TRUE, FALSE, TRUE)
characters <- c("a", "b", "c", "d")
miDf01 <- data.frame(numbers, logicals, characters)
print(miDf01)
##   numbers logicals characters
## 1       1     TRUE          a
## 2       2     TRUE          b
## 3       3    FALSE          c
## 4       4     TRUE          d

6.3.2 Acceder a los elementos de un data.frame

El acceso a los diferentes valores de un data.frame se puede hacer de la misma manera que para un contenedor de tipo list.

print(miDf01$numbers) # vector
## [1] 1 2 3 4
print(miDf01[[1]]) # vector
## [1] 1 2 3 4
print(miDf01[1]) # list
##   numbers
## 1       1
## 2       2
## 3       3
## 4       4
print(miDf01["numbers"]) # list
##   numbers
## 1       1
## 2       2
## 3       3
## 4       4
print(miDf01[["numbers"]]) # vector
## [1] 1 2 3 4

También podemos usar otra forma que consiste en especificar la línea o las líneas seguidas de una coma (con un espacio después de la coma), y luego la columna o columnas entre corchetes. Si se omite la información de línea o columna, R mostrará todas las líneas o columnas. Nuevamente podemos usar el número correspondiente a un elemento o el nombre del elemento que queremos seleccionar.

myRow <- 2
myCol <- 1
print(miDf01[myRow, myCol])
## [1] 2
print(miDf01[myRow, ])
##   numbers logicals characters
## 2       2     TRUE          b
print(miDf01[, myCol])
## [1] 1 2 3 4
myCol <- "numbers"
print(miDf01[, myCol])
## [1] 1 2 3 4

Es posible seleccionar múltiples líneas o columnas.

print(miDf01[, c(1, 2)])
##   numbers logicals
## 1       1     TRUE
## 2       2     TRUE
## 3       3    FALSE
## 4       4     TRUE
print(miDf01[c(2, 1), ])
##   numbers logicals characters
## 2       2     TRUE          b
## 1       1     TRUE          a

Como cada columna está en formato vector, también podemos hacer una selección que depende del contenido con operadores de comparación y operadores lógicos.

miDfSub01 <- miDf01[miDf01$numbers > 2, ]
print(miDfSub01)
##   numbers logicals characters
## 3       3    FALSE          c
## 4       4     TRUE          d
miDfSub02 <- miDf01[(miDf01$logicals == TRUE) & (miDf01$numbers < 2), ]
print(miDfSub02)
##   numbers logicals characters
## 1       1     TRUE          a
miDfSub03 <- miDf01[(miDf01$numbers %% 2) == 0, ]
print(miDfSub03)
##   numbers logicals characters
## 2       2     TRUE          b
## 4       4     TRUE          d
miDfSub04 <- miDf01[((miDf01$numbers %% 2) == 0) | (miDf01$logicals == TRUE), ]
print(miDfSub04)
##   numbers logicals characters
## 1       1     TRUE          a
## 2       2     TRUE          b
## 4       4     TRUE          d

6.3.3 Modificar un data.frame

Para agregar un elemento a un data.frame, procedemos como para un contenedor de tipo list. Es necesario asegurarse de que el nuevo elemento sea del mismo tamaño que los otros elementos de nuestro data.frame. Por defecto, un nuevo elemento en data.frame toma el nombre de la letra V seguido del número de la columna. Podemos cambiar los nombres de las columnas con la función colnames(). Podemos nombrar las líneas con la función rownames().

newVec <- c(4, 5, 6, 7)
miDf01[[4]] <- newVec
print(miDf01)
##   numbers logicals characters V4
## 1       1     TRUE          a  4
## 2       2     TRUE          b  5
## 3       3    FALSE          c  6
## 4       4     TRUE          d  7
print(colnames(miDf01))
## [1] "numbers"    "logicals"   "characters" "V4"
colnames(miDf01)[4] <- "newVec"
print(miDf01)
##   numbers logicals characters newVec
## 1       1     TRUE          a      4
## 2       2     TRUE          b      5
## 3       3    FALSE          c      6
## 4       4     TRUE          d      7
print(rownames(miDf01))
## [1] "1" "2" "3" "4"
rownames(miDf01) <- c("row1", "row2", "row3", "row4")
print(miDf01)
##      numbers logicals characters newVec
## row1       1     TRUE          a      4
## row2       2     TRUE          b      5
## row3       3    FALSE          c      6
## row4       4     TRUE          d      7
newVec2 <- c(40, 50, 60, 70)
miDf01$newVec2 <- newVec2
print(miDf01)
##      numbers logicals characters newVec newVec2
## row1       1     TRUE          a      4      40
## row2       2     TRUE          b      5      50
## row3       3    FALSE          c      6      60
## row4       4     TRUE          d      7      70

Como el contenedor de tipo data.frame es un caso especial de list, la selección y modificación se realiza como un contenedor de tipo list. Dado que los elementos de un data.frame son del tipo vector, la selección y la modificación de los elementos de un data.frame se hace como para un contenedor vector.

miDf01$newVec2 <- miDf01$newVec2 * 2
print(miDf01)
##      numbers logicals characters newVec newVec2
## row1       1     TRUE          a      4      80
## row2       2     TRUE          b      5     100
## row3       3    FALSE          c      6     120
## row4       4     TRUE          d      7     140
miDf01$newVec2 + miDf01$newVec
## [1]  84 105 126 147
miDf01$newVec2[2] <- 0
print(miDf01)
##      numbers logicals characters newVec newVec2
## row1       1     TRUE          a      4      80
## row2       2     TRUE          b      5       0
## row3       3    FALSE          c      6     120
## row4       4     TRUE          d      7     140

Un vector se puede transformar en data.frame con la función as.data.frame().

print(newVec2)
## [1] 40 50 60 70
print(as.data.frame(newVec2))
##   newVec2
## 1      40
## 2      50
## 3      60
## 4      70
is.data.frame(newVec2)
## [1] FALSE
is.data.frame(as.data.frame(newVec2))
## [1] TRUE

6.4 El contenedor matrix

El contenedor matrix se puede ver como un vector de dos dimensiones: líneas y columnas. Corresponde a una matriz en matemáticas, y puede contener solo un tipo de datos (logical, numeric, character, …).

6.4.1 Crear una matrix

Para crear una matrix primero creamos un vector, luego especificamos el número deseado de líneas y columnas en la función matrix().

vecForMatrix <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
miMat <- matrix(vecForMatrix, nrow = 3, ncol = 4)
print(miMat)
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12

No tenemos que especificar el número de líneas nrow y el número de columnas ncol. Si usamos uno u otro de estos argumentos, R calculará automáticamente el número correspondiente.

miMat <- matrix(vecForMatrix, nrow = 3)
print(miMat)
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
miMat <- matrix(vecForMatrix, ncol = 4)
print(miMat)
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12

Observamos que los diferentes elementos del vector inicial aparecen por columna. Si queremos llenar la matrix empezando por línea, entonces tenemos que dar como valor TRUE al argumento byrow.

miMat <- matrix(vecForMatrix, nrow = 3, byrow = TRUE)
print(miMat)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12

También podemos dar un nombre a las líneas y columnas de nuestra matrix cuando se crea con el argumento dimnames que toma como valor una list de dos elementos: el nombre de las líneas y luego el nombre de las columnas. También podemos cambiar el nombre de las líneas y columnas a posteriori con las funciones rownames() y colnames().

miMat <- matrix(
  vecForMatrix, 
  nrow = 3, 
  byrow = TRUE, 
  dimnames = list(c("r1", "r2", "r3"), c("c1", "c2", "c3", "c4"))
)
print(miMat)
##    c1 c2 c3 c4
## r1  1  2  3  4
## r2  5  6  7  8
## r3  9 10 11 12
colnames(miMat) <- c("col1", "col2", "col3", "col4")
rownames(miMat) <- c("row1", "row2", "row3")
print(miMat)
##      col1 col2 col3 col4
## row1    1    2    3    4
## row2    5    6    7    8
## row3    9   10   11   12

Es posible crear una matrix desde un data.frame con la función as.matrix(). Tenemos que verificar que nuestra data.frame contenga solo elementos del mismo tipo (por ejemplo, elementos de tipo numeric).

vecForMat01 <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
vecForMat02 <- vecForMat01 * 10
vecForMat03 <- vecForMat01 / 10
dfForMat <- data.frame(vecForMat01, vecForMat02, vecForMat03)
print(dfForMat)
##    vecForMat01 vecForMat02 vecForMat03
## 1            1          10         0.1
## 2            2          20         0.2
## 3            3          30         0.3
## 4            4          40         0.4
## 5            5          50         0.5
## 6            6          60         0.6
## 7            7          70         0.7
## 8            8          80         0.8
## 9            9          90         0.9
## 10          10         100         1.0
## 11          11         110         1.1
## 12          12         120         1.2
is.matrix(dfForMat)
## [1] FALSE
as.matrix(dfForMat)
##       vecForMat01 vecForMat02 vecForMat03
##  [1,]           1          10         0.1
##  [2,]           2          20         0.2
##  [3,]           3          30         0.3
##  [4,]           4          40         0.4
##  [5,]           5          50         0.5
##  [6,]           6          60         0.6
##  [7,]           7          70         0.7
##  [8,]           8          80         0.8
##  [9,]           9          90         0.9
## [10,]          10         100         1.0
## [11,]          11         110         1.1
## [12,]          12         120         1.2
is.matrix(as.matrix(dfForMat))
## [1] TRUE

También podemos crear una matrix desde un vector con la función as.matrix() (matriz de una sola columna).

as.matrix(vecForMat01)
##       [,1]
##  [1,]    1
##  [2,]    2
##  [3,]    3
##  [4,]    4
##  [5,]    5
##  [6,]    6
##  [7,]    7
##  [8,]    8
##  [9,]    9
## [10,]   10
## [11,]   11
## [12,]   12

6.4.2 Manipular y hacer operaciones en una matrix

Todas las operaciones término a término son posibles con una matrix.

# operaciones término a término
miMat01 <- matrix(vecForMat01, ncol = 3)
miVecOp <- c(1, 10, 100, 1000)
miMat01 * miVecOp
##      [,1] [,2]  [,3]
## [1,]    1    5     9
## [2,]   20   60   100
## [3,]  300  700  1100
## [4,] 4000 8000 12000
miMat01 + miVecOp
##      [,1] [,2] [,3]
## [1,]    2    6   10
## [2,]   12   16   20
## [3,]  103  107  111
## [4,] 1004 1008 1012
miMat01 / miMat01
##      [,1] [,2] [,3]
## [1,]    1    1    1
## [2,]    1    1    1
## [3,]    1    1    1
## [4,]    1    1    1
miMat01 - 10
##      [,1] [,2] [,3]
## [1,]   -9   -5   -1
## [2,]   -8   -4    0
## [3,]   -7   -3    1
## [4,]   -6   -2    2

Para realizar operaciones algebraicas, podemos usar la función %*%.

# operaciones algebraicas
miVecConf <- c(1, 10, 100)
miMat01 %*% miVecConf
##      [,1]
## [1,]  951
## [2,] 1062
## [3,] 1173
## [4,] 1284
miMat02 <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), ncol = 3)
print(miMat02)
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
miMat02 %*% miMat02
##      [,1] [,2] [,3]
## [1,]   30   66  102
## [2,]   36   81  126
## [3,]   42   96  150

La diagonal de una matrix se puede obtener con la función diag() y el determinante de una matrix con la función det().

print(miMat02)
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
diag(miMat02)
## [1] 1 5 9
det(miMat02)
## [1] 0

Suele ser útil poder hacer una transposición de matrix (columnas en líneas o líneas en columnas). Para eso, están las funciones aperm() o t(). la función t() es más genérica y también funciona con data.frame.

aperm(miMat01)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
t(miMat01)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12

6.4.3 Acceder a los elementos de una matrix

Tal como hemos hecho con los data.frame, podemos acceder a los elementos de una matrix especificando un número de línea y un número de columna entre corchetes simples [ ], y separados por una coma. Si i es el número de línea y j es el número de columna, entonces miMat01[i, j] devuelve el elemento en la línea i y en la columnaj. miMat01[i,] devuelve todos los elementos de la línea i, y miMat01[, j] todos los elementos de la columna j. Múltiples selecciones son posibles. También podemos acceder a un elemento de acuerdo con su posición en la matrix entre corchetes simples [ ] contando por columna y luego por línea. En nuestro ejemplo, el valor del décimo elemento es 10.

i <- 2
j <- 1
print(miMat01[i, j])
## [1] 2
print(miMat01[i, ])
## [1]  2  6 10
print(miMat01[, j])
## [1] 1 2 3 4
print(miMat01[c(1, 2), c(2, 3)])
##      [,1] [,2]
## [1,]    5    9
## [2,]    6   10
print(miMat01[10])
## [1] 10

6.5 El contenedor array

El contenedor array es una generalización del contenedor de tipo matrix. Donde el tipo matrix tiene dos dimensiones (líneas y columnas), el tipo array tiene un número indefinido de dimensiones. Podemos saber el número de dimensiones de un array (y por lo tanto una matrix) con la función dim().

dim(miMat01)
## [1] 4 3

6.5.1 Crear un array

La creación de una array es similar a la de una matrix con una dimensión extra.

miVecArr <- c(1, 2, 3, 4, 5, 6, 7, 8, 9)
miArray <- array(miVecArr, dim = c(3, 3, 2))
print(miArray)
## , , 1
## 
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
## 
## , , 2
## 
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
dim(miArray)
## [1] 3 3 2
is.array(miArray)
## [1] TRUE
miVecArr02 <- 10 * miVecArr
miArray02 <- array(c(miVecArr, miVecArr02), dim = c(3, 3, 2))
print(miArray02)
## , , 1
## 
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
## 
## , , 2
## 
##      [,1] [,2] [,3]
## [1,]   10   40   70
## [2,]   20   50   80
## [3,]   30   60   90
dim(miArray02)
## [1] 3 3 2
is.array(miArray02)
## [1] TRUE

Podemos dar nombres a líneas y columnas, pero también a elementos.

miArray02 <- array(
  c(miVecArr, miVecArr02), 
  dim = c(3, 3, 2), 
  dimnames = list(
    c("r1", "r2", "r3"), 
    c("c1", "c2", "c3"), 
    c("matrix1", "matrix2")
  )
)
print(miArray02)
## , , matrix1
## 
##    c1 c2 c3
## r1  1  4  7
## r2  2  5  8
## r3  3  6  9
## 
## , , matrix2
## 
##    c1 c2 c3
## r1 10 40 70
## r2 20 50 80
## r3 30 60 90

6.5.2 Manipular un array

La manipulación de un array se hace de la misma manera que para una matrix. Para acceder a los diferentes elementos de un array, simplemente hay que especificar la línea i, la columna j, y la matrix k.

i <- 2
j <- 1
k <- 1
print(miArray02[i, j, k])
## [1] 2
print(miArray02[, j, k])
## r1 r2 r3 
##  1  2  3
print(miArray02[i, , k])
## c1 c2 c3 
##  2  5  8
print(miArray02[i, j, ])
## matrix1 matrix2 
##       2      20

6.6 Conclusión

Felicitaciones! Ahora conocemos los principales tipos de objetos que usaremos con R. Un objeto se caracteriza por sus atributos:

  • el tipo de contenedor (vector, data.frame, matrix, array)
  • el tipo de contenido de cada elemento (numeric, logical, character, …)
  • el valor de cada uno de los elementos (5, “qwerty”, TRUE, …)

Todos estos objetos se almacenan temporalmente en el entorno global de R (en la memoria RAM de nuestra computadora). El siguiente capítulo tratará las funciones y resaltará uno de los aspectos que hace que R sea tan poderoso para analizar y administrar nuestros datos.