Iteraciones (FOR LOOPS)

El uso de funciones personalizadas y loops son herramientas utiles para reducir la duplicación de código.

Las iteraciones (loops) son de gran utilidad cuando necesitamos hacer la misma tarea con multiples entradas; repetir la misma operación en diferentes columnas o en diferentes conjuntos de datos.

for loops corren por un número determinado de pasos, los cuales son definidos por el usuario, durante el cual se ejecuta algún comando

**¿cuando usamos un for loop?

  • Cuando tenemos un proceso repetido, con formato identico, pero con diferentes valores
  • Evitar script laborioso

La sintaxis básica es:

for (alguna secuenca) {
  
  ejecuta algún comando
}

Esto implica:

for (i in 1:5){ + i inicia en 1 y ejecuta un comando + i se incrementa a i = 2 y el comando se ejecuta nuevamento + i se incrementa a i = 3 y el comando se ejecuta nuevamente + asi sucesivamente hasta que i = 5 }

Por ejemplo:

for (i in 1:5){
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5

o elevar un número al cuadrado

for (i in 1:5){
print(i ^ 2)  
}
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25

tambien es posible hacer la iteración en orden inverso:

for(i in 25:1){
  print(i)
}
## [1] 25
## [1] 24
## [1] 23
## [1] 22
## [1] 21
## [1] 20
## [1] 19
## [1] 18
## [1] 17
## [1] 16
## [1] 15
## [1] 14
## [1] 13
## [1] 12
## [1] 11
## [1] 10
## [1] 9
## [1] 8
## [1] 7
## [1] 6
## [1] 5
## [1] 4
## [1] 3
## [1] 2
## [1] 1

Con esto, es posible usar un loop para hacer operaciones. Por ejemplo mutiplicar una serie de números a x = 5

x <- 5

for(i in 1:10){
  y = x * i
  print(y)
}
## [1] 5
## [1] 10
## [1] 15
## [1] 20
## [1] 25
## [1] 30
## [1] 35
## [1] 40
## [1] 45
## [1] 50

For Looops y vectores

En el ejemplo anterior, utilzamos idirectamente en una operación matematica, pero tammbien es posible hacer una iteración sobre los elementos de un vector

alumnos <- c("Carmen", "Adriana", "Marco", "Fabi", "Brenda")

for(i in 1:length(alumnos)) { 
  print(paste("Hola,", alumnos[i], sep = " "))
  
  }
## [1] "Hola, Carmen"
## [1] "Hola, Adriana"
## [1] "Hola, Marco"
## [1] "Hola, Fabi"
## [1] "Hola, Brenda"
df <- data.frame(
  a = rnorm(10),
  b = rnorm(10),
  c = rnorm(10),
  d = rnorm(10)
)

Esto nos imprimira en pantalla los resultados de la mediana de cada columna

for(i in 1:ncol(df)){
  promedio = median(df[,i])
  print(promedio)
}
## [1] -0.05631079
## [1] 0.3845669
## [1] -0.4035695
## [1] -1.218178

Es mas util poder guardar el resultado de cada iteración, por lo que antes de hacer el for loop, es posible hacer una tabla o vector donde guardar los resutados.

medianas <- data.frame(col = c("a", "b", "c", "d"),
                       mediana = NA)
medianas
##   col mediana
## 1   a      NA
## 2   b      NA
## 3   c      NA
## 4   d      NA

Ahora que tenemos una tabla, es posible meter el resultado en esta tabla

for(i in 1:ncol(df)){
  promedio = median(df[,i])
  medianas[i,2] = promedio
}

medianas
##   col     mediana
## 1   a -0.05631079
## 2   b  0.38456694
## 3   c -0.40356951
## 4   d -1.21817806

Ahora, usemos nuestra base de mediciones

library(tidyverse)
## -- Attaching packages ---------------------------------------------------------------------------- tidyverse 1.3.0 --
## v ggplot2 3.3.1     v purrr   0.3.4
## v tibble  3.0.1     v dplyr   1.0.0
## v tidyr   1.1.0     v stringr 1.4.0
## v readr   1.3.1     v forcats 0.5.0
## -- Conflicts ------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
mediciones <- read_csv("data/Tabla_Colecta_estaciones.csv")
## Parsed with column specification:
## cols(
##   Individuo = col_double(),
##   Sitio = col_character(),
##   Estacion = col_character(),
##   Sexo = col_character(),
##   Profundidad = col_character(),
##   Longitud_total = col_double(),
##   Longitud_parcial = col_double(),
##   anchura = col_double()
## )

Con esto, podemos usar un for loop para producir una grafica de disperción con las lontitudes totales

#¿cuantos sitios hay?
sitios <- unique(mediciones$Sitio)

#generar el for loop
for(i in 1:length(sitios)){
  #subconjunto de cada sitio
  sub_sitio <- subset(mediciones, 
                      mediciones$Sitio == sitios[i])
  plot <- ggplot(sub_sitio, aes(x = Estacion, y = Longitud_total))+
    geom_boxplot()+
    facet_grid(.~ Profundidad)+
    labs(title = sitios[i],
         y = "longitud total (mm)",
         x = "Estacion")
  print(plot)
  
  #ggsave(paste0(sitios[i], ".pdf"), plot = plot)
  
}

Datos de temperatura

Una utilizada de hace for loops es para unir una gran cantidad de datos, como por ejemplo, los que se producen con un sensor de temperatura, donde usualmente se generan muchos archivos con la misma estructura.

archivos <- list.files("data/sensor_temperatura/")
archivos
##  [1] "2018-02-27 070700Z.txt" "2018-02-28 073700Z.txt" "2018-03-01 080700Z.txt"
##  [4] "2018-03-02 083700Z.txt" "2018-03-03 090700Z.txt" "2018-03-04 093700Z.txt"
##  [7] "2018-03-05 100700Z.txt" "2018-03-06 103700Z.txt" "2018-03-07 110700Z.txt"
## [10] "2018-03-08 113700Z.txt" "2018-03-09 120700Z.txt" "2018-03-10 123700Z.txt"
## [13] "2018-03-11 130700Z.txt" "2018-03-12 133700Z.txt" "2018-03-13 140700Z.txt"
## [16] "2018-03-14 143700Z.txt" "2018-03-15 150700Z.txt" "2018-03-16 153700Z.txt"
## [19] "2018-03-17 160700Z.txt" "2018-03-18 163700Z.txt" "2018-03-19 170700Z.txt"
## [22] "2018-03-20 173700Z.txt" "2018-03-21 180700Z.txt" "2018-03-22 183700Z.txt"
## [25] "2018-03-23 190700Z.txt" "2018-03-24 193700Z.txt" "2018-03-25 200700Z.txt"
## [28] "2018-03-26 203700Z.txt" "2018-03-27 210700Z.txt" "2018-03-28 213700Z.txt"
## [31] "2018-03-29 220700Z.txt" "2018-03-30 223700Z.txt" "2018-03-31 230700Z.txt"
## [34] "2018-04-01 233700Z.txt" "2018-04-03 000700Z.txt" "2018-04-04 003700Z.txt"
## [37] "2018-04-05 010700Z.txt" "2018-04-06 013700Z.txt" "2018-04-07 020700Z.txt"
## [40] "2018-04-08 023700Z.txt" "2018-04-09 030700Z.txt" "2018-04-10 033700Z.txt"
## [43] "2018-04-11 040700Z.txt" "2018-04-12 043700Z.txt" "2018-04-13 050700Z.txt"
## [46] "2018-04-14 053700Z.txt" "2018-04-15 060700Z.txt" "2018-04-16 063700Z.txt"
## [49] "2018-04-17 070700Z.txt" "2018-04-18 073700Z.txt" "2018-04-19 080700Z.txt"
## [52] "2018-04-20 083700Z.txt" "2018-04-21 090700Z.txt" "2018-04-22 093700Z.txt"
## [55] "2018-04-23 100700Z.txt" "2018-04-24 103700Z.txt" "2018-04-25 110700Z.txt"
## [58] "2018-04-26 113700Z.txt" "2018-04-27 120700Z.txt" "2018-04-28 123700Z.txt"
## [61] "2018-04-29 130700Z.txt" "2018-04-30 133700Z.txt" "2018-05-01 140700Z.txt"
## [64] "2018-05-02 143700Z.txt" "2018-05-03 150700Z.txt" "2018-05-04 153700Z.txt"
## [67] "2018-05-05 160700Z.txt" "2018-05-06 163700Z.txt" "2018-05-07 170700Z.txt"
## [70] "2018-05-08 173700Z.txt" "2018-05-09 180700Z.txt" "2018-05-10 183700Z.txt"
## [73] "2018-05-11 190700Z.txt" "2018-05-12 193700Z.txt" "2018-05-13 200700Z.txt"
## [76] "2018-05-14 203700Z.txt" "2018-05-15 210700Z.txt" "2018-05-16 213700Z.txt"
## [79] "2018-05-17 220700Z.txt" "2018-05-18 223700Z.txt" "2018-05-19 230700Z.txt"
## [82] "2018-05-20 233700Z.txt" "2018-05-22 000700Z.txt" "2018-05-23 003700Z.txt"
## [85] "2018-05-24 010700Z.txt" "2018-05-25 013700Z.txt" "2018-05-26 020700Z.txt"
## [88] "2018-05-27 023700Z.txt" "2018-05-28 030700Z.txt" "2018-05-29 033700Z.txt"
## [91] "2018-05-30 040700Z.txt" "2018-05-31 043700Z.txt" "2018-06-01 050700Z.txt"
## [94] "2018-06-02 053700Z.txt" "2018-06-03 060700Z.txt" "2018-06-04 063700Z.txt"
## [97] "2018-06-05 070700Z.txt" "2018-06-06 073700Z.txt"

Exploremos el priermo de estos archivos

archivo_1 <- read.table(paste0("data/sensor_temperatura/",archivos[1]), sep = ",", h =TRUE, skip = 2)
head(archivo_1)
##   Time..sec. BV..Volts. T..deg.C. DO..mg.l.  Q...
## 1 1519715220       3.35    20.318     8.813 1.000
## 2 1519717020       3.36    20.580     8.769 1.001
## 3 1519718820       3.36    20.605     8.790 1.007
## 4 1519720620       3.36    20.554     8.780 1.009
## 5 1519722420       3.37    20.428     8.809 1.011
## 6 1519724220       3.37    20.369     8.831 1.015

AL igual que en el caso anterior, primero vamos a generar una tabla vacia, que tenga el mismo nombre de las columnas

tabla_sensor <- data.frame(Tiempo = NA, Volts = NA, T_gC = NA, Omg = NA, Q = NA)

Y ahora generamos el loop

for (i in 1:length(archivos)){
  df <- read.table(paste0("data/sensor_temperatura/",archivos[i]), sep = ",", h =TRUE, skip = 2)
  colnames(df) <- colnames(tabla_sensor)
  tabla_sensor <- rbind(tabla_sensor, df)
}

Verificamos los datos:

ggplot(tabla_sensor, aes(x = Tiempo, y = T_gC))+
  geom_point()+
  geom_smooth(se = TRUE)
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
## Warning: Removed 1 rows containing non-finite values (stat_smooth).
## Warning: Removed 1 rows containing missing values (geom_point).

Funciones

Las funciones se pueden generar según las necesidades del usuario. Para esto, es necesario especificar la palabra function.

La estructura basica de una funcion es:

function_name <- function(argumento1, argumento2) {
  cuerpo de la función
  return
}

Entonces podemos crear una funcion de la forma:

f <- function(x,y){
  d = x + y
}

y aplicamos la funcion

prueba <- f(x = 2, y = 4)
prueba
## [1] 6

Podemos asignarle un valor por default a uno de nuestros parametros, de manera que si nosotors no lo especificamos, se usara el valor defaul

g <- function(x, y = 5){
  d = x + y
}

Entonces podemos aplicar esta función definiendo ambos parametros

prueba2 <- g(10, 2)
prueba2
## [1] 12

o definiendo solo uno

prueba2 <- g(10)
prueba2
## [1] 15

Cuando utilizamos varias operaciones en una función, automaticamente se despliega la ultima linea, pero es posible seleccionar una linea en especifico utilizando return(). Por ejemplo:

h <- function(x, y){
  a <- x + y
  b <- x * y
  c <- x - y
}
res <- h(2,3)
res
## [1] -1

Pero si utilizamos return() en otro objeto

h <- function(x, y){
  a <- x + y
  b <- x * y
  c <- x - y
  return(a)
}
res <- h(2,3)
res
## [1] 5

Uso de funciones personalizadas

Ahora generemos una función para extraer cierta información de una base de datos.

Basandonos en la estructura de subset(x, x$y=z), hagamos una funcion para hacer un subconjunto de datos de la tabla mediciones por sitio

subtabla <- function(z){
  tabla <- subset(mediciones, mediciones$Sitio == z)
  return(tabla)
}
LAP <- subtabla("Bahia La Paz")
LAP
## # A tibble: 48 x 8
##    Individuo Sitio Estacion Sexo  Profundidad Longitud_total Longitud_parcial
##        <dbl> <chr> <chr>    <chr> <chr>                <dbl>            <dbl>
##  1         1 Bahi~ Verano   H     Superficie            30.3             22.4
##  2         2 Bahi~ Verano   M     Superficie            35.5             26.3
##  3         3 Bahi~ Verano   H     Superficie            30.4             22.5
##  4         4 Bahi~ Verano   H     Superficie            24.9             18.4
##  5         5 Bahi~ Verano   M     Superficie            31.8             23.6
##  6         6 Bahi~ Verano   H     Superficie            29.5             21.9
##  7         7 Bahi~ Verano   H     Superficie            26.6             19.7
##  8         8 Bahi~ Verano   M     Superficie            26.2             19.4
##  9         9 Bahi~ Verano   H     Superficie            29.4             21.7
## 10        10 Bahi~ Verano   M     Superficie            34.4             25.4
## # ... with 38 more rows, and 1 more variable: anchura <dbl>

Podemos expandir la funcion para que nos genere el subset de datos y haga una grafica

subtabla <- function(z){
  tabla <- subset(mediciones, mediciones$Sitio == z)
  plot <- ggplot(tabla, aes(x = Profundidad, y = Longitud_total))+
    geom_boxplot()+
    labs(title = z)
  return(plot)
}
LAP <- subtabla("Bahia La Paz")
LAP

y por que no! una función que incluye tanto la tabla como la gráfica en forma de una lista.

subtabla <- function(z){
  tabla <- subset(mediciones, mediciones$Sitio == z)
  plot <- ggplot(tabla, aes(x = Profundidad, y = Longitud_total))+
    geom_boxplot()+
    labs(title = z)
  resultado <- list(tabla, plot)
} 
LAP <- subtabla("Bahia La Paz")
LAP
## [[1]]
## # A tibble: 48 x 8
##    Individuo Sitio Estacion Sexo  Profundidad Longitud_total Longitud_parcial
##        <dbl> <chr> <chr>    <chr> <chr>                <dbl>            <dbl>
##  1         1 Bahi~ Verano   H     Superficie            30.3             22.4
##  2         2 Bahi~ Verano   M     Superficie            35.5             26.3
##  3         3 Bahi~ Verano   H     Superficie            30.4             22.5
##  4         4 Bahi~ Verano   H     Superficie            24.9             18.4
##  5         5 Bahi~ Verano   M     Superficie            31.8             23.6
##  6         6 Bahi~ Verano   H     Superficie            29.5             21.9
##  7         7 Bahi~ Verano   H     Superficie            26.6             19.7
##  8         8 Bahi~ Verano   M     Superficie            26.2             19.4
##  9         9 Bahi~ Verano   H     Superficie            29.4             21.7
## 10        10 Bahi~ Verano   M     Superficie            34.4             25.4
## # ... with 38 more rows, and 1 more variable: anchura <dbl>
## 
## [[2]]

Finalemnte, podemos usar funciones para personalizas un theme()dentro de ggplot, de manera que podamos generar con menos lineas un patrón uniforme de gráficas

theme_acui <- function () { 
    theme_bw(base_size=12)+ 
        theme(
            panel.background  = element_rect(fill="lightblue", colour=NA),
            plot.background = element_blank(), 
            axis.title = element_text(size = 14),
            panel.grid.minor = element_blank(),
            strip.text = element_text(size = 14, colour = "grey25"),
            strip.background = element_rect(fill = "grey97")
        )
}
ggplot(mediciones, aes(x = Profundidad, y = Longitud_total))+
  geom_boxplot()+
  facet_grid(.~ Sitio)+
  theme_acui()

ggplot(mediciones, aes(x = Longitud_parcial, y = Longitud_total, col = Sitio))+
  geom_point()+
  theme_acui()