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?
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
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)
}
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).
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
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()