1 Operadores

1.1 Relacionales

Una parte esencial en programación son los operadores relacionales que comparan datos numéricos, de serie de caracteres o lógicos. El resultado de la comparación, ya sea Verdadero (1) o falso (0), puede utilizarse para tomar una decisión referente al flujo del programa.

Los operadores son

  • Menor que <
  • Mayor que >
  • Menor o igual que <=
  • Mayor o igual que >=
  • Exactamente igual que ==
  • Diferente !=

El resultado de la comparación, ya sea Verdadero (1) o falso (0), puede utilizarse para tomar una decisión referente al flujo del programa

# Logico
TRUE == FALSE
## [1] FALSE
TRUE != FALSE
## [1] TRUE
TRUE == 1
## [1] TRUE
TRUE <= FALSE
## [1] FALSE
TRUE > FALSE
## [1] TRUE
# Numerico
-6 * 14 != 17 - 101
## [1] FALSE
3 == (2 + 1)
## [1] TRUE
(1 + 2) > 4
## [1] FALSE
-6 * 5 + 2 >= -10+1
## [1] FALSE
# Strings
"useR" == "user"
## [1] FALSE
"Rchitect" != "rchitect"
## [1] TRUE
"dog" < "Cats"
## [1] FALSE
"raining" <= "raining dogs"
## [1] TRUE

Se puede hacer comparaciones entre vectores. A continuación, se presentan los datos que muestran las visitas diarias a los perfiles de Linkedin y Facebook de una persona

linkedin <- c(16, 9, 13, 5, 2, 17, 14)
facebook <- c(17, 7, 5, 16, 8, 13, 14)
  1. ¿Qué días las visitas al perfil de Linkedin son más de 15?
linkedin > 15
## [1]  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE
  1. ¿Qué días las visitas al perfil de Linkedin son menores o iguales a 5?
linkedin <= 5
## [1] FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE
  1. ¿Cuándo fue visitado el perfil de Linkedin más que el de Facebook?
linkedin > facebook
## [1] FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE

También se pueden comparar matrices. Con los vectores anteriores se crea una matriz.

views <- matrix(c(linkedin, facebook),
                nrow = 2, 
                byrow = TRUE)
views
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   16    9   13    5    2   17   14
## [2,]   17    7    5   16    8   13   14
  1. ¿Cuándo las vistas en el perfil son de 13?
views == 13
##       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]
## [1,] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
## [2,] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
  1. ¿Cuándo las vistas en el perfil son menores o iguales a 14?
views <= 14
##       [,1] [,2] [,3]  [,4] [,5]  [,6] [,7]
## [1,] FALSE TRUE TRUE  TRUE TRUE FALSE TRUE
## [2,] FALSE TRUE TRUE FALSE TRUE  TRUE TRUE

1.2 Lógicos

Los operadores lógicos pueden crear condiciones compuestas en una fórmula, como que se deben cumplir dos o más condiciones para elegir un determinado método de cálculo. Con los operadores lógicos, puede describir estas combinaciones de condiciones mediante

  • y &
  • o|
  • no !.

A continuación se usan los vectores de Linkedin y Facebook, y la matriz views.

  1. Nombra la matriz con los siete días de la semana.
linkedin
## [1] 16  9 13  5  2 17 14
facebook
## [1] 17  7  5 16  8 13 14
views
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]   16    9   13    5    2   17   14
## [2,]   17    7    5   16    8   13   14
red_social <- c("Linkedin", "Facebook")
dias <- c("Lunes","Martes","Miercoles","Jueves","Viernes","Sabado","Domingo")
rownames(views) <- red_social
colnames(views) <- dias
views
##          Lunes Martes Miercoles Jueves Viernes Sabado Domingo
## Linkedin    16      9        13      5       2     17      14
## Facebook    17      7         5     16       8     13      14
  1. ¿Las visitas en Linkedin son mayores a 10 y las de Facebook son menores a 10?
linkedin > 10 & facebook < 10
## [1] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
  1. ¿Las visitas en Linkedin son mayores o iguales a 10 o las de Facebook son menores o iguales a 10?
linkedin >= 12 | facebook >= 12
## [1]  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE
  1. ¿Cuándo la matriz de views es mayor a 11 y menor o igual a 14?
visits <- views > 11 & views <= 14
visits
##          Lunes Martes Miercoles Jueves Viernes Sabado Domingo
## Linkedin FALSE  FALSE      TRUE  FALSE   FALSE  FALSE    TRUE
## Facebook FALSE  FALSE     FALSE  FALSE   FALSE   TRUE    TRUE
  1. ¿Cuantas veces ocurrió esto? Usa sum() con TRUE
sum(visits, na.rm = TRUE)
## [1] 4
  1. ¿Cuantas veces no ocurrió esto? Usamos sum() con TRUE
sum(visits, na.rm = FALSE)
## [1] 4

2 Funciones

2.1 Internas

Una de las partes más poderosas de R se encuentra en su gran número de funciones cuyo objetivo es agilizar el análisis estadístico, facilitar el manejo de las bases de datos, graficar, crear mapas, etc.

R cuenta con funciones predefinidas dentro de su entorno interno que, aunque elementales, son de lo más útiles.

  1. ¿Cuál es el promedio de visitas al perfil de Linkedin en una semana? mean()
avg_li <- mean(linkedin)
avg_li
## [1] 10.85714
  1. ¿Cuál es la mediana de visitas al perfil de Linkedin en una semana? median()
med_li <- median(linkedin)
med_li
## [1] 13
  1. ¿Cuál es la varianza de visitas al perfil de Linkedin en una semana? var()
var_li <- var(linkedin)
var_li
## [1] 32.47619
  1. ¿Cuál es la desviación estándar de visitas al perfil de Linkedin en una semana? sd()
sd_li <- sd(linkedin)
sd_li
## [1] 5.698789

Es posible llevar a cabo operaciones y funciones al mismo tiempo.

  1. Suma los promedios de visitas de los perfil de Linkedin y Facebook
avg_sum <- mean(linkedin + facebook)
avg_sum
## [1] 22.28571
  1. Suma las desviaciones estándar de los perfiles de Linkedin y Facebook
sd_sum <- sd(linkedin + facebook)
sd_sum
## [1] 8.340949
  1. Construye una función del promedio de las visitas al perfil de Linkedin menos las visitas al perfil de Facebook en terminos absolutos. Usaabs.
x <- mean(abs(linkedin - facebook),
          na.rm = TRUE)
x
## [1] 4.571429

na.rm específica si se quitan o se dejan los valores nulos de un vector o una base de datos, por defecto la función está en FALSE.

Una forma de crear vectores personalizados es a través de la función seq()

  1. Crea una secuencia de números del 1 al 15 que vaya de uno en uno
seq(1, 15, by = 1)
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
  1. Crea una secuencia que vaya del 1 al 100 de diez en diez
seq(1, 100, by = 10)
##  [1]  1 11 21 31 41 51 61 71 81 91
  1. Crea una secuencia de cinco números que vaya de 1 a 10
seq(1, 10, length.out = 5)
## [1]  1.00  3.25  5.50  7.75 10.00

La función rep() permite repetir elementos

  1. Repite el número 2 diez veces.
rep(2, 10)
##  [1] 2 2 2 2 2 2 2 2 2 2
  1. Repite “hola” cuatro veces.
rep("Hola", 4)
## [1] "Hola" "Hola" "Hola" "Hola"
  1. Repite la secuencia 1, 2, 3, dos veces.
rep(c(1, 2, 3), 2)
## [1] 1 2 3 1 2 3
  1. Repite cada número entre 1 y 4 tres veces
rep(1:4, each = 3)
##  [1] 1 1 1 2 2 2 3 3 3 4 4 4

El problema con estas funciones es que ya están predeterminadas a trabajar con los objetos en cuestión, es decir, los parámetros de la función son constantes y, por ende, solo puede ser usada para los vectores de Linkedin y Facebook.

Si se tuviera un tercer vector, por ejemplo, twitter, y se imprimiera de nuevo x, el resultado no variaría dado que, el nuevo vector no forma parte de la función original.

Se podría cambiar la información del vector facebook por la de twitter, pero esto haría que se perdiera la información del primer vector en favor del segundo, lo que generalmente no es recomendable.

También se podría cambiar simplemente facebook por twitter desde el código original para operar la función, pero esto puede ser poco práctico cuando se tienen muchos vectores a sustituir.

Por lo tanto, en muchas ocasiones, será mejor crear funciones propias.

2.2 Sin parámetros

