Aprender a procesar de forma serial operaciones que deben repetirse sobre diferentes objetos
Familiarizarse con el uso de bucles en la plataforma R
Tener una noción general de los opciones disponibles en R para construir bucles
Primero debemos preparar los archivos de ejemplo:
Código
# definir directorio a donde guardar los archivosdirectorio <-"DIRECCION_DONDE_GUARDAR_LOS ARCHIVOS_DE_EJEMPLO"# guardar archivosdownload.file(url =paste0("https://github.com/maRce10/ucr_r_avanzado/","raw/master/additional_files/datos_camara_submarina.zip" ),destfile =file.path(directorio, "datos_camara_submarina.zip"))# extraerlos del zipunzip(zipfile =file.path(directorio, "datos_camara_submarina.zip"),exdir = directorio)# hacer vector con nombre y direccion de archivosarchivos_txt <-list.files(path = directorio,full.names =TRUE,pattern ="TXT$" )
También pueden bajar el archivo directamente de este enlance. Recuerde extraer los archivos y hacer el vector con los nombres de los archivos (correr lineas de la 9 a la 13).
Si todo salió bien el vector “archivos_txt” deberia tener 19 elementos:
Código
length(archivos_txt)
[1] 19
1 Bucles (loops)
Proceso automatizado de varios pasos organizado como secuencias de acciones (p. Ej., procesos ‘por lotes’)
Se usa para acelerar los procedimientos en los que se aplica la misma acción a muchos objetos (similares)
Crítico para la gestión de grandes bases de datos (‘big data’, y buenas prácticas de programación)
2 tipos básicos:
Ejecutar para un número predefinido de iteraciones (es decir, tiempos). Estos se subdividen en dos clases:
Los resultados pueden ingresarse nuevamente en la siguiente iteración (bucles for)
Los resultados de una interacción no pueden afectar a otras iteraciones ((X)apply)
* Modificado de Tutorial de bucles Datacamp
Ejecutar hasta que se cumpla una condición predefinida (bucles while yrepeat)
2 Bucles ‘for’
Por mucho, for es el bucle más popular. Se caracteriza por que el número de iteraciones se puede determinar de antemano y las iteraciones pueden tomar en cuenta resultados de iteraciones anteriores:
El bucle for se inicia con el operador for. A este se le da un vector (sensu lato: lista o vector atómico) sobre el cual repetir una tarea. La tarea se encuentra en el cuerpo del bucle:
Código
vctr <-1:3# vector sobre el cual iterar el buclefor (i in vctr) { # inicio del bucleprint(i^2)} # cuerpo del bucle
[1] 1
[1] 4
[1] 9
Note que en dentro de los paréntesis del operador for se usa el operador in. Este denota el nombre de objeto que se usará en el cuerpo del bucle para asignar los velores del vector. El bucle devuelve 3 valores, uno para cada valor en vctr.
Una forma mas clara de demostrar como el bucle repite la acción de forma serial sobre cada elemento del vector es añadiendo pausas entre iteraciones. La función Sys.sleep() pausa el código de R por el número de segundos que se le defina:
Código
vctr <-1:3# vector sobre el cual iterar el buclefor (i in vctr) { # inicio del bucleprint(paste("# corriendo iteración", i))Sys.sleep(2) # pausar 2 segundosprint(i^2) # cuerpo del bucle}
En este caso vemos como el bucle toma una pausa entre cada iteración y luego hace el cálculo (cuando corre el código en su computadora).
Si deseamos guardar el resultado de las operaciones debemos añadirlo a un vector vacío. Para hacer esto hay 2 opciones:
Usando la función append()
Agregar nuevos elementos a un vector usando indexación
El siguiente código guarda los resultados usando append():
Código
# vector sobre el cual iterar el buclevctr <-1:3# vector vacioresultados <-vector()# inicio del buclefor (i in vctr) {# cuerpo del bucle i2 <- i^2# guardar resultados en vector vacio resultados <-append(resultados, i2)}# ver resultadosresultados
[1] 1 4 9
Note que append() se usa dentro del cuerpo del bucle luego de hacer los cálculos.
Así podemos guardar los resultados usando indexación:
Código
# vector sobre el cual iterar el buclevctr <-1:3# vector vacioresultados <-vector()# inicio del buclefor (i in vctr) {# cuerpo del bucle i2 <- i^2# guardar resultados en vector vacio resultados[length(resultados) +1] <- i2}resultados
[1] 1 4 9
Estos bucles se pueden correr sobre cualquier vector. Por ejemplo podemos evaluar para el juego de datos iris el promedio del largo del sépalo para cada especie de esta forma:
Código
# vector sobre el cual iterar el buclevctr <-unique(iris$Species)# vector vacioresultados <-vector()# inicio del buclefor (i in vctr) {# cuerpo del bucle i2 <-max(iris$Sepal.Length[iris$Species == i])# guardar resultados en vector vacio resultados <-append(resultados, i2)}# añadir nombre de especiesnames(resultados) <- vctrresultados
setosa versicolor virginica
5.8 7.0 7.9
2.1 Aplicación al manejo de datos
Usaremos los datos de ejemplo que bajamos al inicio del manual para demostrar la utilidad del los bucles en el manejo de datos. Estos datos muestran la salida de un programa de identificación automática de especies marinas en videos pasivos tomados en la columna de agua. Para cada video analizado el programa genera un archivo de texto (.TXT) con una fila para cada especie detectada mas una serie de metadatos asociados a la detección. Los datos se ven así:
Recordemos que los nombres de los archivos .TXT están guardados en un vector llamado archivos_txt. Podemos leer el primer archivo (i.e. el primer elemento en archivos_txt) de la siguiente forma:
Código
archivo1 <-read.table(archivos_txt[1],header =TRUE,skip =4,sep ="\t")# ver pirmeras 4 filas y 8 columnashead(archivo1[1:4, 1:8])
Filename
Frame
Time..mins.
Period
Period.time..mins.
OpCode
TapeReader
Depth
GH018767_LcamT3.MP4
11451
3.1840
inicio
1.5504
R3-LcamT3-RcamN4_Pelada1_2021-09-24
Andres
10
GH018767_LcamT3.MP4
25005
6.9528
inicio
5.3192
R3-LcamT3-RcamN4_Pelada1_2021-09-24
Andres
10
GH028767_LcamT3.MP4
30464
24.5201
inicio
22.8865
R3-LcamT3-RcamN4_Pelada1_2021-09-24
Andres
10
GH018767_LcamT3.MP4
26911
7.4828
inicio
5.8492
R3-LcamT3-RcamN4_Pelada1_2021-09-24
Andres
10
Podemos saber cuantas especies se observaron en ese muestreo simplemente calculado el número de filas en archivo:
Código
nrow(archivo1)
[1] 39
Ahora, para hacer esto con todos los archivos no es eficiente leer cada uno en su propia linea de código y luego calcular el número de filas. Al fin de cuentas, todos los códigos serian muy parecidos, solo cambiaría el nombre del archivo. Es en estos casos que los bucles son de gran utilidad. En este ejemplo solo debemos incorporar el código de lectura del archivo y del cálculo del número de filas en el cuerpo del bucle, usando el nombre de los archivos (archivos_txt) como el vector sobre el cual iterar el bucle:
Código
# vector vacioresultados <-vector()# inicio del buclefor (i in archivos_txt) {# leer archivo txt <-read.table(i,header =TRUE,skip =4,sep ="\t")# calcular numero de filas n_sp <-nrow(txt)# guardar resultados en vector vacio resultados <-append(resultados, n_sp)}resultados
Podemos ordenar estos resultados fácilmente haciendo un cuadro de datos (data.frame). Para esto creamos una columna con el nombre del archivo y otra con el resultado del número de filas:
Código
# organizar en data framen_sp_df <-data.frame(archivo =basename(archivos_txt), n_sp = resultados)# ver primeras 6 filashead(n_sp_df)
1.3 Añada una columna al cuadro de datos creado en el ejercicio anterior indicando la fecha de creación del video (esta información se encuentra en la columna ‘Date’ de cada archivo de texto). Note que cada archivo contiene solamente una fecha. Debe usar un bucle for para extraer esta información de los archivos.
1.4 Añada una columna al cuadro de datos creado en el ejercicio 1.2 (y modificado en 1.3) indicando la profundidad a la que se grabó el video. Debe usar un bucle for para extraer esta información de los archivos.
Puede usar el siguiente código para convertir profundidad a un vector numérico:
1.5 ¿Cuál es la correlación entre el número de especies observadas (que es igual al número de filas) y la profundidad? (pista: cor.test())
1.6 ¿Cuál es la correlación entre el número de familias y la profundidad?
El bucle for también puede ser usado para juntar todas los datos de los archivos de texto en un solo cuadro de datos. Esto lo podemos hacer “rellenando” un cuadro de datos vacío, de forma análoga a como rellenamos un vector vació anteriormente:
Código
# vector vaciodf_resultados <-data.frame()# inicio del buclefor (i in archivos_txt) {# leer archivo txt <-read.table(i,header =TRUE,skip =4,sep ="\t")# guardar resultados en vector vacio df_resultados <-rbind(df_resultados, txt)}nrow(df_resultados) ==sum(n_sp_df$filas)
[1] FALSE
3 Bucles ‘(X)apply’
(X)apply se refiere en realidad a una familia de funciones que toman una función como entrada y la aplican a una secuencia de objetos (vectores sensu lato). Por lo tanto hay varias funciones (X)apply en R:
Sin embargo, las más utilizadas son apply,sapply, lapply ytapply. Todos siguen la misma lógica:
lapply toma un vector (atómico o de lista), aplica una función a cada elemento y devuelve una lista:
Código
lapply(X =c(4, 9, 16), FUN = sqrt)
[[1]]
[1] 2
[[2]]
[1] 3
[[3]]
[1] 4
sapply también toma un vector (atómico o de lista) y aplica la función a cada elemento, sin embargo, el resultado es un vector atómico (si es que se puede empaquetar como un vector):
Código
lapply(X =c(4, 9, 16), FUN = sqrt)
[[1]]
[1] 2
[[2]]
[1] 3
[[3]]
[1] 4
apply aplica una función a cada una de las filas o columnas de un objeto bidimensional. Por ejemplo, el siguiente código calcula el promedio para largo y ancho de sépalo en el juego de datos iris:
Código
# promedio de largo y ancho de setaloapply(X = iris[, c("Sepal.Length", "Sepal.Width")], MARGIN =2, FUN = mean)
Sepal.Length Sepal.Width
5.8433 3.0573
Note que el argumento ‘MARGIN’ indica si el calculo se lleva a cabo a nivel de filas (MARGIN = 1) o columnas (MARGIN = 2).
tapply es más específico ya que aplica una función a un subconjunto de datos definido por un vector categórico adicional. Por ejemplo, podemos calcular la longitud promedio de pétalo para cada especie en el juego de datos ‘iris’ de la siguiente manera:
Código
tapply(X = iris$Petal.Length, INDEX = iris$Species, FUN = mean)
setosa versicolor virginica
1.462 4.260 5.552
Los bucles (X)apply pueden modificarse para realizar acciones personalizadas creando nuevas funciones (ya sea dentro o fuera del bucle):
Código
# funcion desde fuera del buclen_filas <-function(x) {# leer archivo txt <-read.table(x,header =TRUE,skip =4,sep ="\t")# calcular numero de filas nrw <-nrow(txt)return(nrw)}# correr buclefilas <-lapply(X = archivos_txt, FUN = n_filas)# ver resultadoshead(filas)
en este tipo de bucles no hay retroalimentación de las iteraciones anteriores (es decir, los resultados de una iteración no se pueden ingresar en las iteraciones posteriores)
(X)apply es más limpio que otros bucles porque los objetos creados dentro de ellos no están disponibles en el entorno de trabajo actual.
3.1 bucles replicate
El bucle replicate también pertenece a la familia de los (X)apply (a pesar de su nombre), ya que toma una función y la replica. Sin embargo solo replica una acción (generalmente aleatoria) y el usuario no tiene control sobre el insumo a la función. El argumento ‘n’ define cuantas veces se replica la acción y ‘expr’ define la acción a realizar:
Note que los resultados son agrupados en una matrix. ‘expr’ también puede replicar código que no ha sido “empaquetado en una función”.
Ejercicio 2
2.1 Haga un bucle lapply equivalente al bucle for en el ejercicio 1.1.
2.2 Haga un bucle sapply equivalente al bucle for en el ejercicio 1.1 y ponga el resultado en un cuadro de datos, similiar a lo hecho en el ejercicio 1.2
2.3 Haga un bucle sapply que permita añadir al cuadro de datos creado en el ejercicio anterior el total de individuos observados en un muestreo (pista: sumatoria de la columna ‘MaxN’).
2.4 Haga un bucle tapply para calcular el error estandar para el ancho de sépalo por especie en el juego de datos ‘iris’.
4 Bucles ‘repeat’
Los bucles repeat deben cumplir una condición para detenerse. Típicamente el bucle lleva a cabo la acción al menos una vez, independientemente de la evaluación de la condición:
El siguiente bucle repeat se ejecuta hasta que la correlación de las variables continuas generadas al azar es mayor que un umbral:
Código
# crear vector vaciocc_vector <-NULLrepeat{# generar la variable 1 v1 <-rnorm(n =20, mean =100, sd =20)# generar la variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# correr la correlacion corr_coef <-cor(v1, v2)# guardar resultados cc_vector[length(cc_vector) +1] <- corr_coef# parar si se cumple la condicionif (corr_coef >0.5) break}head(cc_vector)
cc <-data.frame(y = cc_vector, x =1:length(cc_vector))ggplot(data = cc, aes(x, y)) +geom_hline(yintercept =0.5,col = cols[4],lwd =3) +geom_line(col = cols[8], lwd =4) +labs(x ="Iteraciones", y ="Correlación de pearson (r)") +theme_classic(base_size =25)
La condición determina si el ciclo debe detenerse.
Una característica importante de los bucles while,repeat y for es que pueden tomar resultados de iteraciones anteriores como entrada en iteraciones posteriores. Esto se debe a que los objetos creados dentro de la función se guardan en el entorno actual (a diferencia de los bucles Xapply).
5 Bucles ‘While’
Los bucles while aplican una acción en una secuencia de elementos hasta que se cumpla una condición. Son muy parecidos a los bucles repeat. La condición puede evaluar un resultado del propio bucle o una entrada externa:
Código
# definir valor inicialcorr_coef <-0# iniciar buclewhile (corr_coef <0.5) {# generar la variable 1 v1 <-rnorm(n =20, mean =100, sd =20)# generar la variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# corrrer la correlacion corr_coef <-cor(v1, v2)# imprimirprint(corr_coef)}corr_coef
Con un pequeño ajuste, un bucle while también puede evaluar varias condiciones a la vez. Por ejemplo, también podemos incluir altos valores de correlación negativa:
Código
# definir valor inicialcorr_coef <-0# crear vector vaciocc_vector <-NULLwhile (corr_coef <0.5& corr_coef >-0.5) {# generar la variable 1 v1 <-rnorm(n =20, mean =100, sd =20)# generar la variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# correr correlacion corr_coef <-cor(v1, v2)# guardar resultados cc_vector[length(cc_vector) +1] <- corr_coef}head(cc_vector)
3.1 Haga un bucle while que se detenga si la correlación es superior a 0.8 o si el bucle ha estado ejecutándose durante más de 10 segundos (consejo: use la funcióndifftime y/o as.numeric)
3.2 Haga un bucle repeat que se detenga solo si la correlación es mayor que 0.5 pero menor que 0.55
---title: <font size="7"><b>Bucles</b></font>format: html: theme: simplex.scss css: styles.css toc: true df-print: kable code-fold: show code-tools: true link-external-icon: false link-external-newwindow: true---```{=html}<style>body { counter-reset: source-line 0; }pre.numberSource code { counter-reset: none; }</style>``````{r,echo=FALSE,message=FALSE}options("digits"=5)options("digits.secs"=3)# options to customize chunk outputsknitr::opts_chunk$set(class.source ="numberLines lineAnchors", # for code line numbersmessage =FALSE)library(ggplot2)library(RColorBrewer)theme_set(theme_classic(base_size =50))cols <-brewer.pal(10, "Spectral")```::: {.alert .alert-info}# Objetivos del manual {.unnumbered .unlisted}- Aprender a procesar de forma serial operaciones que deben repetirse sobre diferentes objetos- Familiarizarse con el uso de bucles en la plataforma R- Tener una noción general de los opciones disponibles en R para construir bucles:::------------------------------------------------------------------------Primero debemos preparar los archivos de ejemplo:```{r, eval = FALSE}# definir directorio a donde guardar los archivosdirectorio <-"DIRECCION_DONDE_GUARDAR_LOS ARCHIVOS_DE_EJEMPLO"# guardar archivosdownload.file(url =paste0("https://github.com/maRce10/ucr_r_avanzado/","raw/master/additional_files/datos_camara_submarina.zip" ),destfile =file.path(directorio, "datos_camara_submarina.zip"))# extraerlos del zipunzip(zipfile =file.path(directorio, "datos_camara_submarina.zip"),exdir = directorio)# hacer vector con nombre y direccion de archivosarchivos_txt <-list.files(path = directorio,full.names =TRUE,pattern ="TXT$" )``````{r, echo = FALSE}# definir directorio a donde guardar los archivosdirectorio <-"./data/"# guardar archivosdownload.file(url =paste0("https://github.com/maRce10/","r_avanzado_2023/raw/master/data/datos_camara_submarina.zip" ),destfile =file.path(directorio, "datos_camara_submarina.zip"))# extraerlos del zipunzip(zipfile =file.path(directorio, "datos_camara_submarina.zip"), exdir = directorio)# hacer vector con nombre y direccion de archivosarchivos_txt <-list.files(path = directorio, full.names =TRUE, pattern ="TXT$")```También pueden bajar el archivo directamente de [este enlance](https://github.com/maRce10/ucr_r_avanzado/raw/master/additional_files/datos_camara_submarina.zip). Recuerde extraer los archivos y hacer el vector con los nombres de los archivos (correr lineas de la 9 a la 13).Si todo salió bien el vector "archivos_txt" deberia tener `r length(archivos_txt)` elementos:```{r}length(archivos_txt)```------------------------------------------------------------------------# Bucles (loops)- Proceso automatizado de varios pasos organizado como secuencias de acciones (p. Ej., procesos 'por lotes')- Se usa para acelerar los procedimientos en los que se aplica la misma acción a muchos objetos (similares)- Crítico para la gestión de grandes bases de datos ('big data', y buenas prácticas de programación)2 tipos básicos:1) Ejecutar para un número predefinido de iteraciones (es decir, tiempos). Estos se subdividen en dos clases: a. Los resultados pueden ingresarse nuevamente en la siguiente iteración (bucles `for`) b. Los resultados de una interacción no pueden afectar a otras iteraciones (`(X)apply`)```{r, echo=FALSE,out.width="70%", fig.align="center"}knitr::include_graphics("./images/loops2_esp.png")```<fontsize="2">\* Modificado de <i> Tutorial de bucles Datacamp </i></font>2) Ejecutar hasta que se cumpla una condición predefinida (bucles `while` y`repeat`)```{r, echo=FALSE,out.width="70%", fig.align="center"}knitr::include_graphics("./images/loops1_esp.png")```------------------------------------------------------------------------# Bucles 'for'Por mucho, `for` es el bucle más popular. Se caracteriza por que el número de iteraciones se puede determinar de antemano y las iteraciones pueden tomar en cuenta resultados de iteraciones anteriores:```{r, echo=FALSE,out.width="40%", fig.align="center"}knitr::include_graphics("./images/for_esp.png")```El bucle `for` se inicia con el operador `for`. A este se le da un vector (*sensu lato*: lista o vector atómico) sobre el cual repetir una tarea. La tarea se encuentra en el cuerpo del bucle:```{r}vctr <-1:3# vector sobre el cual iterar el buclefor (i in vctr) { # inicio del bucleprint(i^2)} # cuerpo del bucle```Note que en dentro de los paréntesis del operador `for` se usa el operador `in`. Este denota el nombre de objeto que se usará en el cuerpo del bucle para asignar los velores del vector. El bucle devuelve 3 valores, uno para cada valor en `vctr`.Una forma mas clara de demostrar como el bucle repite la acción de forma serial sobre cada elemento del vector es añadiendo pausas entre iteraciones. La función `Sys.sleep()` pausa el código de R por el número de segundos que se le defina:```{r}vctr <-1:3# vector sobre el cual iterar el buclefor (i in vctr) { # inicio del bucleprint(paste("# corriendo iteración", i))Sys.sleep(2) # pausar 2 segundosprint(i^2) # cuerpo del bucle}```En este caso vemos como el bucle toma una pausa entre cada iteración y luego hace el cálculo (cuando corre el código en su computadora).Si deseamos guardar el resultado de las operaciones debemos añadirlo a un vector vacío. Para hacer esto hay 2 opciones:- Usando la función `append()`- Agregar nuevos elementos a un vector usando indexaciónEl siguiente código guarda los resultados usando `append()`:```{r}# vector sobre el cual iterar el buclevctr <-1:3# vector vacioresultados <-vector()# inicio del buclefor (i in vctr) {# cuerpo del bucle i2 <- i^2# guardar resultados en vector vacio resultados <-append(resultados, i2)}# ver resultadosresultados```Note que `append()` se usa dentro del cuerpo del bucle luego de hacer los cálculos.Así podemos guardar los resultados usando indexación:```{r}# vector sobre el cual iterar el buclevctr <-1:3# vector vacioresultados <-vector()# inicio del buclefor (i in vctr) {# cuerpo del bucle i2 <- i^2# guardar resultados en vector vacio resultados[length(resultados) +1] <- i2}resultados```Estos bucles se pueden correr sobre cualquier vector. Por ejemplo podemos evaluar para el juego de datos `iris` el promedio del largo del sépalo para cada especie de esta forma:```{r}# vector sobre el cual iterar el buclevctr <-unique(iris$Species)# vector vacioresultados <-vector()# inicio del buclefor (i in vctr) {# cuerpo del bucle i2 <-max(iris$Sepal.Length[iris$Species == i])# guardar resultados en vector vacio resultados <-append(resultados, i2)}# añadir nombre de especiesnames(resultados) <- vctrresultados```## Aplicación al manejo de datosUsaremos los datos de ejemplo que bajamos al inicio del manual para demostrar la utilidad del los bucles en el manejo de datos. Estos datos muestran la salida de un programa de identificación automática de especies marinas en videos pasivos tomados en la columna de agua. Para cada video analizado el programa genera un archivo de texto (.TXT) con una fila para cada especie detectada mas una serie de metadatos asociados a la detección. Los datos se ven así:```{r, echo=FALSE,out.width="100%", fig.align="center"}knitr::include_graphics("./images/datos.png")```Recordemos que los nombres de los archivos .TXT están guardados en un vector llamado `archivos_txt`. Podemos leer el primer archivo (i.e. el primer elemento en `archivos_txt`) de la siguiente forma:```{r}archivo1 <-read.table(archivos_txt[1],header =TRUE,skip =4,sep ="\t")# ver pirmeras 4 filas y 8 columnashead(archivo1[1:4, 1:8])```Podemos saber cuantas especies se observaron en ese muestreo simplemente calculado el número de filas en `archivo`:```{r}nrow(archivo1)```Ahora, para hacer esto con todos los archivos **no es eficiente leer cada uno en su propia linea de código** y luego calcular el número de filas. Al fin de cuentas, todos los códigos serian muy parecidos, solo cambiaría el nombre del archivo. Es en estos casos que los bucles son de gran utilidad. En este ejemplo solo debemos incorporar el código de lectura del archivo y del cálculo del número de filas en el cuerpo del bucle, usando el nombre de los archivos (`archivos_txt`) como el vector sobre el cual iterar el bucle:```{r}# vector vacioresultados <-vector()# inicio del buclefor (i in archivos_txt) {# leer archivo txt <-read.table(i,header =TRUE,skip =4,sep ="\t")# calcular numero de filas n_sp <-nrow(txt)# guardar resultados en vector vacio resultados <-append(resultados, n_sp)}resultados```Podemos ordenar estos resultados fácilmente haciendo un cuadro de datos (data.frame). Para esto creamos una columna con el nombre del archivo y otra con el resultado del número de filas:```{r}# organizar en data framen_sp_df <-data.frame(archivo =basename(archivos_txt), n_sp = resultados)# ver primeras 6 filashead(n_sp_df)```::: {.alert .alert-info}<fontsize="5"> Ejercicio 1 </font>Podemos calcular el número de familias observadas para el primer archivo que leímos (`archivo1`) de esta forma:```{r}length(unique(archivo1$Family))```1.1 Haga un bucle `for` que devuelva el número de familias para cada archivo```{r, eval = FALSE, echo = FALSE}# vector vacioresultados <-vector()# inicio del buclefor (i in archivos_txt) {# leer archivo txt <-read.table(i,header =TRUE,skip =4,sep ="\t")# calcular numero de familias nfam <-length(unique(txt$Family))# guardar resultados en vector vacio resultados <-append(resultados, nfam)}n_sp_df$familias <- resultados```1.2 Cree un cuadro de datos (data frame) que contenga dos columnas, una para el nombre del archivo y otra para el número de familias```{r}n_familias_df <-data.frame(archivo =basename(archivos_txt), n_familias = resultados)```1.3 Añada una columna al cuadro de datos creado en el ejercicio anterior indicando la fecha de creación del video (esta información se encuentra en la columna 'Date' de cada archivo de texto). Note que cada archivo contiene solamente una fecha. Debe usar un bucle `for` para extraer esta información de los archivos.```{r, eval = FALSE, echo = FALSE}# vector vacioresultados <-vector()# inicio del buclefor (i in archivos_txt) {# leer archivo txt <-read.table(i,header =TRUE,skip =4,sep ="\t")# guardar fecha fecha <- txt$Date[1]# guardar resultados en vector vacio resultados <-append(resultados, fecha)}n_sp_df$fecha <- resultados```1.4 Añada una columna al cuadro de datos creado en el ejercicio 1.2 (y modificado en 1.3) indicando la profundidad a la que se grabó el video. Debe usar un bucle `for` para extraer esta información de los archivos.```{r, eval = FALSE, echo = FALSE}resultados <-vector() # vector vaciofor (i in archivos_txt) {# inicio del bucle txt <-read.table(i,header =TRUE,skip =4,sep ="\t") # leer archivo prof <- txt$Depth[1] # calcular numero de familias resultados <-append(resultados, prof) # guardar resultados en vector vacio}resultados <-gsub(" m", "", resultados)resultados <-gsub("\\,", ".", resultados)n_sp_df$profundidad <-as.numeric(resultados)```Puede usar el siguiente código para convertir profundidad a un vector numérico:```{r, eval=FALSE}n_sp_df$profundidad <-as.numeric(gsub(" m", "", gsub("\\,", ".", n_sp_df$profundidad)))```1.5 ¿Cuál es la correlación entre el número de especies observadas (que es igual al número de filas) y la profundidad? (pista: `cor.test()`)1.6 ¿Cuál es la correlación entre el número de familias y la profundidad?```{r, eval = FALSE, echo = FALSE}cor(n_sp_df$profundidad, n_sp_df$familias)```:::El bucle `for` también puede ser usado para juntar todas los datos de los archivos de texto en un solo cuadro de datos. Esto lo podemos hacer "rellenando" un cuadro de datos vacío, de forma análoga a como rellenamos un vector vació anteriormente:```{r, eval = TRUE, echo = TRUE}# vector vaciodf_resultados <-data.frame()# inicio del buclefor (i in archivos_txt) {# leer archivo txt <-read.table(i,header =TRUE,skip =4,sep ="\t")# guardar resultados en vector vacio df_resultados <-rbind(df_resultados, txt)}nrow(df_resultados) ==sum(n_sp_df$filas)```------------------------------------------------------------------------# Bucles '(X)apply'`(X)apply` se refiere en realidad a una familia de funciones que toman una función como entrada y la aplican a una secuencia de objetos (vectores *sensu lato*). Por lo tanto hay varias funciones `(X)apply` en R:```{r}apropos("apply$")```Sin embargo, las más utilizadas son `apply`,`sapply`, `lapply` y`tapply`. Todos siguen la misma lógica:```{r, echo=FALSE,out.width="40%", fig.align="center"}knitr::include_graphics("./images/xapply_esp.png")````lapply` toma un vector (atómico o de lista), aplica una función a cada elemento y devuelve una lista:```{r, eval = TRUE}lapply(X =c(4, 9, 16), FUN = sqrt)````sapply` también toma un vector (atómico o de lista) y aplica la función a cada elemento, sin embargo, el resultado es un vector atómico (si es que se puede empaquetar como un vector):```{r, eval = TRUE}lapply(X =c(4, 9, 16), FUN = sqrt)````apply` aplica una función a cada una de las filas o columnas de un objeto bidimensional. Por ejemplo, el siguiente código calcula el promedio para largo y ancho de sépalo en el juego de datos `iris`:```{r}# promedio de largo y ancho de setaloapply(X = iris[, c("Sepal.Length", "Sepal.Width")], MARGIN =2, FUN = mean)```Note que el argumento 'MARGIN' indica si el calculo se lleva a cabo a nivel de filas (`MARGIN = 1`) o columnas (`MARGIN = 2`).`tapply` es más específico ya que aplica una función a un subconjunto de datos definido por un vector categórico adicional. Por ejemplo, podemos calcular la longitud promedio de pétalo para cada especie en el juego de datos 'iris' de la siguiente manera:```{r, eval=T, echo=T}tapply(X = iris$Petal.Length, INDEX = iris$Species, FUN = mean)```Los bucles `(X)apply` pueden modificarse para realizar acciones personalizadas creando nuevas funciones (ya sea dentro o fuera del bucle):```{r, eval = TRUE}# funcion desde fuera del buclen_filas <-function(x) {# leer archivo txt <-read.table(x,header =TRUE,skip =4,sep ="\t")# calcular numero de filas nrw <-nrow(txt)return(nrw)}# correr buclefilas <-lapply(X = archivos_txt, FUN = n_filas)# ver resultadoshead(filas)```Note que el resultado es una lista. Si deseamos generar un vector podemos usar el bucle `sapply`:```{r, eval = TRUE}# correr buclefilas <-sapply(X = archivos_txt, FUN = n_filas)# ver resultadoshead(filas)```Podemos usar funciones anónimas (i.e. funciones que se crean dentro del llamado del bucle) así:```{r, eval = TRUE}# correr buclefilas <-sapply(X = archivos_txt,FUN =function(x) {nrow(read.table( x,header =TRUE,skip =4,sep ="\t" )) })# ver resultadoshead(filas)```Tenga en cuenta que:1) en este tipo de bucles no hay retroalimentación de las iteraciones anteriores (es decir, los resultados de una iteración no se pueden ingresar en las iteraciones posteriores)2) `(X)apply` es más limpio que otros bucles porque los objetos creados dentro de ellos no están disponibles en el entorno de trabajo actual.## bucles `replicate`El bucle `replicate` también pertenece a la familia de los `(X)apply` (a pesar de su nombre), ya que toma una función y la replica. Sin embargo solo replica una acción (generalmente aleatoria) y el usuario no tiene control sobre el insumo a la función. El argumento 'n' define cuantas veces se replica la acción y 'expr' define la acción a realizar:```{r}replicate(n =3, expr =rnorm(10))```Note que los resultados son agrupados en una matrix. 'expr' también puede replicar código que no ha sido "empaquetado en una función".::: {.alert .alert-info}<fontsize="5"> Ejercicio 2</font>2.1 Haga un bucle `lapply` equivalente al bucle `for` en el ejercicio 1.1.2.2 Haga un bucle `sapply` equivalente al bucle `for` en el ejercicio 1.1 y ponga el resultado en un cuadro de datos, similiar a lo hecho en el ejercicio 1.22.3 Haga un bucle `sapply` que permita añadir al cuadro de datos creado en el ejercicio anterior el total de individuos observados en un muestreo (pista: sumatoria de la columna 'MaxN').2.4 Haga un bucle `tapply` para calcular el error estandar para el ancho de sépalo por especie en el juego de datos 'iris'.:::------------------------------------------------------------------------# Bucles 'repeat'Los bucles `repeat` deben cumplir una condición para detenerse. Típicamente el bucle lleva a cabo la acción al menos una vez, independientemente de la evaluación de la condición:```{r, echo=FALSE,out.width="40%", fig.align="center"}knitr::include_graphics("./images/repeat_esp.png")```El siguiente bucle `repeat` se ejecuta hasta que la correlación de las variables continuas generadas al azar es mayor que un umbral:```{r, eval=T, echo=T}# crear vector vaciocc_vector <-NULLrepeat{# generar la variable 1 v1 <-rnorm(n =20, mean =100, sd =20)# generar la variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# correr la correlacion corr_coef <-cor(v1, v2)# guardar resultados cc_vector[length(cc_vector) +1] <- corr_coef# parar si se cumple la condicionif (corr_coef >0.5) break}head(cc_vector)```Podemos graficarlo el resultado asi así:```{r, eval=T, echo=T}cc <-data.frame(y = cc_vector, x =1:length(cc_vector))ggplot(data = cc, aes(x, y)) +geom_hline(yintercept =0.5,col = cols[4],lwd =3) +geom_line(col = cols[8], lwd =4) +labs(x ="Iteraciones", y ="Correlación de pearson (r)") +theme_classic(base_size =25)```La condición determina si el ciclo debe detenerse.Una característica importante de los bucles `while`,`repeat` y `for` es que pueden tomar resultados de iteraciones anteriores como entrada en iteraciones posteriores. Esto se debe a que los objetos creados dentro de la función se guardan en el entorno actual (a diferencia de los bucles `Xapply`).------------------------------------------------------------------------# Bucles 'While'Los bucles `while` aplican una acción en una secuencia de elementos hasta que se cumpla una condición. Son muy parecidos a los bucles `repeat`. La condición puede evaluar un resultado del propio bucle o una entrada externa:```{r, echo=FALSE,out.width="40%", fig.align="center"}knitr::include_graphics("./images/while_esp.png")``````{r, eval=F, echo=T}# definir valor inicialcorr_coef <-0# iniciar buclewhile (corr_coef <0.5) {# generar la variable 1 v1 <-rnorm(n =20, mean =100, sd =20)# generar la variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# corrrer la correlacion corr_coef <-cor(v1, v2)# imprimirprint(corr_coef)}corr_coef``````{r, eval=T, echo=F}# set default value as 0corr_coef <-0cc <-NULLwhile (corr_coef <0.5) {set.seed(length(cc) +1)# generate variable 1 v1 <-rnorm(n =20, mean =100, sd =20)set.seed(length(cc) +100)# generate variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# run correlation corr_coef <-cor(v1, v2) cc <-append(cc, corr_coef)}if (length(cc) >10) { cc <- cc[(length(cc) -10):length(cc)]}print(cc)```Con un pequeño ajuste, un bucle `while` también puede evaluar varias condiciones a la vez. Por ejemplo, también podemos incluir altos valores de correlación negativa:```{r, eval=T, echo=T}# definir valor inicialcorr_coef <-0# crear vector vaciocc_vector <-NULLwhile (corr_coef <0.5& corr_coef >-0.5) {# generar la variable 1 v1 <-rnorm(n =20, mean =100, sd =20)# generar la variable 2 v2 <-rnorm(n =20, mean =100, sd =20)# correr correlacion corr_coef <-cor(v1, v2)# guardar resultados cc_vector[length(cc_vector) +1] <- corr_coef}head(cc_vector)``````{r, eval=T, echo=F, message=F, warning=F}cc <- cc_vectorcc <-data.frame(y = cc, x =1:length(cc))if (nrow(cc) >50) { cc <- cc[(nrow(cc) -50):nrow(cc),]}ggplot(data = cc, aes(x, y)) +geom_hline(yintercept =c(-0.5, 0.5),col = cols[4],lwd =3) +geom_line(col = cols[8], lwd =4) +labs(x ="Iteraciones", y ="Correlación de pearson (r)") +theme_classic(base_size =25)```------------------------------------------------------------------------::: {.alert .alert-info}<fontsize="5">Ejercicio 3</font>3.1 Haga un bucle `while` que se detenga si la correlación es superior a 0.8 o si el bucle ha estado ejecutándose durante más de 10 segundos (consejo: use la función`difftime` y/o `as.numeric`)3.2 Haga un bucle `repeat` que se detenga solo si la correlación es mayor que 0.5 pero menor que 0.55:::------------------------------------------------------------------------## Referencias- [Advanced R, H Wickham](http://adv-r.had.co.nz/Functionals.html)- [A Tutorial on Loops in R - Usage and Alternatives, DataCamp](https://www.datacamp.com/community/tutorials/tutorial-on-loops-in-r)------------------------------------------------------------------------<fontsize="5">Session information</font>```{r session info, echo=F}sessionInfo()```