Capitulo 8 Estructuras de control

Como su nombre lo indica, las estructuras de control nos permiten controlar la manera en que se ejecuta nuestro código.

Las estructuras de control establecen condicionales en nuestros código. Por ejemplo, qué condiciones deben cumplirse para realizar una operación o qué debe ocurrir para ejecutar una función.

Esto es de gran utilidad para determinar la lógica y el orden en que ocurren las operaciones, en especial al definir funciones.

Las estructuras de control más usadas en R son las siguientes.

  • if, else: Si, de otro modo.
  • for: Para cada uno, en.
  • while: Mientras.

8.1 if, else

if (si) es usado cuando deseamos que una operación se ejecute únicamente cuando una condición se cumple.

else (de otro modo) es usado para indicarle a R qué hacer en caso de que la condición de un if no se cumpla.

Un if es la manera de decirle a R: SI esta condición es cierta, ENTONCES haz estas operaciones.

El modelo para un if es:

if(Condicion) {
  operaciones_si_la_condicion_es_TRUE
}

Si la condición se cumple, es decir, es verdadera (TRUE), entonces se realizan las operaciones. En caso contrario, no ocurre nada y el código con las operaciones no es ejecutado.

Por ejemplo, le pedimos a R que nos muestre el texto “verdadero” si la condición se cumple.

# Se cumple la condición y se muestra "verdadero"
if(4 > 3) {
  "Verdadero"
}
## [1] "Verdadero"

En este caso, si no se cumple la condición, no pasa nada

# Se cumple la condición y se muestra "verdadero"
if(4 > 5) {
  "Verdadero"
}

else complementa un if, pues indica qué ocurrirá cuando la condición no se cumple, es decir cuando la condición es falsa (FALSE), en lugar de no hacer nada.

Un if con else es la manera de decirle a R: SI esta condición es es cierta, ENTONCES haz estas operaciones, DE OTRO MODO haz estas otras operaciones.

El modelo para un if con un else es:

if(condicion) {
  operaciones_si_la_condicion_es_TRUE
} else {
  operaciones_si_la_condicion_es_FALSE
}

Usando los ejemplos anteriores, podemos mostrar “Falso” si no se cumple la condición, en lugar de que no ocurra nada.

# Se cumple la condición y se muestra "Verdadero"
if(4 > 3) {
  "Verdadero"
} else {
  "Falso"
}
## [1] "Verdadero"

8.1.1 ifelse()

La función ifelse() nos permite vectorizar if, else. En lugar de escribir una línea de código para cada comparación, podemos usar una sola llamada a esta función, que se aplicará a todos los elementos de un vector.

Si intentamos usar if else con un vector, se nos mostrará una advertencia.

if(1:10 < 3) {
  "Verdadero"
}
## Warning in if (1:10 < 3) {: la condición tiene longitud > 1 y sólo el primer elemento será usado
## [1] "Verdadero"

Este mensaje nos dice que sólo se usará el primer elemento del vector para evaluar si la condición es verdadera y lo demás será ignorado.

En cambio, con ifelse se nos devolverá un valor para cada elemento de un vector en el que la condición sea TRUE, además nos devolverá otro valor para los elementos en que la condición sea FALSE.

Esta función tiene la siguiente forma:

ifelse(vector, valor_si_TRUE, valor_si_FALSE)

Para el ejemplo anterior:

ifelse(1:10 > 3,"Verdadwero","Falso")
##  [1] "Falso"      "Falso"      "Falso"      "Verdadwero" "Verdadwero" "Verdadwero" "Verdadwero" "Verdadwero" "Verdadwero" "Verdadwero"

Y podemos concatenar varios ifelse si nos interesa:

ifelse(1:10 < 3,"peque",
       ifelse(1:10 < 7,"medio","alto"))
##  [1] "peque" "peque" "medio" "medio" "medio" "medio" "alto"  "alto"  "alto"  "alto"

8.2 for

La estructura for nos permite ejecutar un bucle (loop), realizando una operación para cada elemento de un conjunto de datos.

Su estructura es la siguiente:

for(elemento in objeto) {
  operacion_con_elemento
}

Con lo anterior le decimos a R: PARA cada elemento EN un objeto, haz la siguiente operación.

Al escribir un bucle for la parte que corresponde al elemento la podemos llamar como nosotros deseemos, pero la parte que corresponde al objeto debe ser el nombre de un objeto existente.

Los dos bucles siguientes son equivalentes, sólo cambia el nombre que le hemos puesto al elemento.

dado <- 1:6

for(i in dado) {
  dado ^ 2 
}

Notarás que al ejecutar el código anterior parece que no ha ocurrido nada. En realidad, sí se han realizado las operaciones, pero R no ha devuelto sus resultados.

Las operaciones en un for se realizan pero sus resultados nunca son devueltos automáticamente, es necesario pedirlos de manera explícita.

Una solución para mostrar los resultados de un bucle for es usar la función print()

for(i in dado) {
  print(i ^ 2)
}
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25
## [1] 36

Comprobamos que la operación ha sido realizada a cada elemento de nuestro objeto. Sin embargo, usar print() sólo mostrará los resultados de las operaciones en la consola, no los asignará a un objeto.

Si deseamos asignar los resultados de un bucle for a un objeto, usamos índices.

