Capítulo 9 Listas

Hasta ahora hemos hecho un resumen de los elementos básicos de R:

  • vectores: colección de elementos de igual tipo. Recuerda que un elemento individual, por ejemplo x <- 1, es en realidad un vector (de longitud 1). Pueden ser números, caracteres, fechas o valores lógicos, entre otros.

  • matrices: colección BIDIMENSIONAL de elementos de igual tipo e igual longitud.

  • data.frame: colección BIDIMENSIONAL de elementos de igual longitud pero de cualquier tipo, lo más parecido a lo que conocemos como una tabla en Excel.

Además hemos visto una serie de herramientas para tratar esos datos:

  • estructuras condicionalesif-else
  • bucles para repetir trozos de código.
  • organizar el código en proyectos para facilitar nuestro trabajo
  • importar/exportar datos

Con todos estos ingredientes estamos preparados/as para ver el que probablemente sea el tipo de dato más importante en R: las listas.

Las listas son colecciones de variables de diferente tipo pero que pueden ser también de diferente longitud, con estructuras totalmente heterógeneas, de ahí que sea el formato de salida de muchísimas funciones de R que te devuelven a la vez un cadena de texto, un vector de números o una tabla, todo guardado en la misma variable (incluso una lista puede tener dentro a su vez otra lista).

Vamos a crear nuestra primera lista con tres elementos: el nombre de nuestros padres/madres, nuestro lugar de nacimiento y edades de nuestros hermanos.

variable_1 <- c("Paloma", "Gregorio")
variable_2 <- "Madrid"
variable_3 <- c(25, 30, 26)

lista <- list("progenitores" = variable_1,
              "lugar_nacimiento" = variable_2,
              "edades_hermanos" = variable_3)
lista
## $progenitores
## [1] "Paloma"   "Gregorio"
## 
## $lugar_nacimiento
## [1] "Madrid"
## 
## $edades_hermanos
## [1] 25 30 26
length(lista)
## [1] 3

Si observas el objeto que hemos definido como lista, la longitud del objeto es de 3: tenemos guardados tres elementos

  • un vector de caracteres (de longitud 2)
  • un caracter (vector de longitud 1)
  • un vector de números (de longitud 3)

Tenemos guardados elementos de distinto tipo (algo que ya podíamos con los data.frame pero de longitudes dispares).

dim(lista) # devolverá NULL al no tener dos dimensiones
## NULL
length(lista)
## [1] 3
class(lista) # de tipo lista
## [1] "list"

Para acceder a un elemento de la lista tenemos dos opciones:

  • Acceder por índice: con el operador [[i]] accedemos al elemento i-ésimo de la lista.

  • Acceder por nombre: con el operador $nombre_elemento accedemos al elemento por su nombre

# Accedemos por índice
lista[[1]]
## [1] "Paloma"   "Gregorio"
# Accedemos por nombre
lista$progenitores
## [1] "Paloma"   "Gregorio"

Dada su heterogeneidad y flexibilidad, para acceder a un elemento particular, las listas tienen una forma peculiar de acceder (con el corchete doble, en contraposición con el corchete simple que nos permite acceder a varios elementos a la vez)

# Varios elementos
lista[1:2]
## $progenitores
## [1] "Paloma"   "Gregorio"
## 
## $lugar_nacimiento
## [1] "Madrid"

Las listas nos dan tanta flexibilidad que es el formato de dato natural para guardar datos que no están estructurados, como pueden ser los datos almacenados en el registro de una persona

# Fecha de nacimiento
fecha_nacimiento <- as.Date("1989-09-10")

# Notas de asignaturas en primer y segundo parcial
notas <- data.frame("biología" = c(5, 7), "física" = c(4, 5),
                    "matemáticas" = c(8, 9.5))
row.names(notas) <- # Nombre a las filas
  c("primer_parcial", "segundo_parcial")

# Números de teléfono
tlf <- c("914719567", "617920765", "716505013")

# Nombres
padres <- c("Juan", "Julia")

# Guardamos TODO en una lista (con nombres de cada elemento)
datos <- list("nacimiento" = fecha_nacimiento,
              "notas_insti" = notas, "teléfonos" = tlf,
              "nombre_padres" = padres)
datos
## $nacimiento
## [1] "1989-09-10"
## 
## $notas_insti
##                 biología física matemáticas
## primer_parcial         5      4         8.0
## segundo_parcial        7      5         9.5
## 
## $teléfonos
## [1] "914719567" "617920765" "716505013"
## 
## $nombre_padres
## [1] "Juan"  "Julia"
names(datos)
## [1] "nacimiento"    "notas_insti"   "teléfonos"     "nombre_padres"
length(datos)
## [1] 4

