Usando bucles

¿Te encuentras cortando y pegando código de R con frecuencia?

Esto generalmente creará problemas para ti más adelante. Uno de los principios de una buena codificación es tratar de reducir al mínimo la repetición. Hay dos enfoques para organizar tu código y ahorrarte trabajo. El primero es usar funciones y el segundo, cubierto aquí, es usar bucles.

A menudo, queremos realizar tareas repetitivas en las ciencias ambientales. Por ejemplo, es posible que deseemos recorrer una lista de archivos y hacer lo mismo una y otra vez. Hay muchos paquetes en R con funciones que harán todo el trabajo duro por ti (por ejemplo, echa un vistazo a dplyr, tidyr y reshape2 cubiertos aquí). El enfoque de dplyr funciona bien si tus datos son “ordenados” y están en un marco de datos. Si tus datos están en muchos archivos diferentes, entonces un bucle puede ser una solución más rápida.

Sintaxis básica de los bucles

La sintaxis de los bucles es relativamente simple: los componentes esenciales son for(){} donde la parte for() indica cuántas veces se realizan las operaciones dentro de {}.

Considera el siguiente bucle. La primera vez que recorremos el bucle, el valor de i será igual a 1 y este valor se mostrará con la función print. Luego se repetirá con i = 2, hasta i = 10, realizando la tarea que se encuentra dentro de {} cada vez.

