11.3 Bucles y vectorización
11.3.1 Bucles
R
permite crear bucles repetitivos
(loops) y la ejecución condicional de sentencias. R
admite bucles
for
, repeat
and while
.
11.3.1.1 El bucle for
La sintaxis de un bucle for
es la que sigue:
for (i in lista_de_valores) { expresión }
Por ejemplo, dado un vector \(x\) se puede calcular \(y=x^2\) con el código:
<- seq(-2, 2, 0.5)
x <- length(x)
n <- numeric(n) # Es necesario crear el objeto para acceder a los componentes...
y for (i in 1:n) { y[i] <- x[i] ^ 2 }
x
## [1] -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0
y
## [1] 4.00 2.25 1.00 0.25 0.00 0.25 1.00 2.25 4.00
^2 x
## [1] 4.00 2.25 1.00 0.25 0.00 0.25 1.00 2.25 4.00
Otro ejemplo:
for(i in 1:5) print(i)
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
El siguiente código simula gráficamente el segundero de un reloj:
<- seq(0, 360, by = 6)
angulo <- angulo * pi / 180
radianes <- sin(radianes)
x <- cos(radianes)
y
<- seq(6, 61, by = 5)
sec for (i in 1:61) {
plot(x, y, axes = FALSE, xlab = "", ylab = "", type = 'l', col = 'grey')
points(x[i], y[i])
# Añadir "decoración"
text(x[sec]*0.9, y[sec]*0.9, labels = sec - 1)
arrows(0, 0, x[i]*0.85, y[i]*0.85, col = 'blue')
# Esperar un segundo
Sys.sleep(1)
}
11.3.1.2 El bucle while
La sintaxis del bucle while
es la que sigue:
while (condición lógica) { expresión }
Por ejemplo, si queremos calcular el primer número entero positivo cuyo cuadrado no excede de 5000, podemos hacer:
<- 0
cuadrado <- 0
n while (cuadrado <= 5000) {
<- n + 1
n <- n^2
cuadrado
} cuadrado
## [1] 5041
n
## [1] 71
^2 n
## [1] 5041
Nota: Dentro de un bucle se puede emplear el comando break
para terminarlo y el comando next
para saltar a la siguiente iteración.
11.3.2 Vectorización
Como hemos visto en R
se pueden
hacer bucles. Sin embargo, es preferible evitar este tipo de estructuras
y tratar de utilizar operaciones vectorizadas que son mucho más
eficientes desde el punto de vista computacional.
Por ejemplo para sumar dos vectores se puede hacer con un for
:
<- c(1, 2, 3, 4)
x <- c(0, 0, 5, 1)
y <- length(x)
n <- numeric(n)
z for (i in 1:n) {
<- x[i] + y[i]
z[i]
} z
## [1] 1 2 8 5
Sin embargo, la operación anterior se podría hacer de modo más eficiente en modo vectorial:
<- x + y
z z
## [1] 1 2 8 5
11.3.3 Funciones apply
11.3.3.1 La función apply
Una forma de evitar la
utilización de bucles es utilizando la función apply()
que permite
evaluar una misma función en todas las filas, columnas, …. de un array
de forma simultánea.
La sintaxis de esta función es:
apply(X, MARGIN, FUN, ...)
X
: matriz (o array)MARGIN
: Un vector indicando las dimensiones donde se aplicará la función. 1 indica filas, 2 indica columnas, yc(1,2)
indica filas y columnas.FUN
: función que será aplicada....
: argumentos opcionales que serán usados porFUN
.
Veamos la utilización de la función apply
con un ejemplo:
<- matrix(1:9, nrow = 3)
x x
## [,1] [,2] [,3]
## [1,] 1 4 7
## [2,] 2 5 8
## [3,] 3 6 9
apply(x, 1, sum) # Suma por filas
## [1] 12 15 18
apply(x, 2, sum) # Suma por columnas
## [1] 6 15 24
apply(x, 2, min) # Mínimo de las columnas
## [1] 1 4 7
apply(x, 2, range) # Rango (mínimo y máximo) de las columnas
## [,1] [,2] [,3]
## [1,] 1 4 7
## [2,] 3 6 9
11.3.3.2 La función tapply
La function tapply
es
similar a la función apply
y permite aplicar una función a los datos desagregados,
utilizando como criterio los distintos niveles de una variable factor.
La sintaxis de esta función es como sigue:
tapply(X, INDEX, FUN, ...,)
X
: matriz (o array).INDEX
: factor indicando los grupos (niveles).FUN
: función que será aplicada....
: argumentos opcionales .
Consideremos, por ejemplo, el data.frame ChickWeight
con datos de un
experimento relacionado con la repercusión de varias dietas en el peso
de pollos.
data(ChickWeight)
head(ChickWeight)
## weight Time Chick Diet
## 1 42 0 1 1
## 2 51 2 1 1
## 3 59 4 1 1
## 4 64 6 1 1
## 5 76 8 1 1
## 6 93 10 1 1
<- ChickWeight$weight
peso <- ChickWeight$Diet
dieta levels(dieta) <- c("Dieta 1", "Dieta 2", "Dieta 3", "Dieta 4")
tapply(peso, dieta, mean) # Peso medio por dieta
## Dieta 1 Dieta 2 Dieta 3 Dieta 4
## 102.6455 122.6167 142.9500 135.2627
tapply(peso, dieta, summary)
## $`Dieta 1`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 35.00 57.75 88.00 102.65 136.50 305.00
##
## $`Dieta 2`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 39.0 65.5 104.5 122.6 163.0 331.0
##
## $`Dieta 3`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 39.0 67.5 125.5 142.9 198.8 373.0
##
## $`Dieta 4`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 39.00 71.25 129.50 135.26 184.75 322.00
Otro ejemplo:
<- as.factor(c(1, 3, 4, 2, 4, 3, 2, 1, 4, 3, 2))
provincia levels(provincia) = c("A Coruña", "Lugo", "Orense", "Pontevedra")
<- c(1, 2, 0, 3, 4, 1, 0, 0, 2, 3, 1)
hijos data.frame(provincia, hijos)
## provincia hijos
## 1 A Coruña 1
## 2 Orense 2
## 3 Pontevedra 0
## 4 Lugo 3
## 5 Pontevedra 4
## 6 Orense 1
## 7 Lugo 0
## 8 A Coruña 0
## 9 Pontevedra 2
## 10 Orense 3
## 11 Lugo 1
tapply(hijos, provincia, mean) # Número medio de hijos por provincia
## A Coruña Lugo Orense Pontevedra
## 0.500000 1.333333 2.000000 2.000000