En R es posible declarar una función con la palabra function() seguida de un par de {} que contendrán los argumentos de la función. Se puede hacer toda clase de operaciones y procedimientos mediante una función, pero los resultados de esta no se mostrarán a menos que la misma sea llamada.

  1. Crea una función que imprima “Hola mundo” en la consola.
nombre_funcion <- function(){
  print("Hola mundo")
}  

nombre_funcion()
## [1] "Hola mundo"

Las funciones sin parámetros son aquellas que no contienen objetos al interior de los paréntesis (), por lo que básicamente trabajan con constantes.

  1. Construye un objeto que muestre el número de visitas a Facebook y otro que sume éste objeto con el de las visitas a LinkedIn
num_views_f <- 12
num_views_l <- 14

suma_l_f <- function(){#Parentesis vacios
   return(num_views_l + num_views_f)
   }

suma_l_f() 
## [1] 26

Esta función se llama suma_l_f(), cada vez que se le llama se sumaran de forma automática los objetos que la componen.

  1. Crea una función para obtener la proporción de las visitas de LinkedIn con respecto a las de Facebook
div_l_f <- function(){
   return(num_views_l / num_views_f)
   }

div_l_f() 
## [1] 1.166667

Esta función se llama div_l_f(), cada vez que se le llama se sumaran de forma automática los objetos que la componen.

  1. Crea una función que simule el lanzamiento de un dado.
dado <- function(){
   caras <- sample(1:6, size = 1)
   caras
    }

dado()
## [1] 3

Es posible guardar la función en otro objeto y usarla.

  1. Guarda la función dado en un objeto
d <- dado()
d
## [1] 3

El objeto d contiene a la función dado, y cada vez que se le llama se realizara la tarea de simular el lanzamiento de un dado, pero solamente eso. Si lo que se busca es crear una función que admite variables es necesario declarar los parámetros de la función.

  1. Eleva al cuadrado todos los números del 1 al 50.
a <- 1:50
b <- 2

potencia_a_b <- function(){
   return(a^b)
   }

potencia_a_b()
##  [1]    1    4    9   16   25   36   49   64   81  100  121  144  169  196  225
## [16]  256  289  324  361  400  441  484  529  576  625  676  729  784  841  900
## [31]  961 1024 1089 1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936 2025
## [46] 2116 2209 2304 2401 2500

2.3 Con parámetros

Cuando se declaran los parámetros la función puede recibir variables, al interior de los paréntesis, con las que operar.

  1. Crea una función que eleve un número al cuadrado.
pow_two <- function(a){ #Parámetros de entrada de la funcion
   a^2 #Cuerpo de la función
   }

La función admite un parámetro a y, entre llaves {}, colocamos el cuerpo de la función que eleva ese parámetro al cuadrado. Como se ha establecido un parámetro a manera de variable, cada vez que se llame la función pow_two, esta puede cambiar.

  1. ¿¿Cuánto es el cuadrado de 12?
pow_two(12)
## [1] 144

Es posible colocar más de un parámetro en una función.

  1. Crea una función que lleve por nombre suma con dos parámetros x y y. Obten el resultado de 10 más 40.
suma <- function(x, y){ 
   q <- x + y 
   return(q)
   }

Al interior de ella se encuentra el cuerpo de la misma, es decir, aquello que la función debe realizar y, finalmente,

w <- suma(10, 40)
w
## [1] 50

Una vez que la función ha sido construida puede ser llamada, siempre y cuando se cubra el requisito de llenar todos los parámetros necesarios para operarla. Además, es posible guardar el resultado en un objeto.

Como las variables x y y no fueron declaradas de antemano se puede introducir cualquier tipo de parámetro, siempre y cuando no rompa la lógica de la operación en el cuerpo de la función.

a <- suma(20, 80)
a
## [1] 100

Estas funciones pueden ser combinadas con todas aquellas pertenecientes a las distintas librerías de R.

  1. Obten la suma de los valores absolutos de -2 más 3.
sum_abs <- function (b,c){
   abs(b) + abs(c)
   }

sum_abs(-2,3)
## [1] 5

También es posible combinar las funciones con strings para obtener resultados más intuitivos.

pow_two <- function(x) {
   y <- x ^ 2
   print(paste(x, "elevado al cuadrado es", y))
   return(y)
   }

pow_two(5)
## [1] "5 elevado al cuadrado es 25"
## [1] 25

Esto mismo aplica para los funciones sin parámetros y las funciones con parámetros por defecto.

2.4 Con parámetros por defecto

Es posible construir una función con parámetros por defecto, es decir, una función que permita indicar los datos de entrada variables y datos de entrada constantes pero modificables. La sintaxis de esta función es:

nombre_funcion <- function(Datos de entrada, Datos de entrada por defecto){
  Cuerpo de la función
  return(Dato de salida) 
  }
  1. Construye una función que permita el cálculo de la raíz cuadrada de un número.

Para que esta función haga su trabajo se requiere de dos parámetros x y n. El primero de ellos debe ser introducido por el usuario, el segundo puede o no serlo.

raiz <- function(x, n = 2){
   p <- x^(1 / n) 
   return(p) 
}

raiz(100)
## [1] 10

De no introducirse el parámetro, entonces, por defecto, el valor de n será el establecido de antemano en la función, es decir, 2 de tal forma obtendremos como resultado la raíz cuadrada del parámetro x.

No obstante, podemos cambiar valor de n con el objetivo de sacar una raíz cúbica, cuadrática, etc.

  1. Obtén la raíz cúbica de 100
raiz(100, 3) #Raiz cubica
## [1] 4.641589
  1. Crea la función potencia y obtén el cuadrado de 2
potencia <- function(x, n = 2){
  w <- x^n 
  return(w)}

potencia(2)
## [1] 4
  1. Con la función anterior eleva 7 a la 98
potencia(7, 98) 
## [1] 6.600972e+82

2.5 Scoping

Es posible hacer un scoping en las funciones, esto implica que las variables definidas al interior de una función no son accesibles fuera de la misma, por ejemplo, en la siguiente función se simula el lanzamiento de dos dados, pero no es posible acceder a dado1 y dado2 desde fuera de la función. Para hacer modificaciones se debe entrar a la función.

dos_dados <- function() {
  posibilidades <- 1:6
  dado1 <- sample(posibilidades, size = 1)
  dado2 <- sample(posibilidades, size = 1)
  dado1 + dado2
}

dos_dados()
## [1] 7

3 Condicionales

3.1 if

La sentencia if se utiliza para comprobar una condición, si esta es verdadera, entonces, se procesan las sentencias dentro de la misma. La estructura es la siguiente:

if(Condición){
  #Sentencias a realizar si la condición es verdadera
  }
  1. Considere un condicional que verifique si 4 es menor que 10
x <- 4

if(x < 10){
   print("x es menor que 10")
   }
## [1] "x es menor que 10"
  1. Como en el caso anterior verifique si 12 es menor que 10
x <- 12

if(x < 10){
   print("x es menor que 10")
   }

No ha pasado nada, porque la condición es falsa.

  1. Verifique que -10 sea mayor o igual a -100
q <- -10

if(q >= -100){
   print("q es mayor o igual que -100")
   }
## [1] "q es mayor o igual que -100"

Es posible usar strings con los condicionales

nombre <- "JOSE"

if(nombre == "JOSE"){
   print("Tu nombre es JOSE")
   }
## [1] "Tu nombre es JOSE"
  1. A continuación se presentan dos objetos, examina si red es igual a Linkedin, de ser así, imprime “Muestra la información de Linkedin”.
red <- "LinkedIn"
num_visitas <- 14

if (red == "LinkedIn") {
   print("Muestra la información de Linkedin")
   }
## [1] "Muestra la información de Linkedin"
  1. Si el número de visitas es mayor a 15, entonces, imprime “Eres popular”
if (num_visitas > 15) {
   print("Eres popular")
   }

No ocurre nada porque la condición no se cumple.

3.2 if-else

La sentencia if-else se utiliza para comprobar una condición, si esta es verdadera, entonces, se procesan las sentencias dadas, de lo contrario, se procede a realizar otro conjunto de sentencias en su lugar. La estructura es la siguiente:

if(Condición){
  Sentencias a realizar si la condición es verdadera
  }else{
  Sentencias a realizar si la condición es falsa
  }
  1. Considere un condicional if-else que verifique si un número es menor que 10, de ser así imprima “Menor que 10”, de lo contrario, “Mayor o igual que 10”
x<- 4

if(x < 10){
   print("Menor que 10")
   }else{
      print("Mayor o igual que 10")
      }
## [1] "Menor que 10"
  1. Considere un condicional if-else que verifique si -10 es mayor o igual que -100, de ser así imprima “Es mayor o igual que -100”, de lo contrario, “Es menor que -100”
