Capítulo 11 Estructuras de control: if-else

Hasta ahora hemos trasteado con datos: hemos empezado desde una celda, para luego construir columnas (vectores), y luego juntarlas en algo parecido a una base de datos (tablas o data.frame). Pero para poder manejarnos con soltura con dichos datos es importante que conozcamos cómo se usan las expresiones de control más típicas.

Una expresión de control será un conjunto de órdenes que nos permiten decidir por dónde queremos que avance nuestro programa: ¿qué hacemos si sucede A? ¿Y si sucede B?

Si has programado en algún otro lenguaje, estarás familiarizado/a con elementos como un if (blabla) {...} else {...} (que los usaremos a veces) o bucles for y while (que intentaremos no usarlos en R). Si es la primera que programas en algún lenguaje, no te preocupes, las empezamos desde cero, empezando por las estructuras condicionales

11.1 if

Una de las estructuras de control más famosas de cualquier lenguaje de programación es la estructura if: SI las condiciones impuestas se cumplen (TRUE), ejecuta las órdenes que tengamos dentro.

  • if (x == 1) { código A }: si condición (por ejemplo, x == 1) es cierta, se ejecuta código A; en caso contrario, no hace nada.

Definamos por ejemplo una variable sencilla, las edades de 8 personas.

edades <- c(14, 17, 24, 56, 31, 20, 87, 73)

Para comprobar cuales son menores de edades podemos aplicar lo aprendido en temas anteriores, realizando una comparación lógica: si es menor de edad, devuelve un TRUE; en caso contrario, devuelve un FALSE.

edades < 18
## [1]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE

Con las funciones any() y all() podemos saber si todos o alguno de los elementos de un vector cumplen una condición.

all(edades >= 18) # todos mayores de edad
## [1] FALSE
any(edades < 18) # existe algun menor de edad
## [1] TRUE

Con dichos elementos vamos a construir nuestra primera estructura condicional: queremos que, SI existe algún menor de edad, nos imprima un mensaje.

if (any(edades < 18)) { # TRUE si al menos una persona mayor de edad
  
  print("existe alguna persona mayor de edad")
  
}
## [1] "existe alguna persona mayor de edad"

En caso de que no se cumplan las condiciones dentro del if() (condiciones que devuelvan un FALSE), no sucederá nada. Podemos hacer lo mismo cambiando la condición: SI todos son mayores de edad, imprime el mensaje.

if (all(edades >= 18)) { # TRUE si TODOS son mayores de edad
  
  print("todas las personas son mayores de edad")
  
}

Fíjate que en este caso no hemos obtenido ningún mensaje porque la condición all(edades >= 18) no es cierta (no son todos mayores de 18 años), así que no ha ejecutado el código interno.

11.2 if - else

La estructura if (condicion) { } puede ser combinada con un else { }: cuando la condición del if no se cumpla (como en el último ejemplo), se ejecutará el código que haya dentro del else { }, permitiéndonos decidir que sucede cuando SÍ se cumple y cuando NO se cumple.

  • if (x == 1) { código A } else { código B }: si condición (por ejemplo, x == 1) es cierta, se ejecuta código A; si es falsa, se ejecuta código B.

Siguiendo con el ejemplo de las edades, queremos nos imprima un mensaje SI todos son mayores de edad, y un mensaje diferente en caso de que dicha condición no se cumpla.

if (all(edades >= 18)) { # TRUE si TODOS son mayores de edad
  
  print("todas las personas son mayores de edad")
  
} else { # si hay alguno menor de edad 
  
  print("existe alguna persona menor de edad")
}
## [1] "existe alguna persona menor de edad"

Podemos ya complicar un poco el código con lo que hemos aprendido de temas anteriores. Por ejemplo, en caso de que no se cumpla (es decir, que haya alguno menor de edad), queremos extraer la edad por pantalla de esos menores (con edades[edades < 18] extraeremos las edades que cumplen la condición de ser menor de edad).

if (all(edades >= 18)) { # TRUE si TODOS son mayores de edad
  
  print("todas las personas son mayores de edad")
  
} else { # si hay alguno menor de edad 
  
  print(glue("Existen menores de edad. Sus edades son: {paste(edades[edades < 18], collapse = '-')} años"))
}
## Existen menores de edad. Sus edades son: 14-17 años

11.3 if - else anidados

Dicha estructura if - else puede anidarse, de forma que vayamos concatenando dichas estructuras, como en el ejemplo que tenemos debajo. Imagina que queremos realizar una acción si todos fuesen mayores de edad; en caso contrario, pero si todos los menores de edad tienen 16 años o más, realizar otra acción; en caso contrario, otra acción

if (all(edades >= 18)) { # TRUE si TODOS son mayores de edad
  
  print("todas las personas son mayores de edad")
  
} else if (all(edades >= 16)) { # si todos los menores de edad tienen >=16
  
  print("Existe alguna persona menor de edad pero todos con 16 años o más")
  
} else {
  
  print("Existe alguna persona menor de 16 años")

}
## [1] "Existe alguna persona menor de 16 años"

11.4 ifelse () vectorizado

Esta estructura condicional puede ser vectorizada, de forma que podamos reunir en una sola fila un número elevado de estructuras de comparación la función ifelse(), cuyos argumentos de entrada serán la condición a evaluar, lo que sucede cuando se cumple y lo que no, que aplicará a cada elemento del vector de entrada. Con el ejemplo de las edades, vamos a dejar el dato ausente si son menores de edad, y si son mayores de edad se queda como está (recuerda: primero la condición, después lo que devuelve cuando es TRUE, y por último lo que devuelve cuando es FALSE)

