apply(X, MARGIN, FUN)
7 Laboratorio de R
5
- Autor: Edsaúl Emilio Pérez Guerrero
- Afiliación: Universidad de Guadalajara
- mail: edsaul.perezg@academicos.udg.mx
- Última actualización: 2024-09-11
7.1 Introducción
Generar miles de lineas de código puede resultar muy tedioso e implica un arduo trabajo, por lo tanto la eficiencia y claridad en el código son cruciales para el procesamiento de conjuntos de datos complejos y la realización de análisis estadísticos elaborados. La familia de funciones apply y los operadores pipe, %>%
(introducido por el paquete dplyr
) y |>
(de R
base), son herramientas fundamentales que ofrecen una sintaxis más limpia e intuitiva. Además permite ahorrarnos varias líneas de código.
La funciones de la familia apply
permiten la aplicación de diversar funciones a todo un data frame. Por otro lado, los operadores permiten generar código muchas legible. Así pués, en este capítulo se revisarán las funciones de la familia apply
y el operador pipe %>%
y |>
.
7.2 Funciones de la familia apply()
La familia de funciones apply()
pertenecen al paquete base de R. Son funciones creadas para manipular segmentos de datos de matrices, arreglos, listas y marcos de datos de forma repetitiva. Por ejemplo, con estás funciones podemos repetir una función para todas las columnas de un data frame. También, podemos utilizar funciones para grupo de datos en especifico, por ejemplo comparar los casos de los controles. Otras funciones de esta familia realizan funciones de transformación o subconjunto.
La familia de funciones apply en R
, que incluye apply()
, lapply()
, sapply()
, vapply()
, mapply()
, rapply()
y tapply()
, está diseñada para simplificar las operaciones repetitivas sobre estructuras de datos, como matrices y listas, sin la necesidad de recurrir a bucles for.
Estas funciones permiten aplicar una función a los elementos de un objeto de datos de manera eficiente, lo que resulta en código más compacto y legible.
En este texto nos enfocaremos únicamente en las funciones apply()
, lapply()
y tapply()
de forma superficial. Sin embargo, puede encontrar un tutorial más avanzado en: https://www.datacamp.com/tutorial/r-tutorial-apply-family.
7.2.1 La función apply()
Esta función nos permite aplicar una una misma función a todos los elementos de una matriz, ya sea columnas o filas.
La estructura general de la función apply()
es la siguiente:
Si prestamos atención la función necesita de tres argumentos para alimentarse. El segundo parámetro, MARGIN, determina la dimensión (o margen) sobre la cual se agruparán los elementos de X para aplicarles una función específica. Estas dimensiones se identifican mediante números: el 1 para las filas y el 2 para las columnas. Por último, el tercer parámetro, FUN, especifica la función que se aplicará sobre la dimensión seleccionada. Para una mayor claridad sobre cómo opera esta función, es útil examinar un ejemplo práctico.
Supongamos que deseamos conocer media de todas las variables del data frame Pima.tr
, para ello podemos emplear el siguiente código:
#Cargamos la base de datos
library(MASS)
data(Pima.tr)
# Aplicamos la función Apply a las columnas
apply(X=Pima.tr, MARGIN=2, FUN=mean)
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
npreg glu bp skin bmi ped age type
NA NA NA NA NA NA NA NA
Si prestamos atención al código hay dos errores que podemos corregir, el primero es que podemos incluir el argumento na.rm
para omitir los valores perdidos y el segundo es que no es tiene sentido estimar la media para una variable de tipo factor type
(un dato de tipo factor). Vamos a corregir este código por pasos
apply(X=Pima.tr, MARGIN=2, FUN=mean, na.rm=T)
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
Warning in mean.default(newX[, i], ...): argument is not numeric or logical:
returning NA
npreg glu bp skin bmi ped age type
NA NA NA NA NA NA NA NA
Note como el argumento na.rm=T
se aplica la función mean
y no apply
. Todos los argumente que se encuentren despues del argumento FUN
de apply
serán aplicados a la función. Sin embargo, de nueva cuenta presentamos un error, por lo tanto es necesario indicarle las columnas a las que queremos que se aplique la función, para ello vamos a utilizar los corchetes. Estamos aplicando una función a cada elemento de nuestro data frame. Los elementos son las columnas de la 1 a la 7
apply(X=Pima.tr[,1:7], MARGIN=2, FUN=mean, na.rm=T)
npreg glu bp skin bmi ped age
3.570000 123.970000 71.260000 29.215000 32.310000 0.460765 32.110000
Note como el filtro se realizó para el argumento X
y no para otros de los argumentos. En el capitulo Capítulo 6 se revisó a detalle como filtrar columnas y filas.
¿Y si quisiéramos la suma de los datos para cada una de las columnas de Pima.tr
? El siguiente código nos ayudará:
apply(X=Pima.tr[,1:7], MARGIN=2, FUN=sum, na.rm=T)
npreg glu bp skin bmi ped age
714.000 24794.000 14252.000 5843.000 6462.000 92.153 6422.000
7.3 La función lapply()
Esta función es muy parecida a la función apply
sin embargo, esta realiza una operación para una lista y devuelve una lista. De forma simple se puede entender a una lista, con un conjunto de datos de una sola dimensión. La función lapply
se puede aplicar a data frame ya que R
puede coercionar los data frame a lista en algunos situaciones.
El siguiente código ejemplifica como utilizar la función lapply
lapply(X=Pima.tr[,1:7], FUN=sd)
$npreg
[1] 3.366268
$glu
[1] 31.66723
$bp
[1] 11.4796
$skin
[1] 11.72459
$bmi
[1] 6.130212
$ped
[1] 0.3072248
$age
[1] 10.97544
Si notamos en el código no es necesario utilizar el argumento MARGIN
ya que las listas solo tiene una dimensión. Obtenemos el mismo resultado que el que se obtuvo con la función apply
pero en forma de lista.
7.4 La función tapply
Esta función nos permite obtener resultados agrupados. La estructura básica de las función es la siguiente:
tapply(X, INDEX, FUN)
Donde X
es la variable a la que se aplicará la función. INDEX
es la variable de agrupación y FUN
es la función que se aplicará a los datos agrupados. Por ejemplo supongamos que deseamos conocer la media de edad para las pacientes con diabetes y sin diabetes, para ello empleamos el siguiente código:
tapply(X=Pima.tr$age, INDEX=Pima.tr$type, FUN=mean)
No Yes
29.23485 37.69118
Note como fue necesario utilizar el símbolo $
, de lo contrario R
no sabría donde buscar el objeto para hacer las operaciones.
7.4.1 Ejercicios de utlizando las funciones de la familia apply
Ejercicio 7.1 Utilizando la base de datos Pima.tr
de la librería MASS
estime la media, la mediana, la desviación estándar y el rango intercuartil para cada una de las variables del data frame agrupando a las pacientes con y sin diabetes
Ejercicio 7.2 Utilizando la base de datos Melanoma
de la librería MASS
estime la media, la mediana, la desviación estándar y el rango intercuartil para cada una de las variables del data frame que sean cuantitativas
Ejercicio 7.3 Utilizando la base de datos Melanoma
de la librería MASS
estime la media, la mediana, la desviación estándar y el rango intercuartil para cada una de las variables del data frame agrupando por la variable status
.
7.5 Operadores pipe
En R
podemos encontrar varios operados conocidos conocidos como pipe. Los más utilizados es el símbolo %>%
que se encuentra en el paquete dplyr
y el símbolo |>
que se encuentra en el paquete base de R
. Un pipe puede definirse como un símbolo que permite realizar llamadas o funciones encadenadas. De una manera más simple se puede entender como un símbolo que le permite pasar un resultado intermedio a la siguiente función y el resultado de esta función a la siguiente.
Comenzaremos explicando el operador %>%
que es un poco más sencillo de entender que el operador |>
7.5.1 Operador %>%
La estructura básica del operador %>%
es la siguiente:
%>%
df %>%
operación_1 %>%
operación_2 operación_3
El código anterior permite seleccionar una variable de un data frame (df) realizar una operación (operación 1) y tomas los resultados de esta operación para realizar la operación 2. Después toma los resultados de la operación 2 para realizar la última operación (operación 3).
El operador pipe simplemente alimenta los resultados de una operación a la siguiente operación debajo de ella. La ventaja de usar el operador de tubería es que hace que el código sea extremadamente fácil de leer.
El siguiente código permite estimar la media de la edad de lo pacientes incluidos en la base melanoma
y redondear el resultado a dos dígitos
# Llamar la librería MASS para poder cargar la base melanoma
library(MASS)
data("Melanoma")
# Estimar la media redondeando a dos dígitos
round(mean(Melanoma$age), 2)
[1] 52.46
Note como necesitamos de la función round
y dentro de ella estimamos la media. Utilizando el operador pipe el código podría simplificarse de la siguiente forma:
library(dplyr) #Librería para poder utilizar el operador pipe
Attaching package: 'dplyr'
The following object is masked from 'package:MASS':
select
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
$age %>%
Melanomamean() %>%
round(2)
[1] 52.46
El código anterior tomó el objeto Melanoma$age
para estimar la media y luego redondearlo a 2 a dígitos.
Existen algunas funciones en las que no es necesario utilizar el símbolo $
para que R
sepa donde buscar la variable sobre la cual va ejecutar la operación. Estás funciones pertenecen principalmente al mundo de tidyverse
y a la librería rstatix
que utilizaremos en futuros capítulos.
7.5.2 Operador |>
Se podría decir, que los operadores c son iguales y hasta cierto punto funcionan de la misma manera. Sin embargo, al tener orígenes distintos, en ciertas situaciones difieren en su funcionamiento. Profundizar en las diferencias de estos operados se escapa de los objetivos de este texto, pero para fines prácticos podríamos decir que el operador |>
siempre necesita tener declarado la base de datos de donde tomar las variables. Por ejemplo, si quisiéramos estimar una regresión ver capitulo [Regresión lineal múltiple] utilizando el operador %>%
podríamos utilizar el siguiente código:
%>%
Melanoma lm(age~thickness, data=.)
Call:
lm(formula = age ~ thickness, data = .)
Coefficients:
(Intercept) thickness
48.968 1.197
Sin embargo con el operador |>
necesitamos declarar la base de datos, y su uso no se justificaría ya que el resultado sería un error.
|>
Melanoma lm(age~thickness, data=.)# No declarar la base de datos
El código anterior daría como resultado: Error in is.data.frame(data) : object '.' not found
.
Si volvemos al ejemplo de la estimación de la media con dos dígitos, no tendríamos problema en usar los pipe de forma indistinta y el código sería prácticamente igual.
$age |>
Melanomamean() |>
round(2)
[1] 52.46
7.5.3 Conclusión
Aunque es más complejo, podriamos decir que ambos operadores pueden fuincar para lo mismo. En este texto utilizaremos ambos operadores, aunque Ela mayoría de las ocasiones utilizaremos ambos operadores pero le daremos prioridad al operados |>
.
7.5.4 Información extra
Puede encontrar mayor información sobre el uso de los pipe en:
https://www.statology.org/pipe-in-r/
https://www.r-bloggers.com/2017/12/pipes-in-r-tutorial-for-beginners/
https://towardsdatascience.com/an-introduction-to-the-pipe-in-r-823090760d64
https://towardsdatascience.com/understanding-the-native-r-pipe-98dea6d8b61b
https://towardsdatascience.com/understanding-the-native-r-pipe-98dea6d8b61b
7.6 Ejercicio final
Ejercicio 7.4 Utilice los datos del ejercicio Ejercicio 9.32 y genere estadística descriptiva que incluya: media, mediana, desviación estándar, CV, varianza, Q1, Q2, Q3, valor mínimo y máximo para todas las variables numéricas de la base Base Descriptivos.rds divido por hombres y mujeres. Genere una tabla con sus resultados