q <- -10

if(q >= -100){
   print("Es mayor o igual que -100")
   }else{
      print("Es menor que -100")
      }
## [1] "Es mayor o igual que -100"
  1. Usando el objeto nombre verifica la identidad de “JOSE”
nombre <- "ALEJANDRO"

if(nombre == "JOSE"){
   print("Tu nombre es JOSE")
   }else{
      print("Tu nombre no es JOSE")
      }
## [1] "Tu nombre no es JOSE"
  1. Si z es diferente de 2, entonces, “z es diferente de 2”, de lo contrario “z es igual a 2”. Asume que z es igual a 2.
z <- 2

if(z != 2){
   print("z es diferente de 2")
   }else{
      print("z es igual a 2")
      }
## [1] "z es igual a 2"
  1. Con los objetos de medium y num_visitas construye un if-else. Si medium es igual a LinkedIn, entonces, imprime “Muestra la información de Linkedin”, de lo contrario, imprime “Medium desconocido”
if (red == "LinkedIn"){
   print("Muestra la información de Linkedin")
   }else{
      print("Red desconocida")
      }
## [1] "Muestra la información de Linkedin"
  1. Si num_visitas es menor a 15, entonces, imprime “Eres popular”, de lo contrario, imprime “Intenta ser más visible”*
if (num_visitas > 15){
   print("Eres popular")
   }else{
      print("Intenta ser más visible")
      }
## [1] "Intenta ser más visible"

Es posible crear más de un if y un else en una sentencia.

  1. Si red es igual a LinkedIn, entonces, imprime “Muestra la información de LinkedIn”, de lo contrario, si red es igual a Facebook, entonces, imprime “Muestra información de Facebook”, de lo contrario imprime “Unknown medium”*
if (red == "LinkedIn"){
   print("Muestra la información de LinkedIn")
   }else if(red == "Facebook"){
      print("Muestra información de Facebook")}else{
         print("Red desconocida")
         }
## [1] "Muestra la información de LinkedIn"
  1. Si num_visitas es mayor a 15, entonces, imprime “Eres popular”, de lo contrario, si num_visitas es menor o igual que 15 y mayor que 10, entonces, imprime “Tu número de visitas es promedio”, de lo contrario imprime “Intenta ser más visible”*
if (num_visitas > 15){
   print("Eres popular")
   }else if(num_visitas <= 15 & num_visitas > 10){
      print("Tu número de visitas es promedio")}else{
         print("Intenta ser más visible")
         }
## [1] "Tu número de visitas es promedio"

Se puede combinar todo con los operadores relacionales. Recuerde que en programación no hay una sola respuesta, se puede llegar al resultado correcto de distintas formas. Lo importante es no romper la lógica de la sintaxis.

3.3 ifelse

Se recomienda utilizar el condiconal ifelse, cuando la condición en juego tiene más de una expresión, por ejemplo, cuando se realizan comparaciones con vectores.

Considere el vector que representa las calificaciones finales de la asignatura de álgebra de 8 estudiantes. Después compare para saber quiénes obtuvieron notas por debajo de 70.

nota <- c(50,60,49,87,92,30,90,85)
nota < 70 
## [1]  TRUE  TRUE  TRUE FALSE FALSE  TRUE FALSE FALSE

Al realizar la comparación retorna un vector de TRUE o FALSE, así que se tienen muchas expresiones a partir de una condición.

Por tanto, para condiciones de este tipo se sugiere utilizar el condicional ifelse, para hacer el resultado más intuitivo. Su estructura es la siguiente:

ifelse(Condición, 
       Sentencia si la condición es verdadera; Sentencia si la condición es falsa
       )

Por ejemplo, considere el vector anterior e identifique que estudiantes están aprobados o reprobados, teniendo en cuenta que la calificación de aprobación es mayor o igual a 70 puntos.

ifelse(nota >= 70,
       "Aprobado", "Reprobado")
## [1] "Reprobado" "Reprobado" "Reprobado" "Aprobado"  "Aprobado"  "Reprobado"
## [7] "Aprobado"  "Aprobado"

Incluso es posible operar al interior de este condicional, por ejemplo, retomando el vector nota, suponga que a todos aquellos que tienen una nota superior a 70 se les darán 10 puntos más, de lo contario se les quitarán 10 puntos.

ifelse(nota >= 70,
       nota + 10, nota - 10)
## [1]  40  50  39  97 102  20 100  95

Este tipo de condicional es útil cuando se trabaja con variables categóricas y deben ser convertidas a numéricas.

  1. El vector z representa la respuesta dada por ciertas personas acerca de su sexo. Transforma la variable de tal forma que 1 sea Femenino y 0 sea Masculino haciendo uso de un ifelse
z <- c(1,0,0,0,1,0,1,1,1,0,1)

ifelse(z == 1,
       "Femenino","Masculino")
##  [1] "Femenino"  "Masculino" "Masculino" "Masculino" "Femenino"  "Masculino"
##  [7] "Femenino"  "Femenino"  "Femenino"  "Masculino" "Femenino"
  1. A continuación se presentan los objetos li y fb, si las visitas a LinkedIn son mayores o iguales que 15 y las de Facebook son mayores o iguales que 15, entonces, suma las visitas de las dos redes, multiplícalas por 2 y guarda eso en sms, de lo contario, si las visitas a LinkedIn son menores que 10 y las de Facebook son menores que 10, entonces, suma las visitas de las dos redes, multiplícalas por 0.5 y guarda eso en sms, de lo contario, solo suma las visitas de Linkedin y Facebook. Finalmente, imprime el resultado.
li <- 15
fb <- 9

if(li >= 15 & fb >= 15){
   sms <- (li + fb) * 2
   }else if(li < 10 & fb < 10){
      sms <- (li + fb) * 0.5
      }else{
         sms <- li + fb
         }

sms
## [1] 24

4 Loops

Un loop, también conocido como ciclo o bucle, es útil cuando se necesita repetir varias veces una secuencia de acciones. Existen dos tipos de ciclos, los while y los for.

4.1 while

while es la estructura básica que permite repetir varias veces una secuencia de operaciones, mientras se cumpla una determina condición, la estructura es:

while(Condición){
  Sentencias que se repiten si la condición es verdadera
  }

A continuación, se creará un contador que imprima los números del 1 al 10

i <- 1

