9  Bucles

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.

9.1 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?

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 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.

9.2 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:

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:

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:

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:

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:

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:

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:

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:

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í:

## 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:

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:

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):

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í:

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:

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:

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:

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í:

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.

9.3 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í:

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:

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:

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:

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:

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)
}

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.

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:

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:

caracteres <- vector(length = 4)

El siguiente ciclo lo llenará con cadenas de texto:

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:

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:

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:

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:

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 DDs 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:

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:

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:

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.

9.4 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:

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:

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

9.5 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:

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

9.6 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 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.