Aprovechamos que el primer elemento en un bucle siempre es identificado con el número 1 y que continuará realizando operaciones hasta llegar al total de elementos que hemos especificado.

for(i in 1:10) {
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

En nuestro ejemplo, pasamos por los valores de dado, i por i. La primera i será igual a 1, la segunda a 2, y así sucesivamente hasta el 6.

Podemos usar estos valores para asignar cada valor resultante de nuestras operaciones a una posición específica en un vector, incluso si este está vacio.

Creamos un vector vacío, asignándole c() como valor.

mi_vector <- NULL

Ejecutamos nuestro bucle.

for(i in dado) {
  mi_vector[i] <- i ^ 2
}

Aunque no fueron mostrados en la consola, los resultados han sido asignados al objeto “mi_vector”.

mi_vector
## [1]  1  4  9 16 25 36

8.2.1 for y vectorización

Notarás que el resultado que obtuvimos usando for es el mismo que si vectorizamos la operación.

dado ^ 2
## [1]  1  4  9 16 25 36

Dado que en R contamos con vectorización de operaciones, la manera de recuperar los resultados de un for no es muy eficiente. Por lo tanto, este tipo de bucle no es muy popular en R.

En R generalmente hay opciones mejores, en cuanto a simplicidad y velocidad de cómputo, que un bucle for.

Sin embargo, es conveniente que conozcas esta estructura de control, pues hay ocasiones en la que es la mejor herramienta para algunos problemas específicos.

8.3 while

Este es un tipo de bucle que ocurre mientras una condición es verdadera (TRUE). La operación se realiza hasta que se llega a cumplir un criterio previamente establecido.

El modelo de while es:

while(condicion) {
  operaciones
}

Con esto le decimos a R: MIENTRAS esta condición sea VERDADERA, haz estas operaciones.

La condición generalmente es expresada como el resultado de una o varias operaciones de comparación, pero también puede ser el resultado de una función.

8.3.1 Usando while

Probemos sumar +1 a un valor, mientras que este sea menor que 5. Al igual que con for, necesitamos la función print() para mostrar los resultados en la consola.

umbral <- 5
valor <- 0

while(valor < umbral) {
  print("Todavía no.")
  valor <- valor + 1
}
## [1] "Todavía no."
## [1] "Todavía no."
## [1] "Todavía no."
## [1] "Todavía no."
## [1] "Todavía no."

¡Ten cuidado con crear bucles infinitos! Si ejecutas un while con una condición que nunca será FALSE, este nunca se detendrá.

Si corres lo siguiente, presiona la tecla ESC para detener la ejecución, de otro modo, correrá por siempre y puede llegar a congelar tu equipo.

while(1 < 2) {
  print("Presiona ESC para detener")
}

El siguiente es un error común. Estamos sumando +1 a i con cada iteración del bucle, pero como no estamos asignando este nuevo valor a i, su valor se mantiene igual, entonces la condición nunca se cumplirá y el bucle será infinito.

De nuevo, si corres lo siguiente, presiona la tecla ESC para detener la ejecución.

i <- 0
while(i < 10) {
  i + 1
}

Un uso común de while es que realice operaciones que queremos detener cuando se cumple una condición, pero desconocemos cuándo ocurrirá esto.

Supongamos que, por alguna razón queremos sumar calificaciones, del 1 al 10 al azar, hasta llegar a un número que sea mayor o igual a 50. Además nos interesa saber cuántas calificaciones sumaron y cuál fue el resultado al momento de cumplir la condición.

Para obtener números al azar del 1 al 10, usamos la función sample(). Esta función va a tomar una muestra al azar de tamaño igual a 1 (argumento size) de un vector del 1 al 10 (argumento x) cada vez que se ejecute.

Por lo tanto, cada vez que corras el ejemplo siguiente obtendrás un resultado distinto, pero siempre llegarás a un valor mayor a 50.

Creamos dos objetos, conteo y valor. Les asignamos el valor 0.

conteo <-  0
valor <- 0

Nuestro bucle while hará dos cosas.

Primero, tomaremos un número al azar del 1 al 10, y lo sumará a “valor”. Segundo, le sumaremos 1 a conteo cada vez que esto ocurra. De esta manera sabremos cuántas iteraciones ocurrieron para llegar a un valor que no sea menor a 50.

while(valor < 50) {
  valor <- valor + sample(x = 1:10, size = 1)
  conteo <- conteo + 1
}

Aunque no son mostrados en la consola los resultados son asignados a los objetos valor y conteo.

valor
## [1] 50
conteo
## [1] 12

8.4 Ejercicios

  • Usando las siguientes variables, crea un loop usando while para que se reste 10 euros del bote hasta que debamos poner 40€ más en el bote y así poder pagar todas las cervezas.
mensaje <- c("otra ronda: menos 10€")
bote <- 100
  • ¿Sabrías decirle que cuando llegue al final nos diga cuántas rondas nos hemos tomado y cuánto dinero nos queda en el bote?

  • Lo mismo con un loop for

  • Teniendo el siguiente vector, usa if, else o ifelse para clasificar los valores como negativos, normales o valores mayores que 10:

valores = rnorm(100,5,20)
  • ¿Sabrías decirme cuántos tenemos de cada nivel?