while(i <= 10){
   print(i)
   i <- i + 1
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

Para empezar se crea la variable i (contador) con valor de 1, después se construye un ciclo que imprima números hasta que i sea menor o igual que 10.

La lógica de este ejercicio radica en el cuerpo del loop que, en cada ciclo, va sumando i + 1, es decir, en el primer ciclo se imprime el primer i al que asignamos un valor de 1, después a i se le asigna el valor de i + 1, e inicia un nuevo loop.

Las operaciones realizadas en el primer ciclo hacen que i ahora valga 2, este es el nuevo valor de i, a este resultado se le suma de nueva cuenta 1, y así sucesivamente hasta llegar a i <= 10. Llegado este punto el loop se detiene y la función termina.

  1. ¿Qué hace este contador?
contador <- 8 

while (contador <= 10) {
  print(contador)
  contador <- contador + 1
}
## [1] 8
## [1] 9
## [1] 10
  1. ¿Qué resultados da suma y contador?
contador <- 1  
suma <- 0

while (contador <= 5) {
  suma <- suma + contador
  contador <- contador + 1
}

suma
## [1] 15
contador
## [1] 6
  1. La empresa ETN controla los límites de velocidad de sus conductores, esta no puede ser mayor a los 30km/h. Suponga que un camión esta viajando a 64km/h. Crea un loop que advierta al conductor lo rápido que va, de tal forma que, en cada ciclo, la velocidad disminuya 10km/h hasta alcanzar la velocidad límite establecida.
velocidad <- 64
tiempo <- 10

while(velocidad > 30){
   print("Baja la velocidad")
   velocidad <- velocidad - tiempo
}
## [1] "Baja la velocidad"
## [1] "Baja la velocidad"
## [1] "Baja la velocidad"
## [1] "Baja la velocidad"
velocidad
## [1] 24
  1. Inspecciona velocidad, ¿por qué se alcanza ese resultado? Arrancamos en 64km/h, ero cada ciclo que pasa reducimos la velocidad en 10km/h, de tal forma que en la primera vuelta nuestra velocidad será de 54 (64-10) que es mayor a 30, por lo tanto, nuestro loop nos pedirá bajar más la velocidad.

La segunda vuelta del ciclo dará como resultado 44, por lo que nos volverá a advertir de la velocidad, y así sucesivamente hasta que la velocidad sea de 24km/h y, por ende, menor a 30km/h.

Puedes combinar condicionales y bucles para crear algoritmos más sofisticados.

  1. ¿Cuántas iteraciones se requieren para alcanzar el límite?
limite <- 5
valor <- 0

while(valor <= limite) {
   if(valor < 5){
      print("No alcanza el límite")
   }else{
      print("Límite alcanzado")
   }
   valor <- valor + 1
}
## [1] "No alcanza el límite"
## [1] "No alcanza el límite"
## [1] "No alcanza el límite"
## [1] "No alcanza el límite"
## [1] "No alcanza el límite"
## [1] "Límite alcanzado"
  1. Usando el objeto velocidad escribe un código que cumpla las siguientes condiciones
  1. Siempre y cuando la velocidad sea mayor que 30 imprime “Tu velocidad es” seguido del valor de la velocidad (en el primer ciclo será = 64).

  2. Si la velocidad es mayor a 48, entonces, imprime “Baja la velocidad por favor”. Después, a la velocidad resta 11 y guarda el resultado en velocidad (el resultado es = 53).

Observa que todo este chunk esta sujeto al primer while, por lo que, terminando el primer if, regresamos al while con el nuevo resultado de velocidad, es decir, 53 que sigue siendo mayor a 30, por lo que inicia un nuevo bucle de while e inicia de nuevo el if

  1. Si la velocidad es mayor a 48, entonces, imprime “Slow down big time!”. Después, a la velocidad resta 11 y guarda el resultado en speed* (el resultado es = 42).

Ahora inicia el tercer ciclo del while, dado que 42 sigue siendo mayor que 30, pero el if ha terminado, puesto que la condición para que este se ejecute es que la velocidad deber ser mayor a 48, todo esto se podría leer como:

  1. Siempre y cuando la velocidad sea mayor que 30 imprime “Tu velocidad es” seguido del valor de la velocidad. Si la velocidad es mayor a 48, entonces, imprime “Baja la velocidad por favor”, de lo contrario, imprime “Baja la velocidad”, luego a la velocidad réstale 6 y guarda el resultado* que sería = a 36.

Como 36 sigue siendo mayor a 30 el while sigue activo, por lo que inicia un cuarto ciclo y, ya sin el if pasamos directamente al else. Esto se lee como

  1. Siempre y cuando la velocidad sea mayor que 30 imprime “Tu velocidad es” seguido del valor de la velocidad. Si la velocidad es mayor a 48, entonces, imprime “Slow down big time!”, de lo contrario, imprime “Slow down”, luego a la velocidad réstale 6 y guarda el resultado* que sería de 30.

Por lo tanto, como 30 ya no es estrictamente mayor a 30 el while termina.

velocidad <- 64

while (velocidad > 30) {
  print(paste("Tu velocidad es", velocidad))
  if (velocidad > 48) { 
    print("Baja la velocidad por favor")
    velocidad <- velocidad - 11
} else {
    print("Baja la velocidad")
    velocidad <- velocidad - 6
  }
}
## [1] "Tu velocidad es 64"
## [1] "Baja la velocidad por favor"
## [1] "Tu velocidad es 53"
## [1] "Baja la velocidad por favor"
## [1] "Tu velocidad es 42"
## [1] "Baja la velocidad"
## [1] "Tu velocidad es 36"
## [1] "Baja la velocidad"
velocidad
## [1] 30

Si se ejecuta un while con una condición que nunca será FALSE, este no se detendrá creándose un ciclo infinito. En términos prácticos esto agotaría la RAM de la computadora hasta trabarla, por lo que tendría que ser reiniciada. Revisa muy bien los loops antes de correrlos.

Con respecto a esta última observación es necesario describir un break que, como su nombre lo dice, puede romper un ciclo.

Observe el siguiente código, es casi igual al anterior, con la salvedad de que ahora la velocidad inicia en 88 y tenemos un nuevo if. Este nuevo condicional nos dice que si la velocidad es mayor a 80, entonces, rompa el ciclo.

velocidad <- 88

while(velocidad > 30){
  print(paste("Tu velocidad es", velocidad))
   if(velocidad > 80){
      break   
      }
   if(velocidad){
      print("Baja la velocidad por favor")
      velocidad <- velocidad - 11
      }else{
         print("Baja la velocidad")
         velocidad <- velocidad - 6
      }
   }
## [1] "Tu velocidad es 88"

Como 88 es mayor a 80 todo el loop se detiene.

4.2 for

El bucle for en R es una iteración repetitiva de cualquier sentencia, donde cada iteración evalúa una misma sentencia a través de los elementos de un vector.

La diferencia con while es que el primero trabaja sobre una variable, mientras que el segundo trabaja bajo una condición. La sintaxis es:

for(i in vector){
  Sentencias 
  }

A continuación, se presenta un ciclo de potencias para los primeros 10 números naturales.

for(i in 1:10){
   print(i^2)
   }
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25
## [1] 36
## [1] 49
## [1] 64
## [1] 81
## [1] 100

Lo primero es especificar que el contador i es un vector que va de 1 a 10. Después, se pide que cada uno de esos números sean elevados al cuadrado. El ciclo toma el primer número del vector, es decir, 1, lo eleva al cuadrado, y nos da el resultado que es 1. El siguiente ciclo toma el segundo valor del vector que es 2, lo eleva al cuadrado, y arroja el resultado que es 4, y así sucesivamente hasta llegar al 10.

Imprime una lista con nombres a través de un for

for(j in c("MARIA","MIGUEL","ISMAEL","JOSE","ANGEL","TEO")){
   print(j)
   }
## [1] "MARIA"
## [1] "MIGUEL"
## [1] "ISMAEL"
## [1] "JOSE"
## [1] "ANGEL"
## [1] "TEO"

Este tipo de ciclos no solo son sencillos, también son muy versátiles pues, no solo se limita a vectores, también funciona con listas.

Aquí hay una lista con la población, alcaldía y capital de la CDMX, con ayuda de for es posible recorrerla toda

mex <- list(pop = 8405837, 
            barrios = c("Azcapo","MHidal", "Xochi", "Izta", "Coyo"),
            capital = FALSE)

for (n in mex){
   print (n)
   }
## [1] 8405837
## [1] "Azcapo" "MHidal" "Xochi"  "Izta"   "Coyo"  
## [1] FALSE

También funciona con una matriz. Aquí hay un juego de Gato, para ello se construyó una matriz que representa las posiciones del juego con sus respectivas “X” y “O”

a <- c("O", NA, "X")
b <- c(NA, "O", "O")
c <- c("X", NA, "X")
            
row_names <- c(a, b, c)
            
gato <- matrix(row_names,
               byrow = TRUE, 
               nrow = 3)
gato
##      [,1] [,2] [,3]
## [1,] "O"  NA   "X" 
## [2,] NA   "O"  "O" 
## [3,] "X"  NA   "X"

El objetivo del ejercicio es recorrer cada una de las casillas del Gato y saber que contienen, para ello necesitamos anidar un for dentro de otro for, parece complicado, pero solo se debe comprender qué se busca y cómo se encuentra.

Un Gato se compone de 9 casillas organizadas en filas y columnas de \(3 * 3\), para recorrer todas ellas se puede iniciar con el primer elemento ubicado en la fila 1 columna 1 y comenzar a leer de derecha izquierda, de tal forma que, el segundo elemento se encuentra en la fila 1 columna 2, y el tercero en la fila 1 columna 3.

¿Cómo se traduce a un lenguaje de programación? Se debe construir un primer contador que vaya del primer elemento al tercer elemento de la fila 1. Como se trata de una matriz, es posible identificar todas las filas como una secuencia, así que el primer for parte de i y avanza de uno en uno sobre toda la fila de la matriz gato.

Para leer las columnas se usa el for interno o anidado, que se escribe casi igual que el anterior, solo que esta vez la secuencia corre sobre las columnas, de tal forma que el segundo for parte de j y avanza de uno en uno sobre toda la columna de la matriz gato.

for (i in 1:nrow(gato)) {
  for (j in 1:ncol(gato)) {
    print(paste("En la fila", i, "y columna", j, "el Gato contiene", gato[i,j]))
  }
}
## [1] "En la fila 1 y columna 1 el Gato contiene O"
## [1] "En la fila 1 y columna 2 el Gato contiene NA"
## [1] "En la fila 1 y columna 3 el Gato contiene X"
## [1] "En la fila 2 y columna 1 el Gato contiene NA"
## [1] "En la fila 2 y columna 2 el Gato contiene O"
## [1] "En la fila 2 y columna 3 el Gato contiene O"
## [1] "En la fila 3 y columna 1 el Gato contiene X"
## [1] "En la fila 3 y columna 2 el Gato contiene NA"
## [1] "En la fila 3 y columna 3 el Gato contiene X"

Un for evalúa variable por variable, entonces, el primer ciclo del primer for inicia y se detiene en el elemento 1 (fila 1, columna 1) y, posteriormente, inicia el primer ciclo del segundo for que también se detiene en el elemento 1 (columna 1, elemento 1).

El ciclo debe continuar, dado que no se han terminado de evaluar todos los elementos del primer for, es decir, este for sigue abierto, por ende, sigue leyendo la fila 1 con la salvedad que se mueve un espacio a la derecha para encontrar el elemento 2 (fila 1, columna 2), e inicia el segundo ciclo del segundo for que se detiene en el elemento 2 (columna 2, fila 1).

Hay un entrecruzamiento entre los dos for en el elemento 2 después de todo, fila 1, columna 2, es igual que columna 2, fila 1, por eso se pide al final del código gato[i,j] para que identifique lo que hay al interior de esos cruces.

Antes de imprimir los resultados piensa en la estructura con la que el algoritmo esta “leyendo” el problema, ¿qué aparece como resultado del primer for, cómo se puede leer?, ¿qué aparece en el segundo y en el tercero?

En la fila 1 y columna 1 el Gato contiene O En la fila 1 y columna 2 el Gato contiene NA” En la fila 1 y columna 3 el Gato contiene X”

  1. A continuación, se presenta un vector con información de LinkedIn, construya un for que sea válido para todos los elementos del vector mayores a 10, de ser así, imprima “Eres popular!”, de lo contario, imprima “Intenta ser más visible!”. Finalmente, imprime el resultado.
linkedin <- c(16, 9, 13, 5, 2, 17, 14)

for(li in linkedin){
  if(li > 10 ){
     print("Eres popular!")}
   else{
      print("Intenta ser más visible!")}
   print(li)
   }
## [1] "Eres popular!"
## [1] 16
## [1] "Intenta ser más visible!"
## [1] 9
## [1] "Eres popular!"
## [1] 13
## [1] "Intenta ser más visible!"
## [1] 5
## [1] "Intenta ser más visible!"
## [1] 2
## [1] "Eres popular!"
## [1] 17
## [1] "Eres popular!"
## [1] 14
  1. Repita el código anterior anidando dos for, de tal forma que,
  • Primer for Este ciclo será valido para todos los elementos del vector LinkedIn con valores mayores a 10, de ser así, imprime “Eres popular!”, de lo contario, imprime “Intenta ser más visible!”.

  • Segundo for Si el primer elemento del vector es mayor a 16, rompe el ciclo e imprime “Esto es rídiculo”, pero…

  • Tercer for Si el primer elemento del vector es menor a 5,* next salta el resto del código y regresa al primer for

linkedin <- c(16, 9, 13, 5, 2, 17, 14)

for(li in linkedin){
   if (li > 10){
      print("Eres popular!")
      }else{
         print("Intenta ser más visible!")
      }
   }
## [1] "Eres popular!"
## [1] "Intenta ser más visible!"
## [1] "Eres popular!"
## [1] "Intenta ser más visible!"
## [1] "Intenta ser más visible!"
## [1] "Eres popular!"
## [1] "Eres popular!"
   for(li in linkedin){
      if(li > 16){
         break
         }
      print("Esto es ridículo")
      }
## [1] "Esto es ridículo"
## [1] "Esto es ridículo"
## [1] "Esto es ridículo"
## [1] "Esto es ridículo"
## [1] "Esto es ridículo"
      for(li in linkedin){
         if (li < 5){
            next
            }
         print ("Esto es vergonzoso!")
         }
## [1] "Esto es vergonzoso!"
## [1] "Esto es vergonzoso!"
## [1] "Esto es vergonzoso!"
## [1] "Esto es vergonzoso!"
## [1] "Esto es vergonzoso!"
## [1] "Esto es vergonzoso!"

5 Apply

La familia de funciones apply permite crear bucles sin tantas redundancias, por lo tanto, de manera más sencilla.

5.1 apply

Esta función toma una base de datos o matriz como entrada y regresa un vector o una lista.

  1. Crea una matriz de \(5\cdot 6\) y suma los elementos de cada columna
m1 <- matrix(C<-(1:10), nrow = 5, ncol = 6)
m1
##      [,1] [,2] [,3] [,4] [,5] [,6]
## [1,]    1    6    1    6    1    6
## [2,]    2    7    2    7    2    7
## [3,]    3    8    3    8    3    8
## [4,]    4    9    4    9    4    9
## [5,]    5   10    5   10    5   10
a_m1 <- apply(m1, 2, sum)
a_m1
## [1] 15 40 15 40 15 40

5.2 lapply

Existe una manera más sencilla de hacer loops en R y es a través de lapply, una función que permite ahorrar código siempre y cuando los datos a analizar se encuentren en una lista.

Esta función requiere de dos elementos, la lista a analizar, y la función que se aplicará a todos los elementos de dicha lista. El resultado de la función será una nueva lista del mismo tamaño que la original.

lapply(x, FUN)

A continuación, tenemos una lista con información de la CDMX, tenemos datos numéricos, strings y lógicos

cdmx <- list(pop = 18000000, 
             alcaldia = c("Cuauhtemoc","Benijto Juarez", "Miguel Hidalgo", "Coyoacan", "Iztapalapa"),
             capital = FALSE)
  1. Crea un loop para revisar que tipo de variables hay en la lista cdmx
for(info in cdmx){
   print(class(info))
   }
## [1] "numeric"
## [1] "character"
## [1] "logical"
  1. Usa lapply para para revisar que tipo de variables hay en la lista cdmx
lapply(cdmx, class)
## $pop
## [1] "numeric"
## 
## $alcaldia
## [1] "character"
## 
## $capital
## [1] "logical"

Recuerda que esto solo es posible porque cdmx se trata de una lista.

Ahora se presenta un vector con estados de la república mexicana:

estados <- c("Jalisco", "CDMX", "Chiapas", "Michoacan", "Oaxaca", "Jalisco", "Nuevo Leon")
  1. Crea un for que cuenta las letras que componen los nombres de los estados. Necesitas un contador, length() para contar el tamaño del vector y nchar() para contar el tamaño de las palabras.
num_chars <- numeric()

for(i in 1:length(estados)){
   num_chars[i] <- nchar(estados[i])
   }

num_chars
## [1]  7  4  7  9  6  7 10
  1. Usando lapply cuenta las letras que componen los nombres de los estados. Al finalizar convierte la lista en un vector con unlist.
lapply(estados, nchar)
## [[1]]
## [1] 7
## 
## [[2]]
## [1] 4
## 
## [[3]]
## [1] 7
## 
## [[4]]
## [1] 9
## 
## [[5]]
## [1] 6
## 
## [[6]]
## [1] 7
## 
## [[7]]
## [1] 10
unlist(lapply(estados, nchar))
## [1]  7  4  7  9  6  7 10

No lo olvides lapply siempre regresa una lista, pero generalmente trabajamos con vectores, por lo que unlist no solo es útil, es esencial.

Además, lapply puede combinarse con funciones, aquí se presenta el precio de la tortilla en las últimas seis semanas.

p_maiz_l <- list(10.2, 10.4, 10.6, 11, 11.4, 11.6)
  1. Crea una función que multiplique por dos el precio de la tortilla en las últimas seis semanas.
doble <- function(x){
   return(2 * x)
   }

#doble(p_maiz_l)

No es posible usar esta función debido a que p_maiz_l es una lista, no un vector.

  1. Transforma la lista en vector y vuelve a correr la función anterior.
p_maiz_v <- unlist(p_maiz_l)

doble <- function(x){
   return(2 * x)
   }

doble(p_maiz_v)
## [1] 20.4 20.8 21.2 22.0 22.8 23.2
  1. Crea una función que multiplique por dos el precio de la tortilla en las últimas seis semanas usando lapply. Regresa el resultado como vector
resultado <- lapply(p_maiz_l, doble)
resultado
## [[1]]
## [1] 20.4
## 
## [[2]]
## [1] 20.8
## 
## [[3]]
## [1] 21.2
## 
## [[4]]
## [1] 22
## 
## [[5]]
## [1] 22.8
## 
## [[6]]
## [1] 23.2
unlist(resultado)
## [1] 20.4 20.8 21.2 22.0 22.8 23.2
  1. Crea una función que multiplique por un parámetro x el precio de las tortillas, de tal forma que, ahora podamos usar el múltiplo que queramos
p_maiz_l <- list(10.2, 10.4, 10.6, 11, 11.4, 11.6)
p_maiz_v <- unlist(p_maiz_l)

multiplicador <- function(x, factor){
   x * factor
   }

multiplicador(p_maiz_v, 3)
## [1] 30.6 31.2 31.8 33.0 34.2 34.8
  1. Crea una función igual que la anterior usando lapply. Regresa el resultado como un vector.
p3_maiz <- lapply(p_maiz_l, multiplicador, 3)
p3_maiz
## [[1]]
## [1] 30.6
## 
## [[2]]
## [1] 31.2
## 
## [[3]]
## [1] 31.8
## 
## [[4]]
## [1] 33
## 
## [[5]]
## [1] 34.2
## 
## [[6]]
## [1] 34.8
unlist(p3_maiz)
## [1] 30.6 31.2 31.8 33.0 34.2 34.8

Aquí tenemos un vector con los apellidos y fechas de nacimiento de algunos famosos pensadores.

filosofos <- c("MARX:1818", "SMITH:1723", "KEYNES:1883", "HAYEK:1899")
  1. Usa strsplit para separar los apellidos de las fechas a través de :.
split_filos <- strsplit(filosofos, split = ":")
split_filos
## [[1]]
## [1] "MARX" "1818"
## 
## [[2]]
## [1] "SMITH" "1723" 
## 
## [[3]]
## [1] "KEYNES" "1883"  
## 
## [[4]]
## [1] "HAYEK" "1899"
  1. Cambia las mayúsculas por minúsculas con tolower dentro de un lapply usando split_filos.
filos_min <- lapply(split_filos, tolower)
filos_min
## [[1]]
## [1] "marx" "1818"
## 
## [[2]]
## [1] "smith" "1723" 
## 
## [[3]]
## [1] "keynes" "1883"  
## 
## [[4]]
## [1] "hayek" "1899"
  1. Inspeccionamos este último elemento.
str(filos_min)
## List of 4
##  $ : chr [1:2] "marx" "1818"
##  $ : chr [1:2] "smith" "1723"
##  $ : chr [1:2] "keynes" "1883"
##  $ : chr [1:2] "hayek" "1899"

La función strsplit eliminó los : y separó la información, pero sigue siendo una lista, no se creó una columna.

  1. Dentro de filos_min selecciona solo a Smith 1723.
filos_min[2]
## [[1]]
## [1] "smith" "1723"
  1. Crea una función para seleccionar a Marx 1818 en la lista filos_min.
apellidos <- function(x){
   x[1]
   }

apellidos(filos_min)
## [[1]]
## [1] "marx" "1818"
  1. Crea una función que recorra todo filos_min.
apellidos <- function(x){
   for(x in filos_min)
      print(x)
   }

apellidos(filos_min)
## [1] "marx" "1818"
## [1] "smith" "1723" 
## [1] "keynes" "1883"  
## [1] "hayek" "1899"
  1. Usa lapply para que la función apellidos recorra cada uno de los elementos del vector y seleccione solo los apellidos.
apellidos <- function(x){
   x[1]
   }

apellidos <- lapply(filos_min, apellidos)
apellidos
## [[1]]
## [1] "marx"
## 
## [[2]]
## [1] "smith"
## 
## [[3]]
## [1] "keynes"
## 
## [[4]]
## [1] "hayek"
  1. Ahora seleccionemos las fechas de nacimiento
fechas <- function(x) {
  x[2]
  }

fechas <- lapply(filos_min, fechas)
fechas
## [[1]]
## [1] "1818"
## 
## [[2]]
## [1] "1723"
## 
## [[3]]
## [1] "1883"
## 
## [[4]]
## [1] "1899"

No son columnas, sin embargo lapply entiende que buscamos el segundo elemento de la lista.

5.3 sapply

La ventaja de lapply es que, al trabajar con listas, es posible guardar en ellas todo tipo de objetos y obtener como resultado una lista, pero si el resultado que vamos a obtener de una función solo regresa un tipo de objeto, entonces, usamos sapply.

Esta función toma una lista, vector o base datos y regresa un vector o matriz, por lo tanto, hace lo mismo que lapply pero no regresa una lista.

Su sintaxis es igual que lapply pues admite dos parámetros, los datos con los que trabajamos y la función que se aplica a dichos datos.

sapply(X,   # Vector o lista
       FUN, # Función a ser aplicada
       ..., # Argumentos adicionales para ser pasados a FUN
       simplify = TRUE, # Si FALSE devuelve una lista. Si "array" devuelve un array si es posible
       USE.NAMES = TRUE) # Si TRUE y si X es un vector de caracteres, usa los nombres de X

El siguiente vector contiene solo datos de tipo string:

estados <- c("Jalisco", "CDMX", "Chiapas", "Michoacan", "Oaxaca", "Jalisco", "Nuevo Leon")
  1. Usa sapply y nchar para contar las letras de los nombres de cada Estado
sapply(estados, nchar)
##    Jalisco       CDMX    Chiapas  Michoacan     Oaxaca    Jalisco Nuevo Leon 
##          7          4          7          9          6          7         10

Parece que obtuvimos el mismo resultado que con un lapply, pero no es así. Primero, estados es un vector, no una lista.sapply puede trabajar con ambos.

Segundo, sapply realiza la función nchar en cada uno de los elementos del vector y, de manera interna, usa la función simply2array para convertir las listas que dan como resultado lapply en un vector, es decir, sapply crea un bucle.

Finalmente, es de resaltar que sapply se tomó la molestia de nombrar el vector. Si no estás interesado en conservar los nombres usa USE.NAMES = FALSE.

sapply(estados, nchar, USE.NAMES = FALSE)
## [1]  7  4  7  9  6  7 10

Es posible usar funciones son sapply, pero qué pasa si la función que quieres realizar da como resultado un vector con dos variables en lugar de una.

  1. Crea una función que separe las letras y, a través de las funciones min y max, busca la letra con el menor y mayor valor alfabético de la palabra Jalisco.
primera_ultima <- function(nombre){
   letras <- strsplit(nombre, split = "")[[1]]
   c(primera = min(letras), ultima = max(letras))
   }

primera_ultima("Jalisco")
## primera  ultima 
##     "a"     "s"

La letra con menor valor es a y la de mayor valor es s.

  1. Usa la función en todo el vector de estados.
sapply(estados, primera_ultima)
##         Jalisco CDMX Chiapas Michoacan Oaxaca Jalisco Nuevo Leon
## primera "a"     "C"  "a"     "a"       "a"    "a"     " "       
## ultima  "s"     "X"  "s"     "o"       "x"    "s"     "v"

Esto ya no es un vector, es una matriz y la función sapply ya puso todos los nombres.

sapply simplifica los resultados de lapply, pero esto no siempre ocurrirá, puede que la función que apliques no arroje (output) un vector del mismo tamaño que aquello que hayas ingresado (input), por lo tanto, si hay dos variables y los tamaños de sus vectores no coinciden, no puede haber una matriz.

  1. Crea una función que devuelva un vector con todas las letras que fueron usadas en un string una sola vez, es decir, eliminando las letras repetidas. Úsala en “Oaxaca”.
letras_unicas <- function(nombre){
   letras <- strsplit(nombre, split = "")[[1]]
   unique(letras)
   }

letras_unicas("Oaxaca")
## [1] "O" "a" "x" "c"
  1. Usa lapply sobre el vector estados y aplica la función letras_unicas.
lapply(estados, letras_unicas)
## [[1]]
## [1] "J" "a" "l" "i" "s" "c" "o"
## 
## [[2]]
## [1] "C" "D" "M" "X"
## 
## [[3]]
## [1] "C" "h" "i" "a" "p" "s"
## 
## [[4]]
## [1] "M" "i" "c" "h" "o" "a" "n"
## 
## [[5]]
## [1] "O" "a" "x" "c"
## 
## [[6]]
## [1] "J" "a" "l" "i" "s" "c" "o"
## 
## [[7]]
## [1] "N" "u" "e" "v" "o" " " "L" "n"

lapply regresó una lista con vectores de letras.

  1. Usa sapply sobre el vector estados y aplica la función letras_unicas.
sapply(estados, letras_unicas)
## $Jalisco
## [1] "J" "a" "l" "i" "s" "c" "o"
## 
## $CDMX
## [1] "C" "D" "M" "X"
## 
## $Chiapas
## [1] "C" "h" "i" "a" "p" "s"
## 
## $Michoacan
## [1] "M" "i" "c" "h" "o" "a" "n"
## 
## $Oaxaca
## [1] "O" "a" "x" "c"
## 
## $Jalisco
## [1] "J" "a" "l" "i" "s" "c" "o"
## 
## $`Nuevo Leon`
## [1] "N" "u" "e" "v" "o" " " "L" "n"

sapply hace básicamente los mismo, dado que es lo mejor que puede hacer en este caso. Esto puede ser un problema porque siempre esperamos que regrese un vector cuando no siempre será así.

  1. Se presenta una lista compuesta por vectores con distintas temperaturas, usa lapply y sapply para encontrar las temperaturas mínimas y máximas en cada caso.
temp <- list(c(3,7,9,6,-1),
             c(6,9,12,13,5),
             c(4,8,3,-1,-3),
             c(1,4,7,2,-2),
             c(5,7,9,4,2),
             c(-3,5,8,9,4),
             c(-3,5,8,9,4))
temp
## [[1]]
## [1]  3  7  9  6 -1
## 
## [[2]]
## [1]  6  9 12 13  5
## 
## [[3]]
## [1]  4  8  3 -1 -3
## 
## [[4]]
## [1]  1  4  7  2 -2
## 
## [[5]]
## [1] 5 7 9 4 2
## 
## [[6]]
## [1] -3  5  8  9  4
## 
## [[7]]
## [1] -3  5  8  9  4
# Minima    
lapply(temp, min)
## [[1]]
## [1] -1
## 
## [[2]]
## [1] 5
## 
## [[3]]
## [1] -3
## 
## [[4]]
## [1] -2
## 
## [[5]]
## [1] 2
## 
## [[6]]
## [1] -3
## 
## [[7]]
## [1] -3
sapply(temp, min)
## [1] -1  5 -3 -2  2 -3 -3
# Maxima
lapply(temp, max)
## [[1]]
## [1] 9
## 
## [[2]]
## [1] 13
## 
## [[3]]
## [1] 8
## 
## [[4]]
## [1] 7
## 
## [[5]]
## [1] 9
## 
## [[6]]
## [1] 9
## 
## [[7]]
## [1] 9
sapply(temp, max) 
## [1]  9 13  8  7  9  9  9

Al inspeccionar los elementos veras que no son los mismos resultados.

  1. Crea una función que calcule las temperaturas máximas y mínimas promedio, luego usa lapply y sapply
extremes_avg <- function(temp){
   (min(temp) + max(temp)) / 2
   }
    
sapply(temp, extremes_avg)
## [1] 4.0 9.0 2.5 2.5 5.5 3.0 3.0
lapply(temp, extremes_avg)
## [[1]]
## [1] 4
## 
## [[2]]
## [1] 9
## 
## [[3]]
## [1] 2.5
## 
## [[4]]
## [1] 2.5
## 
## [[5]]
## [1] 5.5
## 
## [[6]]
## [1] 3
## 
## [[7]]
## [1] 3
  1. Crea una función que calcule las temperaturas extremas, tanto mínimas cómo máximas de cada día, usa lapply y sapply.
extremes <- function(temp){
   c(min = min(temp), 
     max = max(temp))
   }
    
sapply(temp,extremes)
##     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## min   -1    5   -3   -2    2   -3   -3
## max    9   13    8    7    9    9    9
lapply(temp,extremes)
## [[1]]
## min max 
##  -1   9 
## 
## [[2]]
## min max 
##   5  13 
## 
## [[3]]
## min max 
##  -3   8 
## 
## [[4]]
## min max 
##  -2   7 
## 
## [[5]]
## min max 
##   2   9 
## 
## [[6]]
## min max 
##  -3   9 
## 
## [[7]]
## min max 
##  -3   9
  1. Crea una función que te permita encontrar las temperaturas debajo de los 0° centígrados, usa lapply y sapply.
bajo_cero <- function(x){
   return(x[x < 0])
   }
    
bajo_cero_s <- sapply(temp, bajo_cero)
bajo_cero_s
## [[1]]
## [1] -1
## 
## [[2]]
## numeric(0)
## 
## [[3]]
## [1] -1 -3
## 
## [[4]]
## [1] -2
## 
## [[5]]
## numeric(0)
## 
## [[6]]
## [1] -3
## 
## [[7]]
## [1] -3
bajo_cero_l <- lapply(temp, bajo_cero)
bajo_cero_l
## [[1]]
## [1] -1
## 
## [[2]]
## numeric(0)
## 
## [[3]]
## [1] -1 -3
## 
## [[4]]
## [1] -2
## 
## [[5]]
## numeric(0)
## 
## [[6]]
## [1] -3
## 
## [[7]]
## [1] -3
identical(bajo_cero_s, bajo_cero_l)
## [1] TRUE

Una última función

print_info <- function(x){
   cat("La temperatura promedio es", mean(x), "\n")
   }

sapply(temp, print_info)
## La temperatura promedio es 4.8 
## La temperatura promedio es 9 
## La temperatura promedio es 2.2 
## La temperatura promedio es 2.4 
## La temperatura promedio es 5.4 
## La temperatura promedio es 4.6 
## La temperatura promedio es 4.6
## [[1]]
## NULL
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## NULL
## 
## [[5]]
## NULL
## 
## [[6]]
## NULL
## 
## [[7]]
## NULL
lapply(temp, print_info)
## La temperatura promedio es 4.8 
## La temperatura promedio es 9 
## La temperatura promedio es 2.2 
## La temperatura promedio es 2.4 
## La temperatura promedio es 5.4 
## La temperatura promedio es 4.6 
## La temperatura promedio es 4.6
## [[1]]
## NULL
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## NULL
## 
## [[5]]
## NULL
## 
## [[6]]
## NULL
## 
## [[7]]
## NULL

5.4 vapply

Al igual que sapply permite hacer ciclos rápidos e intenta simplificar el resultado. Sin embargo, cuando usas vapply debes explicitar que tipo resultado quieres. Su sintaxis es:

vapply(x,   # Vector o lista
       FUN, # Función a ser aplicada
       FUN.VALUE, 
       ..., # Argumentos adicionales para ser pasados a FUN
       USE.NAMES = TRUE) # Si TRUE y si X es un vector de caracteres, usa los nombres de X

Aquí tenemos un nuevo argumento FUN.VALUE que funciona para regresar el resultado de la función como mejor convenga. Usaremos el vector estados.

estados <- c("Jalisco", "CDMX", "Chiapas", "Michoacan", "Oaxaca", "Jalisco", "Nuevo Leon")
  1. Usa sapply con la función nchar para contar las letras de los elementos en el vector estados.
sapply(estados, nchar)
##    Jalisco       CDMX    Chiapas  Michoacan     Oaxaca    Jalisco Nuevo Leon 
##          7          4          7          9          6          7         10
  1. Usa vapply con la función nchar para contar las letras de los elementos en el vector estados.
vapply(estados, nchar, numeric(1))
##    Jalisco       CDMX    Chiapas  Michoacan     Oaxaca    Jalisco Nuevo Leon 
##          7          4          7          9          6          7         10

Todo parece igual, pero no es así. nchar es una función que regresa un vector numérico de tamaño 1. Lo que hicimos fue crear una plantilla para este resultado con FUN.VALUE = numeric(1), es decir, le indicamos a vapply que regresara un valor numérico de tamaño 1.

  1. Asigna las funciones a objetos e inspecciona su estructura
x <- sapply(estados, nchar)
y <- vapply(estados, nchar, numeric(1))
str(x)
##  Named int [1:7] 7 4 7 9 6 7 10
##  - attr(*, "names")= chr [1:7] "Jalisco" "CDMX" "Chiapas" "Michoacan" ...
str(y)
##  Named num [1:7] 7 4 7 9 6 7 10
##  - attr(*, "names")= chr [1:7] "Jalisco" "CDMX" "Chiapas" "Michoacan" ...

Mientras que los valores de x son de tipo integer, los de y son de tipo numeric, tal y como se pidió.

La diferencia entre vapply y sapply es que el primero es más seguro que el segundo, porque en el primero estoy especificando el tipo de resultado que quiero, lo que es imposible con sapply.

  1. Observa estas dos funciones, ¿puedes ver la diferencia?
info_temp <- function(x){
   c(min = min(x), 
     mean = mean(x), 
     max = max(x))
   }
    
vapply(temp, info_temp, numeric(3))
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## min  -1.0    5 -3.0 -2.0  2.0 -3.0 -3.0
## mean  4.8    9  2.2  2.4  5.4  4.6  4.6
## max   9.0   13  8.0  7.0  9.0  9.0  9.0

Aquí se específica que se buscan 3 resultados

info_temp <- function(x){
   c(min = min(x), 
     mean = mean(x), 
     median = median(x), 
     max = max(x))
   }

vapply(temp, info_temp, numeric(4))
##        [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## min    -1.0    5 -3.0 -2.0  2.0 -3.0 -3.0
## mean    4.8    9  2.2  2.4  5.4  4.6  4.6
## median  6.0    9  3.0  2.0  5.0  5.0  5.0
## max     9.0   13  8.0  7.0  9.0  9.0  9.0

Ahora se buscan 4 resultados

Recuerda * lapply se usa sobre una lista o vector y regresa una lista. * sapply se usa sobre una lista o vector y regresa un vector simplificado, si no puede, regresa la misma lista que lapply * vapply se usa sobre una lista o vector y explicita específicamente el formato del resultado

6 Funciones matemáticas

Vamos a hacer uso de algunas funciones populares en R

  • abs devuelve los valores absolutos
  • sum calcula la sumatoria de los datos
  • mean media aritmética
  • round redondea por default a 0, pero se puede modificar
  • seq genera secuencias especificando el inicio y el final
  • rep repite elementos de vectores y listas
  • sort ordena un vector de forma ascendente, trabaja con todo tipo de datos
  • rev invierte los elementos en una estructura de datos
  • str muestra la estructura de los datos
  • append une elementos a vectores o listas
  • is.* nos dice la clase de los objetos
  • as.* convierte los objetos de R de un tipo a otro
  • unlist transforma listas en vectores

Recuerda que es posible anidar las funciones, pero deben llevar el orden adecuado para llegar al resultado correcto.

  1. Obtén los valores absolutos del vector errores y súmalos.
errores <- c(1.9, -2.6, 4.0, -9.5, -3.4, 7.3)
sum(abs(round(errores)))
## [1] 29
  1. Usa rev para invertir el orden de vec1. Obtén el promedio de ambos.
vec1 <- c(1.5, 2.5, 8.4, 3.7, 6.3)
vec1
## [1] 1.5 2.5 8.4 3.7 6.3
vec2 <- rev(vec1)
vec2
## [1] 6.3 3.7 8.4 2.5 1.5
mean(c(abs(vec1), abs(vec2)))
## [1] 4.48

A continuación, se presentan las listas de LinkedIn y Facebook

linkedin <- list(16, 9, 13, 5, 2, 17, 14)
facebook <- list(17, 7, 5, 16, 8, 13, 14)
  1. Convierte las listas en vectores con unlist.
li_vec <- unlist(linkedin)
li_vec
## [1] 16  9 13  5  2 17 14
fb_vec <- unlist(facebook)
fb_vec
## [1] 17  7  5 16  8 13 14
  1. Une los vectores con append.
social_vec <- append(li_vec, fb_vec)
social_vec
##  [1] 16  9 13  5  2 17 14 17  7  5 16  8 13 14
  1. Ordenarlos con sort de forma descendente
sort(social_vec, decreasing = TRUE)
##  [1] 17 17 16 16 14 14 13 13  9  8  7  5  5  2
  1. Crea una secuencia y repítela siete veces
rep(seq(1, 7, by = 2), times = 7)
##  [1] 1 3 5 7 1 3 5 7 1 3 5 7 1 3 5 7 1 3 5 7 1 3 5 7 1 3 5 7
  1. Suma la cada elemento de la secuencia.
sum(rep(seq(1, 7, by = 2), times = 7))
## [1] 112

6.1 Regex

R cuenta con expresiones regulares (regex) que son secuencias de caracteres y metacaracteres. que forman un patrón de búsqueda que puedes usar para hacer un match entre strings. Puedes usar una expresión regular para revisar si ciertos patones existen en un texto para reemplazarlos con otros elementos o extraerlos, esto puede ser particularmente útil para limpiar una base de datos.

  • grep regresa un vector de índices de los elementos de x que hagan un match
  • grepl regresa un vector lógico que identifica los elementos de x que hacen match

Practiquemos con este vector de carros

carros <- c("mustang", "el camino", "road runner", "trans am", "impala")
  1. Usa grepl para ver cuál de estos carros tienen una m en su nombre
grepl(pattern = "m", x = carros)
## [1]  TRUE  TRUE FALSE  TRUE  TRUE
  1. ¿Qué carros inician su nombre con m?
grepl(pattern = "^m", x = carros)
## [1]  TRUE FALSE FALSE FALSE FALSE
  1. ¿Qué carros terminan su nombre con m?
grepl(pattern = "m$", x = carros)
## [1] FALSE FALSE FALSE  TRUE FALSE
  1. Compara el resultado que da grep con los anteriores.
grep(pattern = "m", x = carros)
## [1] 1 2 4 5

Como puedes observar esta función no devuelve un resultado de valores lógicos sino los índices de aquellos resultados que coinciden con la búsqueda. Este mismo resultado podríamos obtenerlo con grepl, pero necesitamos usar which

which(grepl(pattern = "m", x = carros))
## [1] 1 2 4 5

No siempre funcionan estas funciones, en algunas ocasiones debemos ser más específicos y apoyarnos en metacaracteres. Observa este vector con correos:

emails <- c("john.doe@ivyleague.edu", "education@world.gov", "dalai.lama@peace.org","invalid.edu", "quant@bigdatacollege.edu", "cookie.monster@sesame.tv")
  1. Busca los correos con terminación edu
#grepl(pattern = "edu", x = emails)

Algo anda mal, el problema es que hay correos que inician con edu, por lo que la función se esta “confundiendo”. Busca sus índices y guarda todo en un objeto.

hits <- grep(pattern = "edu", x = emails)
hits
## [1] 1 2 4 5

Definitivamente algo ando mal, realiza un subset para ver estos correos

emails[hits]
## [1] "john.doe@ivyleague.edu"   "education@world.gov"     
## [3] "invalid.edu"              "quant@bigdatacollege.edu"

Claro, como lo sospechamos, esta búsqueda ha fallado, pero esto aún no termina, hagamos una búsqueda más exacta con los metacaracteres.

  • @.*: incluimos (@*) porque debe tratarse de un correo, luego el punto permite hacer un match con cualquier carácter cero (.) o más () veces
  • \\.edu$: permite hacer un match con la parte (.edu) que se encuentra al final ($) del string. La parte \ le dice a R que queremos usar el (.) como un carácter
  1. Usa grepl para buscar esos correos con terminación (.edu). Esta vez la función tomara en cuenta el (@), el (.*) hará un match con cualquier carácter, mientras que \ hace que R “ignore” el punto como tal y lo tome con otro carácter a buscar.
grepl(pattern = "@.*\\.edu$", x = emails)
## [1]  TRUE FALSE FALSE FALSE  TRUE FALSE

Hemos encontrado dos correos, veamos donde se encuentran

hits<-grep(pattern = "@.*\\.edu$", x = emails)
hits
## [1] 1 5

Hagamos el subset para verlos.

emails[hits]
## [1] "john.doe@ivyleague.edu"   "quant@bigdatacollege.edu"

¡Voila! los tenemos.

6.2 sub y gsub

También es posible hacer reemplazos en estos errores tipográficos

  • sub permite remplazar el primer match buscando con otra string
  • gsub permite remplazar todos los matches con otra string
  1. En el vector de carros sustituye todas las a por o
sub(pattern = "a", replacement = "o", x = carros)
## [1] "mustong"     "el comino"   "rood runner" "trons am"    "impola"

¿Notas algo raro? Así es, no cambio todas las a por o, tenemos tons Am e impolA, eso es por que sub solo cambia la primera letra. Si quieres cambiar todas usa gsub

gsub(pattern = "a", replacement = "o", x = carros)
## [1] "mustong"     "el comino"   "rood runner" "trons om"    "impolo"

¿Recuerdas el operador lógico o |? Pues aquí también los puedes usar. Cambia la a o la r con un *_*

gsub(pattern = "a|r", replacement = "_", x = carros)
## [1] "must_ng"     "el c_mino"   "_o_d _unne_" "t__ns _m"    "imp_l_"
  1. En el vector de correos cambia los dominios por (ipn.edu)
sub(pattern="@.*\\.edu$", replacement = "@ipn.edu", x = emails)
## [1] "john.doe@ipn.edu"         "education@world.gov"     
## [3] "dalai.lama@peace.org"     "invalid.edu"             
## [5] "quant@ipn.edu"            "cookie.monster@sesame.tv"
  1. Las siguientes palabras tienen errores de ortografía, corrígelas con gsub
gsub("A", "a", c("Adios", "HAsta luego"))
## [1] "adios"       "Hasta luego"