for (i in 1:10) {
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10

Podemos cambiar el rango de números (1:10) a cualquier cosa que deseemos, no tienen que ser una secuencia de enteros o incluso números. También puedes cambiar i a cualquier valor que desees.

nums <- c(3.2, 890, 0.0001, 400)

for (bat in nums) {
  print(bat)
}
[1] 3.2
[1] 890
[1] 1e-04
[1] 400
chars <- c("a", "o", "u", "z")

for (bat in chars) {
  print(bat)
}
[1] "a"
[1] "o"
[1] "u"
[1] "z"

De mayor interés para nosotros es cambiar lo que está dentro de {} o la operación que estamos realizando en nuestros datos. Podemos insertar cualquier cosa que deseemos aquí. Aquí hay un bucle que imprimirá el cuadrado y la raíz cuadrada de los números del 1 al 10.

for (i in 1:10) {
  print(i^2)
  print(sqrt(i))
}

A menudo, querremos mantener los resultados que obtenemos de nuestro bucle. La primera opción es crear un vector o marco de datos vacío y agregar los resultados a él. Esto tarda más en ejecutarse, pero no importa realmente con bucles simples, aunque puede aumentar los tiempos de espera para estructuras de bucle más largas y complicadas.

Aquí hay un código que almacenará el cuadrado de los números del 1 al 10 en un nuevo vector llamado x.

x <- vector() # makes a blank vector

for (i in 1:10) {
  y <- i^2 # performs an operation
  x <- append(x, y) # overwrites 'x' with y appended to it
}

Aquí hay un código que almacenará tanto el cuadrado como la raíz cuadrada de los números del 1 al 10 en dos columnas de un nuevo marco de datos llamado x2.

x2 <- data.frame(col1 = vector(), col2 = vector()) # makes a blank data frame with two columns

for (i in 1:10) {
  col1 <- i^2 # performs first operation
  col2 <- sqrt(i) # performs second operation
  x2 <- rbind(x2, cbind(col1, col2)) # overwrites 'x2' values including the new row
}

La segunda opción es crear un vector o marco de datos vacío de dimensiones conocidas y luego colocar los resultados directamente en él. Por ejemplo, si tuviéramos un bucle con 10 elementos, podríamos almacenar los resultados de cada operación en un vector con una longitud 10.

x <- vector(length = 10) # makes a blank vector with a length of 10

for (i in 1:10) {
  y <- i^2
  x[i] <- y # places the output in position i in the vector x
}

Alternativamente, guarda los resultados de múltiples operaciones en un nuevo data frame.

x2 <- data.frame(col1 = vector(length = 10), col2 = vector(length = 10)) # makes a blank data frame with two columns and 10 rows

for (i in 1:10) {
  col1 <- i^2 # performs first operation
  col2 <- sqrt(i) # performs second operation
  x2[i, 1] <- col1 # places the first result into row i, column 1
  x2[i, 2] <- col2 # places the second result into row i, column 2
}

Un ejemplo ecológico

Ahora podemos utilizar tus nuevas habilidades de bucle en un contexto ecológico. Al igual que en el tutorial de Subconjuntos de datos, utilizaremos un conjunto de datos donde se muestrearon murciélagos en un bosque en regeneración en el sureste de Australia, que ha sido adelgazado para reducir la densidad de árboles.

Bats <- read.csv(file = "Bats_data.csv", header = T, stringsAsFactors = F)
str(Bats)
'data.frame':   173 obs. of  10 variables:
 $ Site                 : chr  "CC02A1" "CC02A1" "CC02A1" "CC02A2" ...
 $ Activity             : int  299 276 530 356 571 631 144 124 220 468 ...
 $ Foraging             : int  0 6 14 5 3 17 3 0 7 8 ...
 $ Date                 : chr  "9/01/2013" "8/01/2013" "7/01/2013" "8/01/2013" ...
 $ Treatment.thinned    : chr  "medium-term" "medium-term" "medium-term" "medium-term" ...
 $ Area.thinned         : num  0 0 0 0 0 0 0 0 0 0 ...
 $ Time.since.thinned   : int  7 7 7 7 7 7 7 7 7 7 ...
 $ Exclusion.thinned    : num  25.1 25.1 25.1 25.1 25.1 ...
 $ Distance.murray.water: num  190 190 190 216 216 ...
 $ Distance.creek.water : num  444 444 444 885 885 ...

Al analizar la estructura de estos datos, tenemos dos variables de respuesta: actividad (número de llamadas de murciélagos registradas en una noche) y forrajeo (número de llamadas de alimentación de murciélagos registradas en una noche). Estos datos se recolectaron durante un total de 173 noches de estudio y en 47 sitios diferentes. Hay ocho posibles variables predictoras en el marco de datos, una de las cuales es un factor (Tratamiento.adelgazado), y siete de las cuales son variables continuas (Área.adelgazada, Tiempo.desde.adelgazamiento, Exclusión.adelgazada, Distancia.agua.murray, Distancia.agua.arroyo, Promedio.T, Promedio.H).

Digamos que estamos explorando nuestros datos y nos gustaría saber qué tan bien se correlaciona la actividad de los murciélagos con nuestras covariables continuas. Nos gustaría calcular el coeficiente de correlación de Pearson para la actividad y cada una de las covariables por separado. El coeficiente de correlación de Pearson, calculado con la función cor, varía de -1 (correlación negativa perfecta) a 1 (correlación positiva perfecta), siendo 0 ninguna correlación. Almacenaremos todas nuestras correlaciones en un nuevo data frame llamado Correlations.

Primero, usa select de dplyr para hacer una subconjunto de los datos con la variable de respuesta (actividad) y las 5 variables predictoras.

library(dplyr)

Bats_subset <- select(Bats, Activity, Area.thinned:Distance.creek.water)

A continuación, crea un data frame vacío con dos columnas (el nombre de la variable y la correlación) y el número de filas necesario para almacenar todas las correlaciones.

rows <- ncol(Bats_subset) - 1 # the number of rows needed in our output dataframe

Correlations <- data.frame(
  variable = character(length = rows),
  correlation = numeric(length = rows),
  stringsAsFactors = F
)

Finalmente, podemos utilizar un bucle para calcular cada una de las correlaciones y almacenar la salida en nuestro nuevo data frame.

for (i in 1:rows) {
  temp1 <- colnames(Bats_subset[i + 1]) # retrieves the name of predictor variable
  temp2 <- cor(Bats_subset[, 1], Bats_subset[, i + 1], method = "pearson")
  # calculates the correlation between activity and predictor variable
  Correlations[i, 1] <- temp1 # places the variable name into row i, column 1
  Correlations[i, 2] <- temp2 # places the correlation into row i, column 2
}
               variable correlation
1          Area.thinned -0.40890389
2    Time.since.thinned -0.02135752
3     Exclusion.thinned  0.17562438
4 Distance.murray.water -0.18071570
5  Distance.creek.water -0.09130258

Ahora podemos ver de un vistazo que la actividad está más fuertemente correlacionada (negativamente) con el área adelgazada y que no está correlacionada en absoluto con el tiempo desde el adelgazamiento o la temperatura media. Es posible que luego deseemos investigar más a fondo algunas de estas relaciones con modelos y pruebas estadísticas apropiadas.

Ayuda adicional

Tutorial de DataCamp sobre bucles

Puedes encontrar más ejemplos buenos de bucles, listas y declaraciones if/else en el sitio GitHub del grupo de usuarios de R de BEES Bucles y listas por Mitch.

Autor: Rachel V. Blakey

Año: 2016

Última actualización: Nov. 2023