Los for() loops son una herramienta muy útil para
realizar tareas repetitivas (iterar) sistematicamente. Estas tareas
pueden ser realizadas a lo largo de los elementos de una lista, de un
data frame (filas o columnas), una matriz (filas o columnas), de una
lista de data frames o matrices, o simplemente de un número de
repeticiones dado.
Cuando se crea un loop, R va a ejecutar las instrucciones que le digamos
un número de veces especificado, o hasta que se cumpla cietra condición.
En esta oportunidad vamos a ver solamente los loops donde se especifíca
el número de repeticiones o ciclos.
for(i in 1:5){
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
for(i in 1:5){
x<-i*2
print(x)
}
## [1] 2
## [1] 4
## [1] 6
## [1] 8
## [1] 10
En estos dos ejemplos no hay un objeto externo, pero muchas veces necesitamos que, lo que en nuestro ejemplo fueron simplemente números enteros consecutivos (1,2,3,4,5), sean en verdad objetos externos. Veamos un ejemplo simple con los datos que se encuentran en el archivo “datos_loops.txt”. Imaginemos que cada columna (A a H) es una localidad diferente y que para cada localidad tenemos una muestra de la talla de 100 individuos.
Podemos calcular sistematicamente la media de cada localidad (aunque no es tan práctico, es sólo para ejemplificar…).
Primero cargamos los datos y exploramos su estructura…
datos<-read.table("datos_loops.txt", header=TRUE)
head(datos)
## A B C D E F G H
## 1 11.3 9.6 9.8 19.4 6.2 15.4 21.9 7.4
## 2 11.7 10.1 11.0 12.7 12.0 10.4 7.9 6.8
## 3 11.0 12.3 7.8 13.6 7.8 20.1 23.1 13.7
## 4 10.6 10.6 11.4 12.3 9.7 16.1 29.9 12.4
## 5 9.9 20.3 8.7 3.3 10.8 21.5 6.5 11.3
## 6 9.0 9.3 11.3 1.0 11.1 20.3 30.8 5.8
Son 8 columnas (localidades), tenemos que hacer un loop que tenga 8 ciclos y en cada ciclo calcule (por ejemplo) la media de una de las columnas:
for(i in 1:8){
print(mean(datos[,i]))
}
## [1] 9.82
## [1] 10.847
## [1] 10.231
## [1] 10.015
## [1] 9.701
## [1] 20.665
## [1] 20.296
## [1] 9.334
Como vemos, está haciendo la tarea pero solamente nos muestra (imprime) los resultados. Tal vez nos interece guardarlos. Hay diferentes maneras de conseguir el mismo resultado. Una es crear un objeto y llenarlo en cada vuelta.
medias<-data.frame(localidad=c("A", "B", "C", "D", "E", "F", "G", "H"), media=rep(NA, 8))
medias
## localidad media
## 1 A NA
## 2 B NA
## 3 C NA
## 4 D NA
## 5 E NA
## 6 F NA
## 7 G NA
## 8 H NA
La columna que tiene que tener las medias está temporalmente vacía. Tenemos que lograr que se llene con los valores calculados en cada ciclo.
for(i in 1:8){
medias$media[i]<-mean(datos[,i])
}
medias
## localidad media
## 1 A 9.820
## 2 B 10.847
## 3 C 10.231
## 4 D 10.015
## 5 E 9.701
## 6 F 20.665
## 7 G 20.296
## 8 H 9.334
Ahora medias tiene los valores que queríamos.
Podemos complejizar esto un poco…
estadisticos<-data.frame(localidad=c("A", "B", "C", "D", "E", "F", "G", "H"),
media=rep(NA, 8),
mediana=rep(NA, 8),
Q25=rep(NA, 8),
Q75=rep(NA, 8))
for(i in 1:8){
estadisticos$media[i]<-mean(datos[,i])
estadisticos$mediana[i]<-median(datos[,i])
estadisticos$Q25[i]<-quantile(datos[,i], 0.25)
estadisticos$Q75[i]<-quantile(datos[,i], 0.75)
}
estadisticos
## localidad media mediana Q25 Q75
## 1 A 9.820 10.30 9.075 11.100
## 2 B 10.847 10.10 9.000 11.850
## 3 C 10.231 10.25 9.000 11.225
## 4 D 10.015 10.20 5.550 13.725
## 5 E 9.701 9.75 7.350 12.050
## 6 F 20.665 20.90 17.700 23.800
## 7 G 20.296 19.15 15.200 25.525
## 8 H 9.334 8.50 6.300 12.450
Podríamos usar loops para graficar
par(mfrow=c(2,4))
for(i in 1:8){
hist(datos[,i])
}
Loops anidados: Muchas veces es necesario hacer una especie de mamushka de loops para realizar la tarea que necesitamos. Veamos la lógica de los loops anidados:
for (i in 1:3)
{
for (j in 1:3)
{
print(i * j)
}
}
## [1] 1
## [1] 2
## [1] 3
## [1] 2
## [1] 4
## [1] 6
## [1] 3
## [1] 6
## [1] 9
Veamos una posible aplicación usando los datos de las tallas y
localidades. Supongamos que queremos realizar un test de t para cada par
de localidades. Empecemos de a poco… Para realizar un test de t
comparando dos localidades podemos usar la función
t.test().
t.test(datos$A, datos$B, var.equal = F)
##
## Welch Two Sample t-test
##
## data: datos$A and datos$B
## t = -2.9444, df = 165.83, p-value = 0.003701
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -1.7156556 -0.3383444
## sample estimates:
## mean of x mean of y
## 9.820 10.847
Si (y ya van a ver porqué) de esta salida queremos quedarnos con la información que nos interesa, podemos hacerlo.
resultado<-t.test(datos$A, datos$B, var.equal = F)
str(resultado)
## List of 10
## $ statistic : Named num -2.94
## ..- attr(*, "names")= chr "t"
## $ parameter : Named num 166
## ..- attr(*, "names")= chr "df"
## $ p.value : num 0.0037
## $ conf.int : num [1:2] -1.716 -0.338
## ..- attr(*, "conf.level")= num 0.95
## $ estimate : Named num [1:2] 9.82 10.85
## ..- attr(*, "names")= chr [1:2] "mean of x" "mean of y"
## $ null.value : Named num 0
## ..- attr(*, "names")= chr "difference in means"
## $ stderr : num 0.349
## $ alternative: chr "two.sided"
## $ method : chr "Welch Two Sample t-test"
## $ data.name : chr "datos$A and datos$B"
## - attr(*, "class")= chr "htest"
resultado$statistic
## t
## -2.944404
resultado$p.value
## [1] 0.003700625
Listo, con lo que aprendimos antes podemos armar nuestro loop para realizar las comparaciones de a pares y guardar todo en una tabla
salida=NULL
for (i in 1:7)
{
for (j in (i+1):8)
{
resultados<-t.test(datos[,i], datos[,j])
comparacion<-paste(colnames(datos)[i], colnames(datos)[j])
t<-resultados$statistic
p<-resultados$p.value
salida_ij<-data.frame(comparacion, t, p)
salida<-rbind(salida, salida_ij)
}
}
salida
## comparacion t p
## t A B -2.9444041 3.700625e-03
## t1 A C -1.6232963 1.061232e-01
## t2 A D -0.3352866 7.379901e-01
## t3 A E 0.2850648 7.760030e-01
## t4 A F -23.0722658 1.704977e-48
## t5 A G -13.1220565 2.965652e-24
## t6 A H 1.3431479 1.811153e-01
## t7 B C 1.7956271 7.444353e-02
## t8 B D 1.3291481 1.857952e-01
## t9 B E 2.4008419 1.733246e-02
## t10 B F -18.7383846 1.065266e-43
## t11 B G -11.3674677 4.287396e-21
## t12 B H 3.5223363 5.312983e-04
## t13 C D 0.3735946 7.093742e-01
## t14 C E 1.2843389 2.011509e-01
## t15 C F -22.4001615 1.799799e-46
## t16 C G -12.6467166 4.234068e-23
## t17 C H 2.5175050 1.283444e-02
## t18 D E 0.4710044 6.382266e-01
## t19 D F -15.1970105 1.649135e-34
## t20 D G -10.7920374 3.181419e-21
## t21 D H 1.0752805 2.839067e-01
## t22 E F -19.1694426 1.191982e-46
## t23 E G -12.2869976 4.016009e-24
## t24 E H 0.7536831 4.519647e-01
## t25 F G 0.4150973 6.786448e-01
## t26 F H 21.2700668 5.515831e-51
## t27 G H 13.1001087 1.608314e-25
Los ciclos no necesariamente tienen que estar definidos con números consecutivos de 1 a n (en verdad va a terminar siendo lo mismo, pero podemos hacer referencia directa a objetos sin tener que pasar por números consecutivos).
for (i in c(-8, 9, 11, 45))
{
print(i)
}
## [1] -8
## [1] 9
## [1] 11
## [1] 45
x<- c(-8, 9, 11, 45)
for (i in x)
{
print(i+2)
}
## [1] -6
## [1] 11
## [1] 13
## [1] 47
Usando un ejemplo con una base de datos que se encuentra precargada en R:
data(iris)
head(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
En este caso, si nos interesa analizar por separado cada especie, la lógica es diferente al ejemplo de las tallas ya que no tenemos cada especie en una columna diferente sino que tenemos una columna que nos dice a qué especie pertenece cada dato. Podemos ver esta columna y crear un vector con sus “niveles” o posibles valores.
iris$Species
## [1] setosa setosa setosa setosa setosa setosa
## [7] setosa setosa setosa setosa setosa setosa
## [13] setosa setosa setosa setosa setosa setosa
## [19] setosa setosa setosa setosa setosa setosa
## [25] setosa setosa setosa setosa setosa setosa
## [31] setosa setosa setosa setosa setosa setosa
## [37] setosa setosa setosa setosa setosa setosa
## [43] setosa setosa setosa setosa setosa setosa
## [49] setosa setosa versicolor versicolor versicolor versicolor
## [55] versicolor versicolor versicolor versicolor versicolor versicolor
## [61] versicolor versicolor versicolor versicolor versicolor versicolor
## [67] versicolor versicolor versicolor versicolor versicolor versicolor
## [73] versicolor versicolor versicolor versicolor versicolor versicolor
## [79] versicolor versicolor versicolor versicolor versicolor versicolor
## [85] versicolor versicolor versicolor versicolor versicolor versicolor
## [91] versicolor versicolor versicolor versicolor versicolor versicolor
## [97] versicolor versicolor versicolor versicolor virginica virginica
## [103] virginica virginica virginica virginica virginica virginica
## [109] virginica virginica virginica virginica virginica virginica
## [115] virginica virginica virginica virginica virginica virginica
## [121] virginica virginica virginica virginica virginica virginica
## [127] virginica virginica virginica virginica virginica virginica
## [133] virginica virginica virginica virginica virginica virginica
## [139] virginica virginica virginica virginica virginica virginica
## [145] virginica virginica virginica virginica virginica virginica
## Levels: setosa versicolor virginica
spp<-unique(iris$Species)
Supongamos que queremos graficar la relación entre dos variables morfológicas para cada especie
par(mfrow=c(1,3))
for(i in spp){
datosXsp<-subset(iris, iris$Species==i)
plot(datosXsp$Sepal.Length,datosXsp$Petal.Length)
}