Hemos creado ahora una lista algo más compleja de 4 elementos:

  • nacimiento: una fecha.
  • notas_insti: un data.frame.
  • teléfonos: vector de números.
  • nombre_padres: vector de texto.
datos[[1]]
## [1] "1989-09-10"
datos$nacimiento
## [1] "1989-09-10"
datos[[2]]
##                 biología física matemáticas
## primer_parcial         5      4         8.0
## segundo_parcial        7      5         9.5
datos$notas_insti
##                 biología física matemáticas
## primer_parcial         5      4         8.0
## segundo_parcial        7      5         9.5

Como hemos comentado, también podemos aplicar la recursividad y hacer listas con otras listas dentro, de forma que para acceder a cada nivel deberemos usar el operador [[]].

lista_de_listas <- list("lista_1" = datos[3:4], "lista_2" = datos[1:2])
names(lista_de_listas) # Nombres de los elementos del primer nivel
## [1] "lista_1" "lista_2"
names(lista_de_listas[[1]]) # Nombres de los elementos guardados en el primer elemento, que es a su vez una lista
## [1] "teléfonos"     "nombre_padres"
lista_de_listas[[1]][[1]] # Elemento 1 de la lista guardada como elemento 1 de la lista superior
## [1] "914719567" "617920765" "716505013"

Un ejemplo de la utilidad de las listas la tenemos en los archivos R.Data de vacunas que hemos cargado en nuestro script. Con names(panel_vacunas) podemos ver que elementos contiene en el primer nivel.

names(panel_vacunas)
##  [1] "AN"   "AR"   "AS"   "IB"   "CN"   "CB"   "CL"   "CM"   "CT"   "VC"  
## [11] "EX"   "GA"   "RI"   "MD"   "MC"   "NC"   "PV"   "CE"   "ML"   "FFAA"
## [21] "ES"

Cada elemento de la lista es un data.frame de una comunidad autonónoma, que a su vez contiene una serie de variables (columnas) para cada una de las fechas (filas): ¡nos permite guardar «datos n-imensionales»!.

class(panel_vacunas$ES)
## [1] "data.frame"
names(panel_vacunas$ES)
##  [1] "fechas"                              
##  [2] "ISO"                                 
##  [3] "poblacion"                           
##  [4] "porc_pobl_total"                     
##  [5] "poblacion_mayor_16a"                 
##  [6] "porc_pobl_total_mayor_16a"           
##  [7] "dosis_entrega_pfizer"                
##  [8] "dosis_entrega_astra"                 
##  [9] "dosis_entrega_moderna"               
## [10] "dosis_entrega_janssen"               
## [11] "dosis_entrega"                       
## [12] "dosis_entrega_100hab"                
## [13] "porc_entregadas_sobre_total"         
## [14] "dosis_diarias_entrega_pfizer"        
## [15] "dosis_diarias_entrega_astra"         
## [16] "dosis_diarias_entrega_moderna"       
## [17] "dosis_diarias_entrega"               
## [18] "dosis_7D_entrega_pfizer"             
## [19] "dosis_7D_entrega_astra"              
## [20] "dosis_7D_entrega_moderna"            
## [21] "dosis_7D_entrega"                    
## [22] "dosis_7D_entrega_100hab"             
## [23] "dosis_admin"                         
## [24] "dosis_primera"                       
## [25] "dosis_pauta_completa"                
## [26] "dosis_admin_100hab"                  
## [27] "porc_admin_sobre_ccaa"               
## [28] "porc_admin_vs_total"                 
## [29] "dosis_diarias_admin"                 
## [30] "dosis_diarias_admin_100hab"          
## [31] "crec_diario_dosis_admin"             
## [32] "dosis_diarias_primera"               
## [33] "dosis_diarias_segunda"               
## [34] "dosis_7D_admin"                      
## [35] "dosis_7D_admin_100hab"               
## [36] "crec_7D_dosis_admin"                 
## [37] "porc_admin_vs_total_7D"              
## [38] "personas_vacunadas"                  
## [39] "personas_pauta_completa"             
## [40] "personas_1dosis"                     
## [41] "porc_personas_vacunadas"             
## [42] "porc_personas_pauta_completa"        
## [43] "porc_personas_vacunadas_16a"         
## [44] "porc_personas_pauta_completa_16a"    
## [45] "personas_vacunadas_diarias"          
## [46] "personas_pauta_completa_diarias"     
## [47] "porc_personas_vacunadas_diarias"     
## [48] "porc_personas_pauta_completa_diarias"
## [49] "personas_vacunadas_7D"               
## [50] "personas_pauta_completa_7D"          
## [51] "porc_personas_vacunadas_7D"          
## [52] "porc_personas_pauta_completa_7D"     
## [53] "porc_personas_vacunadas_16a_7D"      
## [54] "porc_personas_pauta_completa_16a_7D" 
## [55] "crec_diario_personas_vacunadas"      
## [56] "crec_7D_personas_vacunadas"          
## [57] "crec_diario_personas_pauta_completa" 
## [58] "crec_7D_personas_pauta_completa"     
## [59] "desv_porc_admin_vs_total"            
## [60] "desv_dosis_entrega"                  
## [61] "desv_porc_personas_vacunadas"        
## [62] "desv_porc_personas_pauta_completa"   
## [63] "fecha_30vacunados_ritmo7D"           
## [64] "fecha_50vacunados_ritmo7D"           
## [65] "fecha_70vacunados_ritmo7D"           
## [66] "fecha_30inmunizados_ritmo7D"         
## [67] "fecha_50inmunizados_ritmo7D"         
## [68] "fecha_70inmunizados_ritmo7D"         
## [69] "fecha_30inmunizados_16a_ritmo7D"     
## [70] "fecha_50inmunizados_16a_ritmo7D"     
## [71] "fecha_70inmunizados_16a_ritmo7D"
head(panel_vacunas$ES[, 1:5])
##        fechas ISO poblacion porc_pobl_total poblacion_mayor_16a
## 1  2021-01-05  ES  47450795             100            40129822
## 2  2021-01-06  ES  47450795             100            40129822
## 21 2021-01-07  ES  47450795             100            40129822
## 3  2021-01-08  ES  47450795             100            40129822
## 11 2021-01-09  ES  47450795             100            40129822
## 12 2021-01-10  ES  47450795             100            40129822

