Capítulo 11 Estructuras de control: if-else
Scripts usados:
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 }
: sicondición
(por ejemplo,x == 1
) es cierta, se ejecutacó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 }
: sicondición
(por ejemplo,x == 1
) es cierta, se ejecutacódigo A
; si es falsa, se ejecutacó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
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:
📝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