-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloops.qmd
613 lines (444 loc) · 29.6 KB
/
loops.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
# Bucles {#loops}
Los bucles son el método de R para repetir una tarea, lo que los convierte en una herramienta útil para programar simulaciones. Este capítulo le enseñará cómo usar las herramientas de bucle de R.
Usemos la función `puntuacion` para resolver un problema del mundo real.
Su máquina tragamonedas está modelada a partir de máquinas reales que fueron acusadas de fraude. Las máquinas parecían pagar 40 centavos por dólar, pero el fabricante afirmó que pagaban 92 centavos por dólar. Puede calcular la tasa de pago exacta de su máquina con el programa `puntuacion`. La tasa de pago será el valor esperado del premio de la máquina tragamonedas.
## Valores Esperados
El valor esperado de un evento aleatorio es un tipo de promedio ponderado; es la suma de cada resultado posible del evento, ponderada por la probabilidad de que ocurra cada resultado:
$$
E(x) = \sum_{i = 1}^{n}\left( x_{i} \cdot P(x_{i}) \right)
$$
Puede pensar en el valor esperado como el premio promedio que observaría si jugara en la máquina tragamonedas una cantidad infinita de veces. Usemos la fórmula para calcular algunos valores esperados simples. Luego aplicaremos la fórmula a su máquina tragamonedas.
¿Recuerdas el `dado` que creaste en [Proyecto 1: Dados Ponderados](#dice)?
``` r
dado <- c(1, 2, 3, 4, 5, 6)
```
Cada vez que lanzas el dado, devuelve un valor seleccionado al azar (de uno a seis). Puedes encontrar el valor esperado de tirar el dado con la fórmula:
$$
E(\text{dado}) = \sum_{i = 1}^{n}\left( \text{dado}_{i} \cdot P(\text{dado}_{i}) \right)
$$
Los $\text{dado}_{i}$ son los posibles resultados de lanzar el dado: 1, 2, 3, 4, 5 y 6; y los $P(\text{dado}_{i})$ son las probabilidades asociadas con cada uno de los resultados. Si su dado es justo, cada resultado ocurrirá con la misma probabilidad: 1/6. Entonces nuestra ecuación se simplifica a:
$$
\begin{array}{rl}
E(\text{dado}) & = \sum_{i = 1}^{n}\left( \text{dado}_{i} \cdot P(\text{dado}_{i}) \right)\\
& = 1 \cdot \frac{1}{6} + 2 \cdot \frac{1}{6} + 3 \cdot \frac{1}{6} + 4 \cdot \frac{1}{6} + 5 \cdot \frac{1}{6} + 6 \cdot \frac{1}{6}\\
& = 3.5\\
\end{array}
$$
Por lo tanto, el valor esperado de lanzar un dado justo es 3,5. Puede notar que este es también el valor promedio del dado. El valor esperado será igual al promedio si todos los resultados tienen la misma probabilidad de ocurrir.
Pero, ¿y si cada resultado tiene una probabilidad diferente de ocurrir? Por ejemplo, manipulamos nuestros dados en [Paquetes y Páginas de Ayuda](#packages) para que cada dado arrojara 1, 2, 3, 4 y 5 con una probabilidad de 1/8 y 6 con una probabilidad de 3/8. Puede usar la misma fórmula para calcular el valor esperado en estas condiciones:
$$
\begin{array}{rl}
E(dado) & = 1 \cdot \frac{1}{8} + 2 \cdot \frac{1}{8} + 3 \cdot \frac{1}{8} + 4 \cdot \frac{1}{8} + 5 \cdot \frac{1}{8} + 6 \cdot \frac{3}{8}\\
& = 4.125\\
\end{array}
$$
Por lo tanto, el valor esperado de un dado manipulado no es igual al valor promedio de un dado no manipulado. Si lanzaste un dado manipulado una cantidad infinita de veces, el resultado promedio sería 4.125, que es más alto de lo que esperarías de un dado justo.
Observe que hicimos las mismas tres cosas para calcular estos dos valores esperados. Tenemos:
- Enumeró todos los resultados posibles
- Determinado el *valor* de cada resultado (aquí solo el valor del dado)
- Calculó la probabilidad de que ocurriera cada resultado
Entonces, el valor esperado era solo la suma de los valores en el paso 2 multiplicada por las probabilidades en el paso 3.
Puede utilizar estos pasos para calcular valores esperados más sofisticados. Por ejemplo, podría calcular el valor esperado de lanzar un par de dados ponderados. Hagamos esto paso a paso.
Primero, enumere todos los resultados posibles. Pueden aparecer un total de 36 resultados diferentes cuando lanzas dos dados. Por ejemplo, puede sacar (1, 1), que anota uno en el primer dado y uno en el segundo dado. O bien, puede tirar (1, 2), uno en el primer dado y dos en el segundo. Y así. Enumerar estas combinaciones puede ser tedioso, pero R tiene una función que puede ayudar.
## expand.grid
La función `expand.grid` en R proporciona una forma rápida de escribir cada combinación de los elementos en *n* vectores. Por ejemplo, puede hacer una lista de todas las combinaciones de dos dados. Para hacerlo, ejecuta `expand.grid` en dos copias de `dado`:
``` r
tiradas <- expand.grid(dado, dado)
```
`expand.grid` devolverá un data frame que contiene todas las formas de emparejar un elemento del primer vector `dado` con un elemento del segundo vector `dado`. Esto capturará las 36 posibles combinaciones de valores:
``` r
tiradas
## Var1 Var2
## 1 1 1
## 2 2 1
## 3 3 1
## ...
## 34 4 6
## 35 5 6
## 36 6 6
```
Puede usar `expand.grid` con más de dos vectores si lo desea. Por ejemplo, podría enumerar cada combinación de tirar tres dados con `expand.grid(dado, dado, dado)` y cada combinación de tirar cuatro dados con `expand.grid(dado, dado, dado, dado)`, y así en. `expand.grid` siempre devolverá un data frame que contiene cada combinación posible de *n* elementos de los *n* vectores. Cada combinación contendrá exactamente un elemento de cada vector.
Puedes determinar el valor de cada tirada una vez que hayas hecho tu lista de resultados. Esta será la suma de los dos dados, que puedes calcular usando la ejecución por elementos de R:
``` r
tiradas$valor <- tiradas$Var1 + tiradas$Var2
head(tiradas, 3)
## Var1 Var2 valor
## 1 1 2
## 2 1 3
## 3 1 4
```
R emparejará los elementos en cada vector antes de agregarlos. Como resultado, cada elemento de `valor` se referirá a los elementos de `Var1` y `Var2` que aparecen en la misma fila.
A continuación, debes determinar la probabilidad de que aparezca cada combinación. Puedes calcular esto con una regla básica de probabilidad:
*La probabilidad de que* n *eventos aleatorios independientes ocurran todos es igual al producto de las probabilidades de que ocurra cada evento aleatorio*.
O más sucintamente:
$$
P(A \& B \& C \& ...) = P(A) \cdot P(B) \cdot P(C) \cdot ...
$$
Entonces, la probabilidad de que saquemos (1, 1) será igual a la probabilidad de que saquemos un uno en el primer dado, 1/8, multiplicada por la probabilidad de que saquemos un uno en el segundo dado, 1/8:
$$
\begin{array}{rl}
P(1 \& 1) & = P(1) \cdot P(1) \\
& = \frac{1}{8} \cdot \frac{1}{8}\\
& = \frac{1}{64}
\end{array}
$$
Y la probabilidad de que saquemos un (1, 2) será:
$$
\begin{array}{rl}
P(1 \& 2) & = P(1) \cdot P(2) \\
& = \frac{1}{8} \cdot \frac{1}{8}\\
& = \frac{1}{64}
\end{array}
$$
Y así sucesivamente.
Permítanme sugerir un proceso de tres pasos para calcular estas probabilidades en R. Primero, podemos buscar las probabilidades de rodar los valores en `Var1`. Haremos esto con la tabla de búsqueda que sigue:
``` r
prob <- c("1" = 1/8, "2" = 1/8, "3" = 1/8, "4" = 1/8, "5" = 1/8, "6" = 3/8)
prob
## 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375
```
Si crea un subconjunto de esta tabla con `tiradas$Var1`, obtendrá un vector de probabilidades perfectamente relacionado con los valores de `Var1`:
``` r
tiradas$Var1
## 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6
prob[tiradas$Var1]
## 1 2 3 4 5 6 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375 0.125 0.125 0.125 0.125 0.125 0.375
## 1 2 3 4 5 6 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375 0.125 0.125 0.125 0.125 0.125 0.375
## 1 2 3 4 5 6 1 2 3 4 5 6
## 0.125 0.125 0.125 0.125 0.125 0.375 0.125 0.125 0.125 0.125 0.125 0.375
tiradas$prob1 <- prob[tiradas$Var1]
head(tiradas, 3)
## Var1 Var2 valor prob1
## 1 1 2 0.125
## 2 1 3 0.125
## 3 1 4 0.125
```
Second, we can look up the probabilities of rolling the values in `Var2`:
``` r
tiradas$prob2 <- prob[tiradas$Var2]
head(tiradas, 3)
## Var1 Var2 valor prob1 prob2
## 1 1 2 0.125 0.125
## 2 1 3 0.125 0.125
## 3 1 4 0.125 0.125
```
Tercero, podemos calcular la probabilidad de obtener cada combinación multiplicando `prob1` por `prob2`:
``` r
tiradas$prob <- tiradas$prob1 * tiradas$prob2
head(tiradas, 3)
## Var1 Var2 valor prob1 prob2 prob
## 1 1 2 0.125 0.125 0.015625
## 2 1 3 0.125 0.125 0.015625
## 3 1 4 0.125 0.125 0.015625
```
Es fácil calcular el valor esperado ahora que tenemos cada resultado, el valor de cada resultado y la probabilidad de cada resultado. El valor esperado será la suma de los valores de los dados multiplicada por las probabilidades de los dados:
``` r
sum(tiradas$valor * tiradas$prob)
## 8.25
```
Entonces, el valor esperado de lanzar dos dados cargados es 8.25. Si lanzaste un par de dados cargados un número infinito de veces, la suma promedio sería 8.25. (Si tiene curiosidad, el valor esperado de lanzar un par de dados justos es 7, lo que explica por qué 7 juega un papel tan importante en juegos de dados como los dados).
Ahora que se ha calentado, usemos nuestro método para calcular el valor esperado del premio de la máquina tragamonedas. Seguiremos los mismos pasos que acabamos de hacer:
- Enumeraremos todos los resultados posibles de jugar en la máquina. Esta será una lista de cada combinación de tres símbolos de tragamonedas.
- Calcularemos la probabilidad de obtener cada combinación cuando juegues en la máquina.
- Determinaremos el premio que ganaríamos por cada combinación.
Cuando hayamos terminado, tendremos un conjunto de datos que se verá así:
``` r
## Var1 Var2 Var3 prob1 prob2 prob3 prob premio
## DD DD DD 0.03 0.03 0.03 0.000027 800
## 7 DD DD 0.03 0.03 0.03 0.000027 0
## BBB DD DD 0.06 0.03 0.03 0.000054 0
## ... y así sucesivamente.
```
El valor esperado será entonces la suma de los premios multiplicada por su probabilidad de ocurrencia:
$$
E(\text{premio}) = \sum_{i = 1}^{n}\left( \text{premio}_{i} \cdot P(\text{premio}_{i}) \right)
$$
¿Listo para comenzar?
**Ejercicio 12.1 (Liste las Combinaciones)** Usa `expand.grid` para crear un data frame que contenga todas las combinaciones posibles de *tres* símbolos del vector `rueda`:
``` r
rueda <- c("DD", "7", "BBB", "BB", "B", "C", "0")
```
Asegúrese de agregar el argumento `stringsAsFactors = FALSE` a su llamada `expand.grid`; de lo contrario, `expand.grid` guardará las combinaciones como factores, una elección desafortunada que interrumpirá la función `puntuacion`
*Solución.* Para crear un data frame de cada combinación de _tres_ símbolos, debe ejecutar `expand.grid` y darle _tres_ copias de `wheel`. El resultado será un data frame con 343 filas, una para cada combinación única de tres símbolos de ranura:
``` r
combos <- expand.grid(rueda, rueda, rueda, stringsAsFactors = FALSE)
combos
## Var1 Var2 Var3
## 1 DD DD DD
## 2 7 DD DD
## 3 BBB DD DD
## 4 BB DD DD
## 5 B DD DD
## 6 C DD DD
## ...
## 341 B 0 0
## 342 C 0 0
## 343 0 0 0
```
Ahora, calculemos la probabilidad de obtener cada combinación. Puedes usar las probabilidades contenidas en el argumento `prob` de `obt_simbolos` para hacer esto. Estas probabilidades determinan la frecuencia con la que se elige cada símbolo cuando su máquina tragamonedas genera símbolos. Se calcularon después de observar 345 jugadas de las terminales de video lotería de Manitoba. Los ceros tienen la mayor probabilidad de ser seleccionados (0,52) y las cerezas la menor (0,01):
``` r
obt_simbolos() <- function() {
rueda <- c("DD", "7", "BBB", "BB", "B", "C", "0")
sample(rueda, size = 3, replace = TRUE,
prob = c(0.03, 0.03, 0.06, 0.1, 0.25, 0.01, 0.52))
}
```
**Ejercicio 12.2 (Haz una Tabla de Búsqueda)** Aísle las probabilidades anteriores en una tabla de búsqueda. ¿Qué nombres usarás en tu tabla?
*Solución.* Sus nombres deben coincidir con la entrada que desea buscar. En este caso, la entrada serán las cadenas de caracteres que aparecen en `Var1`, `Var2` y `Var3`. Entonces su tabla de búsqueda debería verse así:
``` r
prob <- c("DD" = 0.03, "7" = 0.03, "BBB" = 0.06,
"BB" = 0.1, "B" = 0.25, "C" = 0.01, "0" = 0.52)
```
Ahora busquemos nuestras probabilidades.
**Ejercicio 12.3 (Buscar las Probabilidades)** Busque las probabilidades de obtener los valores en `Var1`. Luego agréguelos a `combos` como una columna llamada `prob1`. Luego haga lo mismo para `Var2` (`prob2`) y `Var3` (`prob3`).
*Solución.* Recuerde que usa la notación de selección de R para buscar valores en una tabla de búsqueda. Los valores que resulten estarán relacionados con el índice que utilice:
``` r
combos$prob1 <- prob[combos$Var1]
combos$prob2 <- prob[combos$Var2]
combos$prob3 <- prob[combos$Var3]
head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3
## DD DD DD 0.03 0.03 0.03
## 7 DD DD 0.03 0.03 0.03
## BBB DD DD 0.06 0.03 0.03
```
Ahora, ¿cómo debemos calcular la probabilidad total de cada combinación? Nuestros tres símbolos de tragamonedas se eligen de forma independiente, lo que significa que la misma regla que gobernó nuestras probabilidades de dados gobierna nuestras probabilidades de símbolos:
$$
P(A \& B \& C \& ...) = P(A) \cdot P(B) \cdot P(C) \cdot ...
$$
**Ejercicio 12.4 (Calcular las Probabilidades de Cada Combinación)** Calcule las probabilidades generales para cada combinación. Guárdelos como una columna llamada `prob` en `combos`, luego verifique su trabajo.
Puedes comprobar que las matemáticas funcionaron sumando las probabilidades. Las probabilidades deben sumar uno, porque una de las combinaciones *debe* aparecer cuando juegas en la máquina tragamonedas. En otras palabras, aparecerá una combinación, con probabilidad de uno.
Puede calcular las probabilidades de cada combinación posible de una sola vez con alguna ejecución de elementos:
``` r
combos$prob <- combos$prob1 * combos$prob2 * combos$prob3
head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3 prob
## DD DD DD 0.03 0.03 0.03 0.000027
## 7 DD DD 0.03 0.03 0.03 0.000027
## BBB DD DD 0.06 0.03 0.03 0.000054
```
La suma de las probabilidades es uno, lo que sugiere que nuestra matemática es correcta:
``` r
sum(combos$prob)
## 1
```
Solo necesita hacer una cosa más antes de poder calcular el valor esperado: debe determinar el premio para cada combinación en `combos`. Puedes calcular el premio con `puntuacion`. Por ejemplo, podemos calcular el premio de la primera fila de `combos` así:
``` r
simbolos <- c(combos[1, 1], combos[1, 2], combos[1, 3])
## "DD" "DD" "DD"
puntuacion(simbolos)
## 800
```
Sin embargo, hay 343 filas, lo que hace que el trabajo sea tedioso si planea calcular las puntuaciones manualmente. Será más rápido automatizar esta tarea y hacer que R lo haga por usted, lo que puede hacer con un bucle `for`.
## Bucles for
Un bucle `for` repite un trozo de código muchas veces, una vez para cada elemento en un conjunto de entrada. Los bucles `for` proporcionan una manera de decirle a R: "Haz esto para cada valor de aquello". En sintaxis R, esto se ve así:
``` r
for (valor in aquello) {
esto
}
```
El objeto `aquello` debe ser un conjunto de objetos (a menudo un vector de números o cadenas de caracteres). El bucle for ejecutará el código que aparece entre llaves una vez para cada miembro de `aquello`. Por ejemplo, el bucle for siguiente ejecuta `print("una ejecución")` una vez para cada elemento en un vector de cadenas de caracteres:
``` r
for (valor in c("Mi", "primer", "bucle", "foe")) {
print("una ejecución")
}
## "una ejecución"
## "una ejecución"
## "una ejecución"
## "una ejecución"
```
El símbolo `valor` en un bucle for actúa como un argumento en una función. El bucle for creará un objeto llamado `valor` y le asignará un nuevo valor en cada ejecución del bucle. El código en su bucle puede acceder a este valor llamando al objeto `valor`.
¿Qué valores asignará el bucle for a `valor`? Utilizará los elementos del conjunto en el que ejecuta el ciclo. `for` comienza con el primer elemento y luego asigna un elemento diferente a `valor` en cada ejecución del ciclo for, hasta que todos los elementos han sido asignados a `valor`. Por ejemplo, el bucle for de abajo ejecutará `print(valor)` cuatro veces e imprimirá un elemento de `c("Mi", "segundo", "bucle", "for")` cada vez:
``` r
for (valor in c("Mi", "segundo", "bucle", "for")) {
print(valor)
}
## "Mi"
## "segundo"
## "bucle"
## "for"
```
En la primera ejecución, el ciclo for sustituyó `"My"` por `valor` en `print(valor)`. En la segunda ejecución, sustituyó `"segundo"`, y así sucesivamente hasta que `for` ejecutó `print(valor)` una vez con cada elemento del conjunto:
Si mira `valor` después de que se ejecuta el bucle, verá que todavía contiene el valor del último elemento del conjunto:
``` r
valor
## "for"
```
He estado usando el símbolo `valor` en mis bucles for, pero no tiene nada de especial. Puede usar cualquier símbolo que desee en su bucle para hacer lo mismo siempre que el símbolo aparezca antes de `in` en los paréntesis que siguen a `for`. Por ejemplo, podría reescribir el bucle anterior con cualquiera de los siguientes:
``` r
for (palabra in c("Mi", "segundo", "bucle", "for")) {
print(palabra)
}
for (texto in c("Mi", "segundo", "bucle", "for")) {
print(texto)
}
for (i in c("Mi", "segundo", "bucle", "for")) {
print(i)
}
```
::: callout-warning
**Elige tus símbolos con cuidado**
R ejecutará su ciclo en cualquier entorno desde el que lo llame. Estas son malas noticias si su bucle usa nombres de objetos que ya existen en el entorno. Su bucle sobrescribirá los objetos existentes con los objetos que crea. Esto también se aplica al símbolo de valor.
:::
::: callout-tip
**Los bucles for se ejecutan en conjuntos**
En muchos lenguajes de programación, los bucles `for` están diseñados para funcionar con números enteros, no con conjuntos. Le da al bucle un valor inicial y un valor final, así como un incremento para avanzar el valor entre bucles. El bucle `for` se ejecuta hasta que el valor del bucle supera el valor final.
Puede recrear este efecto en R haciendo que un bucle `for` se ejecute en un conjunto de enteros, pero no pierda de vista el hecho de que los bucles `for` de R se ejecutan en miembros de un conjunto, no en secuencias de enteros.
:::
Los bucles `for` son muy útiles en la programación porque te ayudan a conectar un fragmento de código con cada elemento de un conjunto. Por ejemplo, podríamos usar un ciclo `for` para ejecutar `puntuacion` una vez por cada fila en `combos`. Sin embargo, los bucles `for` de R tienen un defecto que querrá conocer antes de empezar a usarlos: los bucles `for` no devuelven salida.
Los bucles `for` son como Las Vegas: lo que sucede en un bucle `for` permanece en un bucle `for`. Si desea utilizar los productos de un bucle `for`, debe escribir el bucle `for` para que guarde su propia salida a medida que avanza.
Nuestros ejemplos anteriores parecían devolver la salida, pero esto era engañoso. Los ejemplos funcionaron porque llamamos `print`, que siempre imprime sus argumentos en la consola (incluso si se llama desde una función, un bucle `for` o cualquier otra cosa). Nuestros bucles `for` no devolverán nada si eliminas la llamada `print`:
``` r
for (valor in c("Mi", "tercer", "bucle", "for")) {
valor
}
##
```
Para guardar la salida de un bucle `for`, debe escribir el bucle para que guarde su propia salida mientras se ejecuta. Puede hacer esto creando un vector vacío o una lista antes de ejecutar el bucle `for`. Luego use el bucle `for` para llenar el vector o la lista. Cuando finalice el bucle `for`, podrá acceder al vector o lista, que ahora tendrá todos sus resultados.
Veamos esto en acción. El siguiente código crea un vector vacío de longitud 4:
``` r
caracteres <- vector(length = 4)
```
El siguiente ciclo lo llenará con cadenas de texto:
``` r
palabras <- c("Mi", "cuarto", "bucle", "for")
for (i in 1:4) {
caracteres[i] <- palabras[i]
}
caracteres
## "Mi" "cuarto" "bucle" "for"
```
Este enfoque generalmente requerirá que cambie los conjuntos en los que ejecuta su bucle `for`. En lugar de ejecutar en un conjunto de objetos, ejecute en un conjunto de enteros que puede usar para indexar tanto su objeto como su vector de almacenamiento. Este enfoque es muy común en R. Encontrará en la práctica que usa bucles `for` no tanto para ejecutar código, sino para llenar vectores y listas con los resultados del código.
Usemos un bucle `for` para calcular el premio de cada fila en `combos`. Para comenzar, cree una nueva columna en `combos` para almacenar los resultados del bucle `for`:
``` r
combos$premio <- NA
head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3 prob premio
## DD DD DD 0.03 0.03 0.03 0.000027 NA
## 7 DD DD 0.03 0.03 0.03 0.000027 NA
## BBB DD DD 0.06 0.03 0.03 0.000054 NA
```
El código crea una nueva columna llamada premio y la llena con `NA`. R usa sus reglas de reciclaje para completar cada valor de la columna con `NA`.
**Ejercicio 12.5 (Construye un Bucle)** Construya un bucle `for` que ejecutará `puntuacion` en las 343 filas de `combos`. El ciclo debe ejecutar `puntuacion` en las primeras tres entradas de la \_i_ésima fila de `combos` y debe almacenar los resultados en la \_i_ésima entrada de `combos$premio`.
*Solución.* Puedes puntuar las filas en `combos` con:
``` r
for (i in 1:nrow(combos)) {
simbolos <- c(combos[i, 1], combos[i, 2], combos[i, 3])
combos$premio[i] <- puntuacion(simbolos)
}
```
Después de ejecutar el ciclo for, `combos$premio` contendrá el premio correcto para cada fila. Este ejercicio también prueba la función `puntuacion`; `puntuacion` parece funcionar correctamente para todas las combinaciones posibles de tragamonedas:
``` r
head(combos, 3)
## Var1 Var2 Var3 prob1 prob2 prob3 prob premio
## DD DD DD 0.03 0.03 0.03 0.000027 800
## 7 DD DD 0.03 0.03 0.03 0.000027 0
## BBB DD DD 0.06 0.03 0.03 0.000054 0
```
Ahora estamos listos para calcular el valor esperado del premio. El valor esperado es la suma de `combos$premio` ponderada por `combos$prob`. Esta es también la tasa de pago de la máquina tragamonedas:
``` r
sum(combos$premio * combos$prob)
## 0.538014
```
Oh oh. El premio esperado es de aproximadamente 0,54, lo que significa que nuestra máquina tragamonedas solo paga 54 centavos por dólar a largo plazo. ¿Significa esto que el fabricante de las máquinas tragamonedas de Manitoba *estaba* mintiendo?
No, porque ignoramos una característica importante de la máquina tragamonedas cuando escribimos `puntuacion`: un diamante es un comodín. Puede tratar un `DD` como cualquier otro símbolo si aumenta su premio, con una excepción. No puede convertir un `DD` en una `C` a menos que ya tenga otra `C` en sus símbolos (sería demasiado fácil si cada `DD` le otorgara automáticamente \$2).
Lo mejor de los `DD`s es que sus efectos son acumulativos. Por ejemplo, considere la combinación `B`, `DD`, `B`. El `DD` no solo cuenta como `B`, que ganaría un premio de \$10; el `DD` también duplica el premio a \$20.
Agregar este comportamiento a nuestro código es un poco más difícil que lo que hemos hecho hasta ahora, pero involucra todos los mismos principios. Puede decidir que su máquina tragamonedas no use comodines y conservar el código que tenemos. En ese caso, su máquina tragamonedas tendrá una tasa de pago de alrededor del 54 por ciento. O bien, podría reescribir su código para usar comodines. Si lo hace, encontrará que su máquina tragamonedas tiene una tasa de pago del 93 por ciento, un uno por ciento más alta que la afirmación del fabricante. Puedes calcular esta tasa con el mismo método que usamos en esta sección.
**Ejercicio 12.6 (Desafío)** Hay muchas formas de modificar `puntuacion` que contarían `DD` como comodines. Si desea probar su habilidad como programador de R, intente escribir su propia versión de `puntuacion` que maneje correctamente los diamantes.
Si desea un desafío más modesto, estudie el siguiente código de `puntuacion`. Da cuenta de los diamantes salvajes de una manera que me parece elegante y sucinta. Vea si puede entender cada paso en el código y cómo logra su resultado.
*Solución.* Aquí hay una versión de puntuacion que maneja diamantes salvajes:
``` r
puntuacion <- function(simbolos) {
diamantez <- sum(simbolos == "DD")
cerezas <- sum(simbolos == "C")
# identificar caso
# dado que los diamante son salvajes, solo
# importan los no diamantes para tres de un
# tipo y para todos barras
tragamonedas <- simbolos[simbolos != "DD"]
iguales <- length(unique(tragamonedas)) == 1
barras <- tragamonedas %in% c("B", "BB", "BBB")
# asignar premio
if (diamantes == 3) {
premio <- 100
} else if (iguales) {
pagos <- c("7" = 80, "BBB" = 40, "BB" = 25,
"B" = 10, "C" = 10, "0" = 0)
premio <- unname(pagos[tragamonedas[1]])
} else if (all(barras)) {
premio <- 5
} else if (cerezas > 0) {
# diamantes cuentan como cerezas
# siempre y cuando haya una cereza real
premio <- c(0, 2, 5)[cerezas + diamantes + 1]
} else {
premio <- 0
}
# duplicar por cada diamante
premio * 2^diamantes
}
```
**Ejercicio 12.7 (Calcular el Valor Esperado)** Calcule el valor esperado de la máquina tragamonedas cuando usa la nueva función `puntuacion`. Puede usar el data frame `combos` existente, pero necesitará crear un bucle `for` para recalcular `combos$premio`.
Para actualizar el valor esperado, simplemente actualice `combos$premio`:
``` r
for (i in 1:nrow(combos)) {
simbolos <- c(combos[i, 1], combos[i, 2], combos[i, 3])
combos$premio[i] <- puntuacion(simbolos)
}
```
Luego vuelva a calcular el valor esperado:
``` r
sum(combos$premio * combos$prob)
## 0.934356
```
Este resultado justifica la afirmación del fabricante. En todo caso, las máquinas tragamonedas parecen más generosas de lo que dijo el fabricante.
## Bucles while
R tiene dos compañeros del bucle `for`: el bucle `while` y el bucle `repeat`. Un bucle `while` vuelve a ejecutar un fragmento *mientras* una determinada condición permanece `TRUE`. Para crear un bucle `while`, siga `while` por una condición y un fragmento de código, como este:
``` r
while (condición) {
código
}
```
`while` volverá a ejecutar `condición`, que debería ser una prueba lógica, al comienzo de cada ciclo. Si `condición` se evalúa como `TRUE`, `while` ejecutará el código entre sus llaves. Si `condición` se evalúa como `FALSE`, `while` finalizará el ciclo.
¿Por qué la `condición` podría cambiar de `TRUE` a `FALSE`? Presumiblemente porque el código dentro de su ciclo ha cambiado si la condición sigue siendo `TRUE`. Si el código no tiene relación con la condición, se ejecutará un ciclo `while` hasta que lo detenga. Así que ten cuidado. Puede detener un bucle `while` pulsando Escape o haciendo clic en el icono de señal de alto en la parte superior del panel de la consola de RStudio. El icono aparecerá una vez que el ciclo comience a ejecutarse.
Al igual que los bucles `for`, los bucles `while` no devuelven un resultado, por lo que debe pensar en lo que quiere que devuelva el bucle y guardarlo en un objeto durante el bucle.
Puede usar bucles `while` para hacer cosas que requieran un número variable de iteraciones, como calcular cuánto tiempo se tarda en ir a la quiebra jugando tragamonedas (como se indica a continuación). Sin embargo, en la práctica, los bucles `while` son mucho menos comunes que los bucles `for` en R:
``` r
jugadas_hasta_quiebra <- function(empezar_con) {
efectivo <- empezar_con
n <- 0
while (efectivo > 0) {
efectivo <- efectivo - 1 + play()
n <- n + 1
}
n
}
jugadas_hasta_quiebra(100)
## 260
```
## Bucles repeat
Los bucles `repeat` son incluso más básicos que los bucles `while`. Repetirán un fragmento de código hasta que les digas que se detengan (pulsando Escape) o hasta que encuentren el comando `break`, que detendrá el ciclo.
Puedes usar un bucle `repeat` para recrear `jugadas_hasta_quiebra`, mi función que simula cuánto tiempo lleva perder dinero mientras juegas tragamonedas:
``` r
jugadas_hasta_quiebra <- function(empezar_con) {
efectivo <- empezar_con
n <- 0
repeat {
efectivo <- efectivo - 1 + play()
n <- n + 1
if (efectivo <= 0) {
break
}
}
n
}
jugadas_hasta_quiebra(100)
## 237
```
## Resumen
Puede repetir tareas en R con bucles `for`, `while` y `repeat`. Para usar `for`, dale un fragmento de código para ejecutar y un conjunto de objetos para recorrer. `for` ejecutará el fragmento de código una vez para cada objeto. Si desea guardar la salida de su bucle, puede asignarlo a un objeto que existe fuera del bucle.
La repetición juega un papel importante en la ciencia de datos. Es la base para la simulación, así como para las estimaciones de varianza y probabilidad. Los bucles no son la única forma de crear repeticiones en R (considere `replicate` por ejemplo), pero son una de las formas más populares.
Desafortunadamente, los bucles en R a veces pueden ser más lentos que los bucles en otros idiomas. Como resultado, los bucles de R tienen mala reputación. Esta reputación no es del todo merecida, pero resalta un tema importante. La velocidad es esencial para el análisis de datos. Cuando su código se ejecuta rápido, puede trabajar con datos más grandes y hacer más antes de que se le acabe el tiempo o la potencia computacional. [Velocidad](#speed) te enseñará cómo escribir bucles `for` rápidos y código rápido en general con R. Allí, aprenderás a escribir código vectorizado, un estilo de código ultrarrápido que aprovecha todas las fortalezas de R.