2 tipos básicos:
while
yrepeat
)
* Modificado de Tutorial de bucles Datacamp
for
)(X) aplica
)
Nota: cuando el resultado de una iteración es completamente independiente de otras iteraciones, la tarea podría ejecutarse en paralelo. Hablaremos sobre computación paralela durante la clase de “codigo eficiente”
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:
# set default value as 0
corr_coef <- 0
# initiate loop
while(corr_coef < 0.5) {
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# print result
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
:
# set default value as 0
corr_coef <- 0
# create empty vector
cc_vector <- NULL
while(corr_coef < 0.5) {
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using append
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:
# set default value as 0
corr_coef <- 0
# create empty vector
cc_vector <- NULL
while(corr_coef < 0.5) {
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using append
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:
# set default value as 0
corr_coef <- 0
# create empty vector
cc_vector <- NULL
while(corr_coef < 0.5 & corr_coef > -0.5) {
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using append
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:
# create empty vector
cc_vector <- NULL
repeat
{
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using 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
:
# create empty vector
cc_vector <- NULL
# set number of iterations
reps <- 30
# initiate loop
for(i in 1:reps)
{
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using append
cc_vector[length(cc_vector) + 1] <- corr_coef
}
los bucles for
se pueden convertir en bucles condicionales usando break
:
# create empty vector
cc_vector <- NULL
# set number of iterations
reps <- 100
# initiate loop
for(i in 1:reps)
{
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using append
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:
# create empty vector
cc_vector <- NULL
# set number of iterations
reps <- 100
# initiate loop
for(i in 1:reps)
{
set.seed(i)
# generate variable 1
v1 <- rnorm(n = 20, mean = 100, sd = 20)
set.seed(i + 10)
# generate variable 2
v2 <- rnorm(n = 20, mean = 100, sd = 20)
# run correlation
corr_coef <- cor(v1, v2)
# store results using append
cc_vector[length(cc_vector) + 1] <- corr_coef
# calculate absolute difference only after first iteration
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
}
La utilidad de los bucles for
para la gestión de datos se entiende mejor cuando se aplica a conjuntos de datos. En este ejemplo examinamos los juegos de datos de ejemplo en R
:
# read example data set names
dt_sets <- data()$results[,3]
# remove the ones with spaces in their names
dt_sets <- grep(" ", dt_sets, value = TRUE, invert = TRUE)
# create empty vector
is_df <- NULL
# run for loop over each element
for(i in 1:length(dt_sets))
is_df[i] <- is.data.frame(get(dt_sets[i]))
# put results in a data frame
df <- data.frame(dt_sets, is_df, stringsAsFactors = FALSE)
# check firs 13 rows
head(df, 13)
dt_sets | is_df |
---|---|
diamonds | TRUE |
economics | TRUE |
economics_long | TRUE |
faithfuld | TRUE |
luv_colours | TRUE |
midwest | TRUE |
mpg | TRUE |
msleep | TRUE |
presidential | TRUE |
seals | TRUE |
txhousing | TRUE |
AirPassengers | FALSE |
BJsales | FALSE |
Note el uso de la funcion get
, que permite leer el nombre del juego de datos como si fuera un objeto en R
:
que estos 3 bucles son equivalentes:
# 1
for(i in 1:length(dt_sets))
is_df[i] <- is.data.frame(get(dt_sets[i]))
# 2
for(i in 1:length(dt_sets)) {
is_df[i] <- is.data.frame(get(dt_sets[i]))
}
# 3
for(i in 1:length(dt_sets)) {
x <- get(dt_sets[i])
is_df[i] <- is.data.frame(x)
}
Las llaves solo se necesitan cuando la ‘acción’ a realizar requiere más de una línea de código.
Podemos usar el juego de datos recién creado para excluir objetos que no son ‘data.frames’:
# remove non-data frames
df <- df[df$is_df, ]
# check firs 13 rows
head(df, 13)
dt_sets | is_df |
---|---|
diamonds | TRUE |
economics | TRUE |
economics_long | TRUE |
faithfuld | TRUE |
luv_colours | TRUE |
midwest | TRUE |
mpg | TRUE |
msleep | TRUE |
presidential | TRUE |
seals | TRUE |
txhousing | TRUE |
BOD | TRUE |
CO2 | TRUE |
Con este nuevo juego de datos podemos explorar más a fondo la estructura de los conjuntos de datos de ejemplo usando bucles for
.
Ejercicio 3
3.1 Haga un bucle for
que devuelva el número de columnas para cada juego de datos de ejemplo (recuerde usar la funcion get
)
3.2 Haga un bucle for
que devuelva el número de filas para cada juego de datos de ejemplo
3.3 Haga un bucle for
que devuelva el número de filas y columnas para cada juego de datos de ejemplo
3.4 El juego de datos de ejemplo “ChickWeight” describe el “peso versus la edad de los pollitos en diferentes dietas”:
# load data
data("ChickWeight")
# Convert to a regular data frame
ChickWeight <- data.frame(ChickWeight, stringsAsFactors = FALSE)
# check first rows
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 |
# see structure
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 ...
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:
df_list <- lapply(X = df$dt_sets, FUN = get)
class(df_list)
## [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 = df_list, FUN = nrow)
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):
# function outside loop
dims <- function(x) c(nrow(x), ncol(x))
#run loop
df_dims <- lapply(X = df_list, FUN = dims)
#check results
head(df_dims, 3)
## [[1]]
## [1] 53940 10
##
## [[2]]
## [1] 574 6
##
## [[3]]
## [1] 2870 4
# function inside loop and run loop
df_dims <- lapply(X = df_list, FUN = function(x) c(nrow(x), ncol(x)))
#check results
head(df_dims, 3)
## [[1]]
## [1] 53940 10
##
## [[2]]
## [1] 574 6
##
## [[3]]
## [1] 2870 4
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’.
Ejercicios extra
E.1 Reúna los resultados del ejercicio 3.4 en un nuevo juego de datos con columnas para ‘chick’ y ‘correlation’ usando un bucle for
(consejo: use rbind
)
E.2 ¿Cuántos de los juego de datos de ejemplo contenían una columna que era un factor?
E.3 Calcule el coeficiente de variación de cada variable numérica por especie en el juego de datos ‘iris’ usando ‘tapply’.
Session information
## R version 3.6.1 (2019-07-05)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.3 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=es_CR.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=es_CR.UTF-8 LC_MESSAGES=en_US.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.2.1 RColorBrewer_1.1-2 kableExtra_1.1.0
## [4] knitr_1.24
##
## loaded via a namespace (and not attached):
## [1] Rcpp_1.0.2 pillar_1.4.2 compiler_3.6.1
## [4] tools_3.6.1 zeallot_0.1.0 digest_0.6.20
## [7] evaluate_0.14 tibble_2.1.3 gtable_0.3.0
## [10] viridisLite_0.3.0 pkgconfig_2.0.2 rlang_0.4.0
## [13] rstudioapi_0.10 yaml_2.2.0 xfun_0.9
## [16] withr_2.1.2 dplyr_0.8.3 stringr_1.4.0
## [19] httr_1.4.1 xml2_1.2.2 vctrs_0.2.0
## [22] hms_0.5.1 tidyselect_0.2.5 webshot_0.5.1
## [25] grid_3.6.1 glue_1.3.1 R6_2.4.0
## [28] rmarkdown_1.15 purrr_0.3.2 readr_1.3.1
## [31] magrittr_1.5 backports_1.1.4 scales_1.0.0
## [34] htmltools_0.3.6 assertthat_0.2.1 rvest_0.3.4
## [37] colorspace_1.4-1 labeling_0.3 stringi_1.4.3
## [40] lazyeval_0.2.2 munsell_0.5.0 crayon_1.3.4