A lo largo de esta unidad hemos explorado las principales estructuras de datos que ofrece R, como vectores, matrices y listas, y aprendimos a manipularlas mediante operaciones tanto elementales como vectorizadas. En este último capítulo, reunimos algunas herramientas y conceptos adicionales que complementan y potencian el trabajo con estructuras de datos. Veremos, por un lado, funciones de la familia apply, que permiten realizar operaciones repetitivas de forma eficiente. También abordaremos formas de generar secuencias numéricas y de combinar estructuras como vectores, matrices y listas, operaciones fundamentales para construir y transformar datos. Por último, introduciremos los arreglos multidimensionales, una extensión natural de las matrices que permite trabajar con datos en más de dos dimensiones.
27.1 Familia de funciones apply
Como ya sabemos, una de las fortalezas del lenguaje R es la existencia de funciones que permiten aplicar operaciones de manera vectorizada o sobre estructuras de datos completas, evitando escribir estructuras de repetición explícitas como los bucles for. En particular, existe un conjunto de funciones conocidas como la familia apply que permiten resumir muchas tareas repetitivas con una sintaxis concisa, adaptándose a diferentes estructuras de datos.
27.1.1 Función apply
Supongamos que queremos encontrar el máximo valor en cada fila de una matriz. Podemos lograrlo de la siguiente forma. Creamos un vector maximos con lugar para guardar el máximo de cada fila. Luego, iteramos para recorrer cada fila de la matriz, buscar el máximo y guardarlo en el vector maximos:
maximos <-numeric(nrow(m))for (i in1:nrow(m)) { maximos[i] <-max(m[i, ])}maximos
[1] 20 13 17
En R existe una forma más práctica y eficiente de conseguir el mismo resultado:
apply(m, 1, max)
[1] 20 13 17
La función apply() sirve para aplicar una misma operación a cada fila o columna de una matriz. En el ejemplo anterior:
el primer argumento, m, es la matriz a analizar.
el segundo argumento, 1, indica que la operación se realizará fila por fila (para que se haga por columna, debemos indicar 2)
el tercer argumento, max, es el nombre de la función que se le aplica a cada fila.
De manera similar, podemos encontrar el mínimo valor de cada columna:
apply(m, 2, min)
[1] -2 -7 -8
27.1.2 Funciones lapply() y sapply()
lapply(X, FUN) aplica una función FUN a cada elemento de una lista o vector y devuelve siempre una lista como resultado:
lista1 <-list(a =1:5, b =6:10, c =11:15)
sapply() funciona igual que lapply(), pero intenta simplificar la salida: si todos los resultados son del mismo tipo y longitud, devuelve un vector o matriz en lugar de una lista:
sapply(lista1, mean)
a b c
3 8 13
Si la simplificación no es posible, el resultado será una lista, igual que con lapply().
En el caso de que la lista tenga elementos de distinto tipo, la función a aplicar debe ser admisible para cada uno de ellos, si no se generará un error:
lista2 <-list(w =c(-4.5, 12, 2.71),x =c("hola", "chau"),y =matrix(c(8, 11, 13, 16), nrow =2),z =TRUE)# Vemos el largo de cada elemento de la listalapply(lista2, length)
$w
[1] 3
$x
[1] 2
$y
[1] 4
$z
[1] 1
sapply(lista2, length)
w x y z
3 2 4 1
# Vemos la suma de cada elemento de la lista: no se puede con algunoslapply(lista2, sum)
Error in FUN(X[[i]], ...): invalid 'type' (character) of argument
sapply(lista2, sum)
Error in FUN(X[[i]], ...): invalid 'type' (character) of argument
27.1.3 Otras funciones
Dentro de la familia apply también se encuentran las funciones mapply() (versión multivariada de sapply(), que aplica una función a varios vectores o listas en paralelo), rapply() (aplica una función de forma recursiva sobre listas anidadas), tapply() (aplica una función a subconjuntos de datos) y vapply() (requiere que especifiquemos el tipo de salida esperado). No nos ocuparemos de usarlas en este curso.
Además de la familia apply, R ofrece otras herramientas muy potentes para realizar operaciones repetitivas de forma clara y expresiva. En particular, el ecosistema tidyverse introduce funciones como map(), map_dbl(), map_df() y otras variantes, que permiten aplicar funciones sobre listas y otros objetos de manera muy cómoda. Estas funciones combinan la versatilidad de lapply() y sapply() con una sintaxis más moderna y consistente, y ofrecen un control más explícito sobre los tipos de salida. Además, el tidyverse ofrece muchas opciones para realizar tareas repetitivas sobre conjuntos de datos. Si bien no forman parte de la base de R, son ampliamente utilizadas en la práctica y constituyen una alternativa muy recomendable para quienes ya trabajan con herramientas de dicho sistema de paquetes.
27.2 Generación de vectores con secuencias numéricas
A continuación mostramos cómo generar algunos vectores numéricos en R (algunos casos ya los estuvimos usando):
También es posible combinar o juntar listas para formar nuevas listas que contengan todos los elementos de las originales. Esto puede resultar útil cuando queremos unificar resultados parciales, construir estructuras complejas a partir de otras más simples, o agregar nuevos elementos a una lista existente. La forma más directa de combinar listas es usando también la función c():
lista3 <-list(a =1, b =2)lista4 <-list(c =3, d =4)lista_combinada <-c(lista3, lista4)lista_combinada
$a
[1] 1
$b
[1] 2
$c
[1] 3
$d
[1] 4
También es posible anidar listas dentro de otras listas. En el siguiente caso, lista_anidada es una lista de longitud 2, donde cada elemento es a su vez una lista:
Un arreglo multidimensional contiene más de dos dimensiones, es decir, requiere más de dos índices para identificar a cada uno de sus elementos. La representación matemática o visual ya no es tan sencilla como la de los vectores o matrices. Para interpretarlos o saber cuándo usarlos, pensamos que cada una de las dimensiones representa una característica de los elementos.
Por ejemplo, imaginemos que en un local comercial se quiere registrar cuántos clientes se atendieron en cada una de las tres cajas disponibles (primer dimensión del arreglo: caja 1, caja 2 o caja 3), ya sea en el turno mañana o tarde (segunda dimensión: 1 para la mañana o 2 para la tarde) en cada día hábil de una semana (tercera dimensión: 1 lunes, 2 martes, 3 miércoles, 4 jueves o 5 viernes). Si queremos registrar, por ejemplo, que la caja 1 en el turno tarde del día jueves atendió 12 clientes, tenemos que guardar el valor 12 en la posición [1, 2, 4] del arreglo.
El arreglo de 3 dimensiones que permite acomodar toda la información del ejemplo en una sola estructura puede definirse en R así: