2 tipos básicos:
while
yrepeat
)
* Modificado de Tutorial de bucles Datacamp
for
)(X) aplica
)
Los bucles while
aplican una acción (1 o más funciones) en una secuencia de elementos hasta que se cumpla una condición. La condición puede evaluar un resultado del propio ciclo o una entrada externa:
El siguiente ciclo while
se ejecuta hasta que la correlación de las variables continuas generadas al azar es mayor que un umbral:
# determinar valor por defecto 0
corr_coef <- 0
# iniciar bucle
while(corr_coef < 0.5) {
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# imprimir resultado
print(corr_coef)
}
corr_coef
## [1] 0.0872575 0.3865472 0.1199017 -0.2902941 0.2413406 -0.1667497
## [7] 0.1272541 0.0011283 -0.1344395 -0.0184824 0.5966901
Para guardar cada uno de los resultados hay 2 opciones:
append
Usando append
:
# determinar valor por defecto 0
corr_coef <- 0
# crear vector vacio
cc_vector <- NULL
while(corr_coef < 0.5) {
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# guardar resultado
cc_vector <- append(cc_vector, corr_coef)
}
head(cc_vector)
## [1] -0.047693 0.232015 0.133767 -0.089900 0.364152 0.203306
Usando indexación:
# determinar valor por defecto 0
corr_coef <- 0
# crear vector vacio
cc_vector <- NULL
while(corr_coef < 0.5) {
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# guardar resultado
cc_vector[length(cc_vector) + 1] <- corr_coef
}
head(cc_vector)
## [1] 0.239060 0.212589 0.392069 0.132913 -0.093418 -0.320691
Pero tenga en cuenta que append
puede ser muy lento (no recomendado)
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:
# determinar valor por defecto 0
corr_coef <- 0
# crear vector vacio
cc_vector <- NULL
while(corr_coef < 0.5 & corr_coef > -0.5) {
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlacion
corr_coef <- cor(v1, v2)
# guardar resultado
cc_vector[length(cc_vector) + 1] <- corr_coef
}
head(cc_vector)
## [1] 0.323266 -0.153637 -0.049445 0.154694 0.191022 -0.168300
Ejercicio 1
1.1 Haga un bucle while
que se detenga solo si la correlación es mayor que 0.5 pero menor que 0.55
1.2 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
)
Los bucles repeat
también deben cumplir una condición para detenerse. Muy parecido a los bucles while
. Sin embargo, se realiza para que la acción se ejecute al menos una vez, independientemente de la evaluación de la condición
El siguiente bucle repeat
hace lo mismo que el buclewhile
anterior:
# crear vector vacio
cc_vector <- NULL
repeat
{
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# guardar resultado con append
cc_vector[length(cc_vector) + 1] <- corr_coef
if (corr_coef > 0.5) break
}
head(cc_vector)
## [1] 0.2327630 0.2225129 0.3036371 -0.0098055 0.0495164 -0.1844224
Tenga en cuenta que en este caso la condición determina si el ciclo debe detenerse. En el ciclo while
la condición determina si el ciclo debe continuar.
Ejercicio 2
2.1 Convierta en un bucle repeat
el bucle while
del ejercicio 1.2
Por mucho, for
es el bucle más popular. El número de iteraciones se puede arreglar y conocer de antemano:
Nuevamente, creamos un bucle que calcula las correlaciones entre variables aleatorias, en este caso usando un bucle for
:
# crear vector vacio
cc_vector <- NULL
# definir numero de iteraciones
reps <- 30
# iniciar bucle
for(i in 1:reps)
{
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# guardar resultado
cc_vector[length(cc_vector) + 1] <- corr_coef
}
los bucles for
se pueden convertir en bucles condicionales usando break
:
# crear vector vacio
cc_vector <- NULL
# definir iteraciones
reps <- 100
# iniciar bucle
for(i in 1:reps)
{
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# guardar resultado
cc_vector[length(cc_vector) + 1] <- corr_coef
# set conditional stop
if(corr_coef > 0.5 | corr_coef < -0.5) break
}
De esta manera podemos controlar el número máximo de iteraciones mientras seguimos aplicando un umbral condicional.
El operador de control next
también se puede usar para omitir una iteración basada en una condición.
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
). Por ejemplo, el siguiente bucle for
también se detiene si la diferencia (absoluta) entre la correlación actual y la de la iteración anterior es superior a 0.6:
# crear vector vacio
cc_vector <- NULL
# definir iteraciones
reps <- 100
# iniciar bucle
for(i in 1:reps)
{
set.seed(i)
# generar variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
set.seed(i + 10)
# generar variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# correr correlacion
corr_coef <- cor(v1, v2)
# guardar resultado
cc_vector[length(cc_vector) + 1] <- corr_coef
# calcular diferencia absoluta solo luego de la primer iteracion
if (i == 1) abs_diff <- 0 else
abs_diff <- abs(cc_vector[length(cc_vector) - 1] - corr_coef)
# set conditional stop
if (corr_coef > 0.5 | corr_coef < -0.5 | abs_diff > 0.6) break
}
Ejercicio 3
El juego de datos de ejemplo “ChickWeight” describe el “peso versus la edad de los pollitos en diferentes dietas”:
# load data
data("ChickWeight")
# Convertir a un data frame regular
ChickWeight <- data.frame(ChickWeight, stringsAsFactors = FALSE)
# revisar las primeras filas
head(ChickWeight)
weight | Time | Chick | Diet |
---|---|---|---|
42 | 0 | 1 | 1 |
51 | 2 | 1 | 1 |
59 | 4 | 1 | 1 |
64 | 6 | 1 | 1 |
76 | 8 | 1 | 1 |
93 | 10 | 1 | 1 |
# ver estructura
str(ChickWeight)
## 'data.frame': 578 obs. of 4 variables:
## $ weight: num 42 51 59 64 76 93 106 125 149 171 ...
## $ Time : num 0 2 4 6 8 10 12 14 16 18 ...
## $ Chick : Ord.factor w/ 50 levels "18"<"16"<"15"<..: 15 15 15 15 15 15 15 15 15 15 ...
## $ Diet : Factor w/ 4 levels "1","2","3","4": 1 1 1 1 1 1 1 1 1 1 ...
3.1 Usando los datos de ChickWeight, calcule la correlación entre el peso y la edad de cada pollito (consejo: (1) use unique (ChickWeight $ Chick)
dentro del inicio del bucle for
y (2) cree subconjuntos usando indexación dentro del cuerpo del bucle)
(X) apply
son funciones de nivel superior que toman una función como entrada y la aplican a una secuencia de objetos (vectores sensu lato). Bucles creados con (X)apply
. Hay varias funciones (X)apply
en R:
apropos("apply$")
## [1] "apply" "dendrapply" "eapply" "kernapply" "lapply"
## [6] "mapply" "rapply" "sapply" "tapply" "vapply"
Sin embargo, los más utilizados 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:
lista_datos <- list(iris, PlantGrowth, quakes)
df_nrow <- lapply(X = lista_datos, FUN = nrow)
df_nrow
## [[1]]
## [1] 150
##
## [[2]]
## [1] 30
##
## [[3]]
## [1] 1000
class(df_nrow)
## [1] "list"
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 puede empaquetarse como un vector):
df_nrow <- sapply(X = lista_datos, FUN = nrow)
df_nrow
## [1] 150 30 1000
class(df_nrow)
## [1] "integer"
apply
aplica una función a cada una de las filas o columnas de un objeto bidimensional:
head(iris, 4)
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | Species |
---|---|---|---|---|
5.1 | 3.5 | 1.4 | 0.2 | setosa |
4.9 | 3.0 | 1.4 | 0.2 | setosa |
4.7 | 3.2 | 1.3 | 0.2 | setosa |
4.6 | 3.1 | 1.5 | 0.2 | setosa |
apply(X = iris[1:10, -5],MARGIN = 1, FUN = sum)
## 1 2 3 4 5 6 7 8 9 10
## 10.2 9.5 9.4 9.4 10.2 11.4 9.7 10.1 8.9 9.6
apply(X = iris[1:10, -5], MARGIN = 1, FUN = mean)
## 1 2 3 4 5 6 7 8 9 10
## 2.550 2.375 2.350 2.350 2.550 2.850 2.425 2.525 2.225 2.400
apply(X = iris, MARGIN = 2, FUN = class)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## "character" "character" "character" "character" "character"
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:
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):
# funcion fuera del bucle
dims <- function(x) c(nrow(x), ncol(x))
# correr bucle
df_dims <- lapply(X = lista_datos, FUN = dims)
# ver resultados
head(df_dims, 3)
## [[1]]
## [1] 150 5
##
## [[2]]
## [1] 30 2
##
## [[3]]
## [1] 1000 5
# funcion dentro del loop
df_dims <- lapply(X = lista_datos, FUN = function(x) c(nrow(x), ncol(x)))
# ver resultados
head(df_dims, 3)
## [[1]]
## [1] 150 5
##
## [[2]]
## [1] 30 2
##
## [[3]]
## [1] 1000 5
Tenga en cuenta que:
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.
Ejercicio 4
4.1 Haga un bucle lapply
equivalente al bucle for
en el ejercicio 3.4 (utilizando los datos ‘ChickWeight’ calcule la correlación entre peso y tiempo para cada Chick)
4.2 Haga un bucle sapply
para calcular el mayor peso registrado para cada tipo de dieta (pista: unique(ChickWeight$Diet)
, deberia devolver un valor por tipo de dieta). Nombre el vector resultante para que contenga el identificador de cada dieta.
4.3 Haga un bucle apply
para calcular la media de cada variable numérica en el juego de datos ‘iris’.
Información de la sesión
## R version 4.0.2 (2020-06-22)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
##
## locale:
## [1] LC_CTYPE=pt_BR.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=es_CR.UTF-8 LC_COLLATE=pt_BR.UTF-8
## [5] LC_MONETARY=es_CR.UTF-8 LC_MESSAGES=pt_BR.UTF-8
## [7] LC_PAPER=es_CR.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=es_CR.UTF-8 LC_IDENTIFICATION=C
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] ggplot2_3.3.2 RColorBrewer_1.1-2 kableExtra_1.1.0 knitr_1.29
##
## loaded via a namespace (and not attached):
## [1] Rcpp_1.0.5 pillar_1.4.6 compiler_4.0.2 tools_4.0.2
## [5] digest_0.6.25 evaluate_0.14 lifecycle_0.2.0 tibble_3.0.3
## [9] gtable_0.3.0 viridisLite_0.3.0 pkgconfig_2.0.3 rlang_0.4.7
## [13] rstudioapi_0.11 yaml_2.2.1 xfun_0.16 withr_2.2.0
## [17] httr_1.4.2 stringr_1.4.0 dplyr_1.0.2 xml2_1.3.2
## [21] generics_0.0.2 vctrs_0.3.2 hms_0.5.3 tidyselect_1.1.0
## [25] webshot_0.5.2 grid_4.0.2 glue_1.4.1 R6_2.4.1
## [29] rmarkdown_2.3 farver_2.0.3 purrr_0.3.4 readr_1.3.1
## [33] magrittr_1.5 scales_1.1.1 ellipsis_0.3.1 htmltools_0.5.0
## [37] rvest_0.3.6 colorspace_1.4-1 labeling_0.3 stringi_1.4.6
## [41] munsell_0.5.0 crayon_1.3.4