Curso
En este tutorial vas a aprender varias funciones y utilidades sencillas y muy usadas en el lenguaje de programación R. Primero, veremos algunas funciones matemáticas. Después, exploraremos funciones relacionadas con las estructuras de datos de R, como la manipulación de listas anidadas, expresiones regulares, y el trabajo con horas y fechas.
Funciones matemáticas
Veamos las siguientes celdas de código que usan varias funciones matemáticas.
Primero defines dos vectores, x y y, que incluyen valores positivos y negativos.
x <- c(1.1, -2.3, -4.5)
y <- c(2.4,-44, -2.2)
print(x)
[1] 1.1 -2.3 -4.5
print(y)
[1] 2.4 -44.0 -2.2
Tomemos estos dos vectores x y y para:
- Sacar sus valores absolutos,
- Redondearlos a cero decimales,
- Sumar cada uno y
- Finalmente, calcular la media de ambos.
mean(c(sum(round(abs(x))),sum(round(abs(y)))))
27.5
¿Qué tal si descompones la línea anterior en partes pequeñas y miras de cerca cada función?
- La función
abs()toma el valor positivo o absoluto de los elementos de los vectoresxyy.
Por ejemplo, al aplicar abs() a x y y, obtienes todos los valores positivos, como se muestra abajo.
abs(x)
- 1.1
- 2.3
- 4.5
abs(y)
- 2.4
- 44
- 2.2
- La siguiente es la función
round(). Su cometido es redondear la entrada. Acepta un argumento adicional para indicar a cuántos decimales quieres redondear. Por defecto,round()redondea a cero decimales.
Por ejemplo, tras aplicar round() a los vectores x y y, la salida sería la siguiente:
round(x)
- 1
- -2
- -4
round(y)
- 2
- -44
- -2
- La función
sum()calcula la suma de los elementos de un vector o matriz. Por ejemplo, si pasas un vector fila como argumento, devolverá un escalar con la suma de todos sus elementos.
En este caso, se pasan los vectores x y y a sum(). R calcula la suma de sus elementos y devuelve un escalar.
sum(abs(round(x)))
7
sum(abs(round(y)))
48
- Por último, la función
mean()(media) calcula la media aritmética: suma un conjunto de valores numéricos y los divide entre el número de términos.
En este caso, la entrada de mean() es un vector fila de longitud dos con los números 7 y 48. Por tanto, la media es la suma de ambos dividida entre el número de elementos, es decir, 2.
mean(c(7,48))
27.5
Ha sido fácil, ¿verdad?
¡Pasemos al siguiente bloque, el más interesante del tutorial!
Funciones para estructuras de datos
Ahora veamos algunas estructuras de datos como listas y vectores. Verás distintas formas de operar con una list, invertir una list y cómo convertir una lista en vector y viceversa.
La siguiente función es una list(), o mejor dicho, una lista de listas (lista anidada). Crea una lista de elementos que puede incluir valores lógicos, numéricos y cadenas.
En el ejemplo siguiente hay tres listas dentro de una lista: log, ch e int_vec. Cada una tiene un tipo de dato u objeto de R: lógico, numérico y carácter/cadena.
list_define <- list(log = TRUE, ch = "hello_datacamp", int_vec = sort(rep(seq(8,2, by = -2), times = 2)))
list_define
- $log
- TRUE
- $ch
- 'hello_datacamp'
- $int_vec
-
- 2
- 2
- 4
- 4
- 6
- 6
- 8
- 8
loges un valor lógico, TRUE o FALSEches la cadena de caractereshello_datacampint_veces una secuencia de valores numéricos.
Como log y ch son directos, fijémonos en int_vec.
int_vec = sort(rep(seq(8,2, by = -2), times = 2))
int_vec
- 2
- 2
- 4
- 4
- 6
- 6
- 8
- 8
Entendamos la expresión paso a paso.
La función seq produce una secuencia de números en orden descendente del 8 al 2.
La sintaxis de seq() es: seq(x1,x2, by = y)
Los dos primeros argumentos, x1 y x2, indican a R el rango de la secuencia (inicio y fin). El argumento by especifica el incremento o decremento en cada paso.
Por ejemplo, la línea de abajo genera una secuencia de 100 a 200 con un paso de 20.
seq(100,200, by = 20)
- 100
- 120
- 140
- 160
- 180
- 200
En nuestro ejemplo, seq devuelve una secuencia del 8 al 2 (incluidos) con un decremento de 2, lo que da un vector de longitud 4.
a = seq(8,2, by = -2)
a
- 8
- 6
- 4
- 2
Ahora, la función rep.
Puede repetir la entrada (normalmente un vector o lista) usando el argumento times, que recibe un entero e indica cuántas veces repetir la secuencia. rep acepta dos argumentos: la entrada y el número de repeticiones.
Aplicando rep a nuestro vector de longitud 4, obtenemos una salida de longitud 8.
b = rep(a, times = 2)
Si quieres repetir cada elemento del vector o lista, en lugar de repetir el vector completo, usa el argumento each como alternativa a times.
La diferencia entre times y each es el patrón con el que aparece cada elemento.
rep(a, each = 2)
- 8
- 8
- 6
- 6
- 4
- 4
- 2
- 2
Por último, sort(). Es una función genérica y autoexplicativa para ordenar estructuras como vectores o listas. No se limita a numéricos: también funciona con lógicos y caracteres. Por defecto, ordena en ascendente.
Pasemos la salida de rep a sort para obtener el resultado final.
sort(b)
- 2
- 2
- 4
- 4
- 6
- 6
- 8
- 8
¡Genial! Has desgranado la expresión larga int_vec, que estaba dentro de la lista list_define, de forma muy sencilla.
Ahora, veamos el contenido de list_define usando str(). Esta función muestra la estructura de los objetos de R.
str(list_define)
List of 3
$ log : logi TRUE
$ ch : chr "hello_datacamp"
$ int_vec: num [1:8] 2 2 4 4 6 6 8 8
Algunas expresiones útiles de R:
- Las funciones
issirven para comprobar el tipo de tu estructura de datos; devuelven un lógico y vienen muy bien con condiciones.
is.list(list_define) #devuelve TRUE si el argumento es una lista.
TRUE
Mientras que devuelve FALSE si pasas un vector que no es lista, como se ve abajo.
is.list(c(1,2,3)) #devuelve FALSE porque pasas un vector.
FALSE
- Convertir un vector en lista es muy fácil en R. Usa
asseguido de.list()y pasa el vector como argumento.
vec_to_list <- as.list(c(1,2,3))
is.list(vec_to_list) #verifícalo con is.list()
TRUE
- Por otro lado, puedes "aplanar" una lista a vector con
unlist. R lo hace aplanando toda la estructura y devolviendo un único vector.
list_to_vec <- unlist(vec_to_list)
list_to_vec
- 1
- 2
- 3
Igual que is.list(), puedes usar is.vector() para saber si el argumento es un vector.
is.vector(list_to_vec)
TRUE
Convirtamos la lista grande list_define a vector. Ojo: un vector solo puede contener un único tipo de dato u objeto de R. Por tanto, los valores logical y numeric se convertirán en cadenas.
unlist(list_define)
- log
- 'TRUE'
- ch
- 'hello_datacamp'
- int_vec1
- '2'
- int_vec2
- '2'
- int_vec3
- '4'
- int_vec4
- '4'
- int_vec5
- '6'
- int_vec6
- '6'
- int_vec7
- '8'
- int_vec8
- '8'
Antes de pasar al siguiente tema, veamos append() y rev().
- Como su nombre indica,
append()te permite añadir dos o más vectores o listas a un vector o lista existente o nueva.
Probemos append() con list_define. Verás que ahora la lista tiene 6 elementos en lugar de 3, porque has concatenado la lista consigo misma.
str(append(list_define, list_define))
List of 6
$ log : logi TRUE
$ ch : chr "hello_datacamp"
$ int_vec: num [1:8] 2 2 4 4 6 6 8 8
$ log : logi TRUE
$ ch : chr "hello_datacamp"
$ int_vec: num [1:8] 2 2 4 4 6 6 8 8
- Por último, invirtamos el orden de
list_defineconrev()en R.
str(rev(list_define))
List of 3
$ int_vec: num [1:8] 2 2 4 4 6 6 8 8
$ ch : chr "hello_datacamp"
$ log : logi TRUE
Expresiones regulares
Mucha gente ve las expresiones regulares como un tema complejo. Sin embargo, son esenciales no solo en R, sino en muchos lenguajes como Python. Muchos lenguajes, incluido R, traen soporte de regex integrado.
Las expresiones regulares tienen infinidad de aplicaciones y son muy útiles para preprocesar texto. Se usan en problemas de PLN (NLP).
También se emplean en motores de búsqueda y editores de texto.
Las expresiones regulares, también llamadas regex, regexp u operadores racionales, son una secuencia de caracteres que define un patrón de búsqueda. Estos patrones se usan para encontrar coincidencias, buscar y reemplazar o filtrar cadenas que encajan con el patrón.
Empecemos entendiendo grep() y grepl().
Para simplificar, definamos un vector fila animals_regex de longitud 5 sobre el que aplicaremos patrones regex.
animals_regex <- c('cat','dog','cheetah','lion','mice')
Primero, grepl(). Esta función devuelve un lógico: TRUE si la cadena casa con el patrón, FALSE en caso contrario.
La sintaxis de grepl() es sencilla: el primer argumento es el patrón a buscar y el segundo es el vector/cadena donde buscar. Puedes ignorar el resto por ahora. grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE) Averigüemos qué animales de los cinco anteriores tienen una c usando grepl().
En este caso buscas animales que contengan c, así que el patrón es simplemente c.
grepl(pattern = 'c', x = animals_regex)
- TRUE
- FALSE
- TRUE
- FALSE
- TRUE
Como ves, hay c en cat, cheetah y mice, por eso esas posiciones del vector animals_regex devuelven TRUE, y FALSE en las que no coinciden con c.
Busquemos ahora los elementos que empiezan por c y no solo los que la contienen.
Para ello, usa el acento circunflejo ^ al inicio del patrón que quieres encontrar.
grepl(pattern = '^c', x = animals_regex) #solo cat y cheetah empiezan por `c`.
- TRUE
- FALSE
- TRUE
- FALSE
- FALSE
Como solo cat y cheetah empiezan por c, solo esas posiciones son TRUE.
De forma similar a ^, el signo $ al final del patrón sirve para hacer coincidir elementos que terminan con dicho patrón. Para encontrar animales que terminan en n, usa n seguido de $.
grepl(pattern = 'n$', x = animals_regex) #solo lion termina en `n`.
- FALSE
- FALSE
- FALSE
- TRUE
- FALSE
Nota: Para saber más sobre expresiones regulares, escribe ?regex en una celda de Jupyter y se abrirá la documentación. También puedes consultar esta fuente, que ofrece una herramienta para diseñar y probar patrones sobre tus cadenas.
- Similar a
grepl(),grep()devuelve los índices del vector/matriz que coinciden con el patrón, en lugar de lógicos.
La sintaxis de grep() es la misma que la de grepl(): grep(pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE, fixed = FALSE, useBytes = FALSE, invert = FALSE). Tomemos el mismo ejemplo de arriba, pero aplicando grep().
grep(pattern = 'c', x = animals_regex)
- 1
- 3
- 5
Como era de esperar, devuelve los índices de cat, cheetah y mice, no TRUE/FALSE. ¡Y básicamente eso es todo!
Usemos which para comparar grep() y grepl(). which() devuelve los índices de un objeto lógico en los que hay TRUE.
Uniendo piezas: como grepl() devuelve un objeto lógico, puedes pasarlo a which(), que transformará la salida a algo equivalente a grep().
which(grepl(pattern = 'c', x = animals_regex))
- 1
- 3
- 5
Al igual que grepl(), grep() maneja distintos patrones de expresiones regulares.
Si aplicas grep() para encontrar elementos en el vector animals_regex que terminan en n, esperas la salida 4, ya que solo lion acaba en n, como se muestra:
grep(pattern = 'n$', x = animals_regex)
4
¡Bien hecho!
Has aprendido lo básico de regex: cómo filtrar elementos de un vector que casan con un patrón. Pero R no se limita a buscar patrones. Tiene varias funciones útiles, entre ellas sub().
La función sub(), en lugar de filtrar, reemplaza las coincidencias por otras cadenas. ¡Veámosla!
sub()recibe principalmente tres argumentos:- pattern, el patrón o regex a buscar,
- replacement, el valor que sustituirá la coincidencia, y
- x, el vector de entrada sobre el que aplicas la regex.
La sintaxis es: sub(pattern, replacement, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE). Usemos de nuevo el ejemplo de arriba para entender cómo funciona.
sub(pattern = 'c', replacement = 'a', x = animals_regex)
- 'aat'
- 'dog'
- 'aheetah'
- 'lion'
- 'miae'
Como ves, cat pasa a ser aat, cheetah se convierte en aheetah y mice en miae. En esas posiciones el patrón ha coincidido.
Ten en cuenta que sub() solo busca la primera coincidencia en cada cadena: si hay dos c, solo la primera se reemplaza por a; la segunda queda igual.
Si quieres reemplazar todas las coincidencias, usa gsub() (fuera del alcance de este tutorial).
Antes de pasar al siguiente tema, probemos una expresión más.
Ahora usaremos el operador | (o) para intentar casar cualquiera de los patrones definidos y, si coinciden, reemplazarlos por -. Recuerda: con gsub() se reemplaza cada coincidencia del patrón.
gsub(pattern = 'c|d|l', replacement = '-', x = animals_regex) #los animales con `c`,`d`,`l` se reemplazan por `-`.
- '-at'
- '-og'
- '-heetah'
- '-ion'
- 'mi-e'
¡Pasemos al último tema de hoy: tiempo y fechas!
Tiempo y fechas
La información de fecha y hora puede ser muy útil en muchos casos. Por ejemplo, si trabajas en Visión por Computador y quieres calcular los FPS (frames por segundo) a los que corre tu algoritmo, puedes usar el objeto de tiempo para medir la velocidad de proceso. Para series temporales y análisis de estacionalidad, R despliega todo su potencial.
Para empezar, imprimamos la fecha de hoy en R con Sys.Date(). Sys hace referencia al sistema, es decir, devuelve la fecha del sistema.
Sys.Date()
Fácil, ¿verdad?
Las fechas y horas en R pertenecen al objeto Date, es decir, el tipo de dato es Date. Puedes verificarlo con la función class que viste en el tutorial Data Types in R.
Similar a la fecha, tienes time() del sistema con Sys.time(), que devuelve hora y fecha actuales.
Sys.time()
[1] "2020-02-04 02:05:04 IST"
Crear objetos Date
Ya sabes obtener la fecha y hora actuales. Veamos cómo crear fechas de otros días pasando una simple cadena como argumento.
Para crear un objeto fecha para el 10 de mayo de 1993, usa:
date_may <- as.Date('1993-05-10') #convierte una cadena de caracteres a objeto Date
date_may
class(date_may)
'Date'
Un punto importante: la función as.Date en R espera por defecto el formato YYYY-MM-DD. Si intercambias año, mes o día, obtendrás un error. ¡Probemos!
date_may <- as.Date('05-1993-10') #R sigue por defecto el formato de fecha ISO
Error in charToDate(x): character string is not in a standard unambiguous format
Traceback:
1. as.Date("05-1993-10")
2. as.Date.character("05-1993-10")
3. charToDate(x)
4. stop("character string is not in a standard unambiguous format")
La buena noticia es que puedes cambiar el formato explícitamente con el argumento format y adaptarlo.
date_may <- as.Date('05-1993-10', format = '%m-%Y-%d')
as.Date() acepta distintos formatos de entrada, pero al final convierte a formato ISO. Puedes comprobarlo imprimiendo date_may.
date_may
date_may <- as.Date('05-10-1993', format = '%m-%d-%Y')
date_may
Aritmética con fechas
¿No sería genial poder hacer operaciones como suma y resta con objetos Date en R?
Sumemos 1 a date_may y verás que muestra la fecha del día siguiente.
date_may + 1
Perfecto: al sumar uno, la fecha pasa del 10 al 11 de mayo de 1993. Del mismo modo, puedes restar.
Imagina que quieres calcular la diferencia entre tu fecha de nacimiento y la de tu hermana/o mayor.
elder_sib <- as.Date('1989-03-21')
date_may - elder_sib
Time difference of 1511 days
Conclusión
Enhorabuena por completar el tutorial.
Este tutorial es un buen punto de partida para quienes quieren aprender varias funciones de utilidad en R. Como buen ejercicio, te recomendamos profundizar en expresiones regulares: se usan en multitud de aplicaciones y son una herramienta potentísima para limpiar o preprocesar texto.
Déjanos cualquier duda sobre el tutorial en los comentarios.
Si quieres seguir aprendiendo R, haz el curso Intermediate R de DataCamp y echa un vistazo a nuestro tutorial Using Functions in R.