# NA si no cumple la condición, la edad si se cumple.
ifelse(edades >= 18, edades, NA)
## [1] NA NA 24 56 31 20 87 73

Todas estas estructuras no solo sirven para datos numéricos. Vamos a definir un vector de nombres con algunos ausentes (NA), y vamos a sustituir los ausentes por el texto "nombre_desconocido" (los que no sean ausentes, es decir los que is.na() devuelva FALSE, se quedan como están).

nombres <- c("Juan", "María", NA, NA, "Lucía", "Carmen", "Javier",
             NA, "Carlos", NA, "Gregorio", "Paloma")

# Si tiene ausente --> "nombre_desconocido"
# Si no tiene ausente --> nombres originales
nombres <- ifelse(is.na(nombres), "nombre_desconocido", nombres)
nombres
##  [1] "Juan"               "María"              "nombre_desconocido"
##  [4] "nombre_desconocido" "Lucía"              "Carmen"            
##  [7] "Javier"             "nombre_desconocido" "Carlos"            
## [10] "nombre_desconocido" "Gregorio"           "Paloma"

Esta función ifelse() es muy util para codificar variables o averiguar cuales cumplen una condición.

11.5 Consejos

CONSEJOS

 

Rainbow parentheses

Uno de los errores más habituales, y que seguirás cometiendo aunque lleves años programando, es no cerrar un paréntesis que has abierto, por lo que el programa no sabe si has acabado de llamar a una orden o no. Para ello en las nuevas versiones de RStudio, en el menú Tools < Global Options < Code < Display podemos habilitar la opción Rainbow Parentheses que nos escribe cada par de () de un color distinto

Activando Rainbow Parentheses.

Imagen/gráfica 11.1: Activando Rainbow Parentheses.

 

Minimiza estructuras de control en el código

Puedes colapsar las estructuras de control pulsando en la flecha que aparece a la izquierda de ellas en tu script.

 

11.6 📝 Ejercicios

(haz click en las flechas para ver soluciones)

📝Ejercicio 1: modifica el código inferior para imprimir un mensaje por pantalla si todos los datos del conjunto airquality son de meses que no sean enero.

  • Solución:
# install.packages("dataset") # solo la primera vez
library(datasets) # paquete con los datos
mes <- airquality$Month

if (all(mes != 1)) { # todos con mes distinto de 1
  
  print("Ningún dato es del mes de enero")
  
}
## [1] "Ningún dato es del mes de enero"
# install.packages("dataset") # solo la primera vez
library(datasets) # paquete con los datos
mes <- airquality$Month

if (mes == 2) {
  
  print("Ningún dato es del mes de enero")
  
}

 

📝Ejercicio 2: modifica el código inferior para guardar en una variable temperatura_alta un valor TRUE si alguno de los registros tiene una temperatura mayor a 90 (están en Farenheit) y un FALSE en caso contrario.

  • Solución:
temperatura <- airquality$Temp
temperatura_alta <- FALSE # por defecto FALSE
if (any(temperatura > 90)) {
  
  temperatura_alta <- TRUE # si se cumple la condición, a TRUE
  
}
temperatura <- airquality$Temp

if (temperatura == 100) {
  
  print("Alguno de los registros tiene temperatura superior a 90 Farenheit")
  
}

 

📝Ejercicio 3: modifica el código inferior para imprimir un mensaje por pantalla si alguno de los días supera la temperatura de 100.

  • Solución:
temperatura <- airquality$Temp

if (any(temperatura > 100)) { # nos basta con uno
  
  print("Alguno de los registros tiene temperatura superior a 100 Farenheit")
  
}

No debería imprimar nada porque ninguno cumple la condición.

temperatura <- airquality$Temp

if (all(temperatura < 50)) {
  
  print("Alguno de los registros tiene temperatura superior a 100 Farenheit")
  
}

 

📝Ejercicio 4: del paquete lubridate, la función hour() nos devuelve la hora de una fecha dada, y la función now() nos devuelve fecha y hora del momento actual. Con ambas funciones, y usando if, imprime por pantalla "buenas noches" solo a partir de las 21 horas.

  • Solución:
# Cargamos librería
library(lubridate)

# Fecha-hora actual
fecha_actual <- now()

# Estructura if
if (hour(fecha_actual) > 21) {
  
  cat("Buenas noches") # print/cat dos formas de imprimir por pantalla
}

 

📝Ejercicio 5: con las funciones del ejercicio anterior, y usando una estructura if-else, imprime por pantalla (con cat() o print()) los mensajes "buenos días" (de 6 a 14 horas), "buenas tardes" (de 14 a 21 horas) o "buenas noches" (de las 21 a las 6 horas).

  • Solución:
# Fecha actual
fecha_actual <- now()

# Estructura if-else
if (hour(fecha_actual) > 6 & hour(fecha_actual) < 14) {
  
  cat("Buenos días")
  
} else if (hour(fecha_actual) > 14 & hour(fecha_actual) < 21) {
  
  cat("Buenas tardes")
  
} else {
  
  cat("Buenas noches")
}
## Buenas tardes

 

📝Ejercicio 6: realiza el ejercicio anterior pero sin estructura de llaves, de forma concisa con ifelse(). Mira la ayuda si la necesitases poniendo ? ifelse en consola.

  • Solución:
# Fecha actual
fecha_actual <- now()

# Estructura if-else
cat(ifelse(hour(fecha_actual) > 6 & hour(fecha_actual) < 14,
           "Buenos días",
           ifelse(hour(fecha_actual) > 14 &
                    hour(fecha_actual) < 21,
                  "Buenas tardes", "Buenas noches")))
## Buenas tardes