El acceso lo podemos realizar por orden que ocupa en la lista pero también de forma intuitiva con $ y el código ISO de la comunidad autónoma. Lo mismo podemos hacer con el panel de fechas, donde ahora cada elemento de la lista es una fecha, y en cada elemento de ella, está guardada la información de cada variable (columna) y cada comunidad (fila).

names(panel_vacunas_fecha)
##   [1] "2021-01-05" "2021-01-06" "2021-01-07" "2021-01-08" "2021-01-09"
##   [6] "2021-01-10" "2021-01-11" "2021-01-12" "2021-01-13" "2021-01-14"
##  [11] "2021-01-15" "2021-01-16" "2021-01-17" "2021-01-18" "2021-01-19"
##  [16] "2021-01-20" "2021-01-21" "2021-01-22" "2021-01-23" "2021-01-24"
##  [21] "2021-01-25" "2021-01-26" "2021-01-27" "2021-01-28" "2021-01-29"
##  [26] "2021-01-30" "2021-01-31" "2021-02-01" "2021-02-02" "2021-02-03"
##  [31] "2021-02-04" "2021-02-05" "2021-02-06" "2021-02-07" "2021-02-08"
##  [36] "2021-02-09" "2021-02-10" "2021-02-11" "2021-02-12" "2021-02-13"
##  [41] "2021-02-14" "2021-02-15" "2021-02-16" "2021-02-17" "2021-02-18"
##  [46] "2021-02-19" "2021-02-20" "2021-02-21" "2021-02-22" "2021-02-23"
##  [51] "2021-02-24" "2021-02-25" "2021-02-26" "2021-02-27" "2021-02-28"
##  [56] "2021-03-01" "2021-03-02" "2021-03-03" "2021-03-04" "2021-03-05"
##  [61] "2021-03-06" "2021-03-07" "2021-03-08" "2021-03-09" "2021-03-10"
##  [66] "2021-03-11" "2021-03-12" "2021-03-13" "2021-03-14" "2021-03-15"
##  [71] "2021-03-16" "2021-03-17" "2021-03-18" "2021-03-19" "2021-03-20"
##  [76] "2021-03-21" "2021-03-22" "2021-03-23" "2021-03-24" "2021-03-25"
##  [81] "2021-03-26" "2021-03-27" "2021-03-28" "2021-03-29" "2021-03-30"
##  [86] "2021-03-31" "2021-04-01" "2021-04-02" "2021-04-03" "2021-04-04"
##  [91] "2021-04-05" "2021-04-06" "2021-04-07" "2021-04-08" "2021-04-09"
##  [96] "2021-04-10" "2021-04-11" "2021-04-12" "2021-04-13" "2021-04-14"
## [101] "2021-04-15" "2021-04-16" "2021-04-17" "2021-04-18" "2021-04-19"
## [106] "2021-04-20" "2021-04-21" "2021-04-22" "2021-04-23"
names(panel_vacunas_fecha$`2021-04-23`)
##  [1] "ccaa"                                
##  [2] "fechas"                              
##  [3] "ISO"                                 
##  [4] "poblacion"                           
##  [5] "porc_pobl_total"                     
##  [6] "poblacion_mayor_16a"                 
##  [7] "porc_pobl_total_mayor_16a"           
##  [8] "dosis_entrega_pfizer"                
##  [9] "dosis_entrega_astra"                 
## [10] "dosis_entrega_moderna"               
## [11] "dosis_entrega_janssen"               
## [12] "dosis_entrega"                       
## [13] "dosis_entrega_100hab"                
## [14] "porc_entregadas_sobre_total"         
## [15] "dosis_diarias_entrega_pfizer"        
## [16] "dosis_diarias_entrega_astra"         
## [17] "dosis_diarias_entrega_moderna"       
## [18] "dosis_diarias_entrega"               
## [19] "dosis_7D_entrega_pfizer"             
## [20] "dosis_7D_entrega_astra"              
## [21] "dosis_7D_entrega_moderna"            
## [22] "dosis_7D_entrega"                    
## [23] "dosis_7D_entrega_100hab"             
## [24] "dosis_admin"                         
## [25] "dosis_primera"                       
## [26] "dosis_pauta_completa"                
## [27] "dosis_admin_100hab"                  
## [28] "porc_admin_sobre_ccaa"               
## [29] "porc_admin_vs_total"                 
## [30] "dosis_diarias_admin"                 
## [31] "dosis_diarias_admin_100hab"          
## [32] "crec_diario_dosis_admin"             
## [33] "dosis_diarias_primera"               
## [34] "dosis_diarias_segunda"               
## [35] "dosis_7D_admin"                      
## [36] "dosis_7D_admin_100hab"               
## [37] "crec_7D_dosis_admin"                 
## [38] "porc_admin_vs_total_7D"              
## [39] "personas_vacunadas"                  
## [40] "personas_pauta_completa"             
## [41] "personas_1dosis"                     
## [42] "porc_personas_vacunadas"             
## [43] "porc_personas_pauta_completa"        
## [44] "porc_personas_vacunadas_16a"         
## [45] "porc_personas_pauta_completa_16a"    
## [46] "personas_vacunadas_diarias"          
## [47] "personas_pauta_completa_diarias"     
## [48] "porc_personas_vacunadas_diarias"     
## [49] "porc_personas_pauta_completa_diarias"
## [50] "personas_vacunadas_7D"               
## [51] "personas_pauta_completa_7D"          
## [52] "porc_personas_vacunadas_7D"          
## [53] "porc_personas_pauta_completa_7D"     
## [54] "porc_personas_vacunadas_16a_7D"      
## [55] "porc_personas_pauta_completa_16a_7D" 
## [56] "crec_diario_personas_vacunadas"      
## [57] "crec_7D_personas_vacunadas"          
## [58] "crec_diario_personas_pauta_completa" 
## [59] "crec_7D_personas_pauta_completa"     
## [60] "desv_porc_admin_vs_total"            
## [61] "desv_dosis_entrega"                  
## [62] "desv_porc_personas_vacunadas"        
## [63] "desv_porc_personas_pauta_completa"   
## [64] "fecha_30vacunados_ritmo7D"           
## [65] "fecha_50vacunados_ritmo7D"           
## [66] "fecha_70vacunados_ritmo7D"           
## [67] "fecha_30inmunizados_ritmo7D"         
## [68] "fecha_50inmunizados_ritmo7D"         
## [69] "fecha_70inmunizados_ritmo7D"         
## [70] "fecha_30inmunizados_16a_ritmo7D"     
## [71] "fecha_50inmunizados_16a_ritmo7D"     
## [72] "fecha_70inmunizados_16a_ritmo7D"
head(panel_vacunas_fecha$`2021-04-23`[, 1:7])
##     ccaa     fechas ISO poblacion porc_pobl_total poblacion_mayor_16a
## 77    AN 2021-04-23  AN   8464411          17.838             7062213
## 771   AR 2021-04-23  AR   1329391           2.802             1132764
## 772   AS 2021-04-23  AS   1018784           2.147              901209
## 773   IB 2021-04-23  IB   1171543           2.469              986279
## 774   CN 2021-04-23  CN   2175952           4.586             1871033
## 775   CB 2021-04-23  CB    582905           1.228              501384
##     porc_pobl_total_mayor_16a
## 77                     17.598
## 771                     2.823
## 772                     2.246
## 773                     2.458
## 774                     4.662
## 775                     1.249

 

