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?
<- c(1, 2, 3, 4, 5, 6) dado
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
:
<- expand.grid(dado, dado) tiradas
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:
$valor <- tiradas$Var1 + tiradas$Var2
tiradashead(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:
<- c("1" = 1/8, "2" = 1/8, "3" = 1/8, "4" = 1/8, "5" = 1/8, "6" = 3/8)
prob
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
:
$Var1
tiradas## 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
$Var1]
prob[tiradas## 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
$prob1 <- prob[tiradas$Var1]
tiradashead(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
:
$prob2 <- prob[tiradas$Var2]
tiradas
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
:
$prob <- tiradas$prob1 * tiradas$prob2
tiradas
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
:
<- c("DD", "7", "BBB", "BB", "B", "C", "0") rueda
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:
<- expand.grid(rueda, rueda, rueda, stringsAsFactors = FALSE)
combos
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() {
<- c("DD", "7", "BBB", "BB", "B", "C", "0")
rueda 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í:
<- c("DD" = 0.03, "7" = 0.03, "BBB" = 0.06,
prob "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:
$prob1 <- prob[combos$Var1]
combos$prob2 <- prob[combos$Var2]
combos$prob3 <- prob[combos$Var3]
combos
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:
$prob <- combos$prob1 * combos$prob2 * combos$prob3
combos
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í:
<- c(combos[1, 1], combos[1, 2], combos[1, 3])
simbolos ## "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:
<- vector(length = 4) caracteres
El siguiente ciclo lo llenará con cadenas de texto:
<- c("Mi", "cuarto", "bucle", "for")
palabras
for (i in 1:4) {
<- palabras[i]
caracteres[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
:
$premio <- NA
combos
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)) {
<- c(combos[i, 1], combos[i, 2], combos[i, 3])
simbolos $premio[i] <- puntuacion(simbolos)
combos }
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 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:
<- function(simbolos) {
puntuacion
<- sum(simbolos == "DD")
diamantez <- sum(simbolos == "C")
cerezas
# identificar caso
# dado que los diamante son salvajes, solo
# importan los no diamantes para tres de un
# tipo y para todos barras
<- simbolos[simbolos != "DD"]
tragamonedas <- length(unique(tragamonedas)) == 1
iguales <- tragamonedas %in% c("B", "BB", "BBB")
barras
# asignar premio
if (diamantes == 3) {
<- 100
premio else if (iguales) {
} <- c("7" = 80, "BBB" = 40, "BB" = 25,
pagos "B" = 10, "C" = 10, "0" = 0)
<- unname(pagos[tragamonedas[1]])
premio else if (all(barras)) {
} <- 5
premio else if (cerezas > 0) {
} # diamantes cuentan como cerezas
# siempre y cuando haya una cereza real
<- c(0, 2, 5)[cerezas + diamantes + 1]
premio else {
} <- 0
premio
}
# duplicar por cada diamante
* 2^diamantes
premio }
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)) {
<- c(combos[i, 1], combos[i, 2], combos[i, 3])
simbolos $premio[i] <- puntuacion(simbolos)
combos }
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:
<- function(empezar_con) {
jugadas_hasta_quiebra <- empezar_con
efectivo <- 0
n while (efectivo > 0) {
<- efectivo - 1 + play()
efectivo <- n + 1
n
}
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:
<- function(empezar_con) {
jugadas_hasta_quiebra <- empezar_con
efectivo <- 0
n repeat {
<- efectivo - 1 + play()
efectivo <- n + 1
n 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.