WARNING: operaciones aritméticas con listas

Una lista no se puede vectorizar de forma inmediata, por lo cualquier operación aritmética aplicada a una lista dará error (para ello está disponible la función lapply(), cuyo uso corresponde a otros cursos más avanzados).

datos <- list("a" = 1:5, "b" = 10:20)
datos / 2
## Error in datos/2: argumento no-numérico para operador binario
lapply(datos, FUN = function(x) { x / 2})
## $a
## [1] 0.5 1.0 1.5 2.0 2.5
## 
## $b
##  [1]  5.0  5.5  6.0  6.5  7.0  7.5  8.0  8.5  9.0  9.5 10.0

 

9.1 📝 Ejercicios

Ejercicio 1: define una lista de 4 elementos de tipos distintos y accede al segundo de ellos (yo incluiré uno que sea un data.frame para que veas que en una lista cabe de todo).

  • Solución:
# Ejemplo: lista con texto, numérico, lógico y un data.frame
lista_ejemplo <- list("nombre" = "Javier", "cp" = 28019,
                      "soltero" = TRUE,
                      "notas" = data.frame("mates" = c(7.5, 8, 9),
                                           "lengua" = c(10, 5, 6),
                                           "gimnasia" = c(4, 8, 6)))
lista_ejemplo
## $nombre
## [1] "Javier"
## 
## $cp
## [1] 28019
## 
## $soltero
## [1] TRUE
## 
## $notas
##   mates lengua gimnasia
## 1   7.5     10        4
## 2   8.0      5        8
## 3   9.0      6        6
# Longitud
length(lista_ejemplo)
## [1] 4
# Accedemos al elemento dos
lista_ejemplo[[2]]
## [1] 28019

 

Ejercicio 2: accede a los elementos que ocupan los lugares 1 y 4 de la lista definida anteriormente.

  • Solución:
# Accedemos al 1 y al 4
lista_ejemplo[c(1, 4)]
## $nombre
## [1] "Javier"
## 
## $notas
##   mates lengua gimnasia
## 1   7.5     10        4
## 2   8.0      5        8
## 3   9.0      6        6

Otra opción es acceder con los nombres

# Accedemos al 1 y al 4
lista_ejemplo$nombre
## [1] "Javier"
lista_ejemplo$notas
##   mates lengua gimnasia
## 1   7.5     10        4
## 2   8.0      5        8
## 3   9.0      6        6
lista_ejemplo[c("nombre", "notas")]
## $nombre
## [1] "Javier"
## 
## $notas
##   mates lengua gimnasia
## 1   7.5     10        4
## 2   8.0      5        8
## 3   9.0      6        6

 

Ejercicio 3: define una lista de 4 elementos que contenga, en una sola variable, tu nombre, apellido, edad (como dato numérico) y si estás soltero/a.

  • Solución:
library(lubridate)
# Creamos lista: con lubridate calculamos la diferencia de años desde la fecha de nuestro nacimiento hasta hoy (sea cuando sea hoy)
lista_personal <- list("nombre" = "Javier",
                       "apellidos" = "Álvarez Liébana",
                       "edad" = time_length(interval(ymd("1989-09-10"), ymd(Sys.Date())), unit = "years"),
                       "soltero" = TRUE)
lista_personal
## $nombre
## [1] "Javier"
## 
## $apellidos
## [1] "Álvarez Liébana"
## 
## $edad
## [1] 32.28493
## 
## $soltero
## [1] TRUE
# Otra opción: la edad calculada con floor (quita decimales y se queda con la parte entera)
lista_personal <- list("nombre" = "Javier",
                       "apellidos" = "Álvarez Liébana",
                       "edad" = floor(time_length(interval(ymd("1989-09-10"), ymd(today())), unit = "years")),
                       "soltero" = TRUE)
lista_personal
## $nombre
## [1] "Javier"
## 
## $apellidos
## [1] "Álvarez Liébana"
## 
## $edad
## [1] 32
## 
## $soltero
## [1] TRUE