Capítulo 5 Tipos de datos

Scripts usados:

 

Tras los ejercicios planteados en la lección anterior y lo que hayas jugueteado por tu cuenta, deberíamos saber más o menos usar R como una calculadora, definiendo variables que nos guardan números, como a <- 1. Vamos a ir más allá: ¿existen variables más allá de los números? En este capítulo vamos a empezar a familiarizarnos con los tipos de datos que podemos necesitar. Piensa por ejemplo en los datos guardados de una persona:

  • La edad será un número (sin decimales)
  • Su peso o estatura será otro número (ahora con decimales).
  • Su nombre será una cadena de texto.
  • Su fecha de nacimiento será precisamente eso, una fecha.
  • A la pregunta «¿está usted soltero/a?» la respuesta será lo que llamamos una variable binaria o lógica: una variable que solo puede valer TRUE (si está soltero/a) y FALSE (en otro caso).

Existen más tipos (por ejemplo, números complejos) pero con estos nos valdrá de momento.

5.1 Variables numéricas

  • [X] Variables numéricas (individuales)
  • [ ] Variables de caracteres
  • [ ] Variables lógicas
  • [ ] Variables de tipo fecha

Probablemente el dato más sencillo y obvio de entender sean los datos numéricos, datos que ya hemos usado en nuestros primeros pasos como calculadora.

a <- 1
b <- 2
a + b
## [1] 3

En el código anterior, tanto a como b, como la suma a + b, son de tipo numérico, algo que podemos comprobar con la función class() (nos devuelve numeric).

# Clase de las variables
class(a)
## [1] "numeric"
## [1] "numeric"
class(a + b)
## [1] "numeric"

También podemos comprobar su naturaleza con typeof(), que nos devuelve la naturaleza del dato tal cual es guardada en R.

# Topología interna
typeof(a)
## [1] "double"
## [1] "double"
typeof(a + b)
## [1] "double"

Aunque no es especialmente relevante profundizar en ello de momento, fíjate que ambas órdenes combinadas nos dicen que las variables son de tipo numérico pero concretamente de tipo double: dichas variables son entendidas internamente como números con decimales (aunque no los veamos), lo que en matemáticas se llaman números reales (por curiosidad: el término double viene de lenguajes de programación antiguos como C, que significa «doble precisión», para indicarle que reserve 8 bytes - 64 bits - de memoria). Durante el curso no distinguiremos entre distintos tipos de números, para nosotros serán siempre de clase numeric y de tipo double, pero podríamos definir números enteros o integer (sin decimales, ahorrando huecos en memoria).

# Dato numérico (entero)
a <- 1L
class(a)
## [1] "integer"
## [1] "integer"

Con los datos numéricos podemos realizar todas las operaciones que se nos ocurriría hacer en una calculadora, como sumar (+), restar (-), multiplicar (*), dividir (/), raíz cuadrada (sqrt()), valor absoluto (abs()), elevar al cuadrado (^2), elevar al cubo (^3), etc.

# Definimos dos variables numéricas
a <- 1
b <- -2

# Suma y resta
a + b
## [1] -1
a - b
## [1] 3
# Multiplicación y división
a * b
## [1] -2
a / b
## [1] -0.5
# Raíz cuadrada
sqrt(a)
## [1] 1
# Potencias
a^2
## [1] 1
b^3
## [1] -8

5.2 Variables de tipo caracter (texto)

  • [X] Variables numéricas (individuales)
  • [X] Variables de caracteres
  • [ ] Variables lógicas
  • [ ] Variables de tipo fecha

Pero no solo de números viven los datos: imagina que además de la edad de una persona queremos guardar su nombre.

edad <- 32
nombre <- "Javier"

class(edad)
## [1] "numeric"
typeof(edad)
## [1] "double"
class(nombre)
## [1] "character"
typeof(nombre)
## [1] "character"

Fíjate que ahora tenemos en nombre una variable de tipo character, es decir, una cadena de texto (conocido en otros lenguajes como string o char): letras y caracteres entre comillas.

5.2.1 Nuestra primera función: paste

Las cadenas de texto son un tipo especial de dato, con los que obviamente no podremos hacer operaciones aritméticas, pero si podemos hacer operaciones propias de cadenas de texto como puede ser la función paste(). Dicha función nos permite pegar dos cadenas de caracteres, decidiendo que caracter queremos que vaya entre palabra con el argumento sep =.

nombre <- "Javier"
apellido <- "Álvarez"
paste(nombre, apellido, sep = "") # todo junto
## [1] "JavierÁlvarez"
paste(nombre, apellido, sep = " ") # separados por un espacio
## [1] "Javier Álvarez"
paste(nombre, apellido, sep = ".") # separados por un punto .
## [1] "Javier.Álvarez"

Si queremos pegar cadenas de texto sin ningún tipo de caracter, existe una forma más abreviada y limpia de ejecutar la orden, usando la función paste0()

# Son equivalentes
paste(nombre, apellido, sep = "") # todo junto
## [1] "JavierÁlvarez"
paste0(nombre, apellido) # todo junto sin nada separando
## [1] "JavierÁlvarez"

Cuando hemos ejecutado paste() estamos ejecutando lo que se conoce como una función: una palabra reservada que representa un conjunto de órdenes, y que se ejecuta a partir de unos argumentos de entrada. En el caso de la función paste(), los argumentos serán las cadenas de texto que queremos copiar, y un argumento opcional llamado sep, que podemos darle un valor en concreto o dejarlo sin especificar. Lo que hará R será tomar su valor por defecto igual a sep = " " (por defecto, la función paste() pega cadenas de texto con un espacio entre ellas). Prueba a ejecutar ? paste en consola para ver las opciones en el panel de ayuda.

# Son equivalentes
paste(nombre, apellido, sep = " ")
## [1] "Javier Álvarez"
paste(nombre, apellido)
## [1] "Javier Álvarez"

5.2.2 Nuestro primer paquete: glue

Otra forma más intuitiva de trabajar con textos y variables numéricas es usar el paquete glue, que nos permite pegar cadenas de texto a variables numéricas de forma simbólica.

install.packages("glue") # solo la primera vez

Recuerda que install.packages() es solo necesario la primera que «compramos el libro»: nos bajamos una serie de archivos a nuestro ordenador. Una vez que hemos comprado el libro, cada vez que queramos usarlo bastará con indicarle que nos traiga ese libro concreto con library().

El paquete glue nos permite pegar de una forma mucho más legible cadenas de texto

# Ejemplo 1
edad <- 32
glue("La edad es de {edad} años")
## La edad es de 32 años
paste("La edad es de", edad, "años") # equivalente
## [1] "La edad es de 32 años"
# Ejemplo 2
edad <- 32
unidades <- "años"
glue("La edad es de {edad} {unidades}")
## La edad es de 32 años

También podemos hacer uso de dicha función sin tener los valores numéricos previamente guardados en variables.

# Otra forma sin definir variables a priori
glue("La edad es de {32} años")
## La edad es de 32 años

 

5.3 Variables lógicas (TRUE/FALSE)

  • [X] Variables numéricas (individuales)
  • [X] Variables de caracteres
  • [X] Variables lógicas
  • [ ] Variables de tipo fecha

Probablemente el tipo de datos más importante en todo lenguaje de programación son las variables lógicas. Un valor lógico puede tomar dos valores (en realidad pueden tomar un tercer valor, NA, las siglas de not available para representar datos ausentes, pero lo veremos más adelante):

  • TRUE (guardado internamente como un 1).
  • FALSE (guardado internamente como un 0).

Este tipo de variables, también conocidas como variables binarias (solo dos valores) o booleanas, son la base de la programación ya que cada bit de nuestro ordenador puede guardar un 1 o un 0.

soltero <- TRUE # ¿Es soltero? --> SÍ
carnet_conducir <- FALSE # ¿Tiene carnet de conducir? --> NO

class(soltero)
## [1] "logical"
typeof(soltero)
## [1] "logical"
class(carnet_conducir)
## [1] "logical"
typeof(carnet_conducir)
## [1] "logical"

Ahora nuestas variables son guardadas como logical, y pueden tomar los valores TRUE/FALSE, aunque internamente son guardados como 1/0. Es importante entender que no son variables de texto:

  • "TRUE" es un texto (internamente idéntico a rojo o azul)
  • TRUE es una variable lógica
# Texto
texto <- "TRUE"
texto + 1
## Error in texto + 1: argumento no-numérico para operador binario
# Lógica
logica <- TRUE
logica + 1
## [1] 2

Estos valores suelen ser resultado de evaluar condiciones lógicas. Por ejemplo, imaginemos que queremos comprobar si una persona está soltero o no, y si tiene carnet de conducir o no. Basta con que ejecutemos la orden soltero == TRUE, que nos devolverá TRUE si está soltero, y FALSE en caso contrario. De igual manera podremos hacer carnet_conducir == TRUE. IMPORTANTE: cuando queremos comparar si un elemento es igual a otro, usaremos el operador de comparación ==, pudiendo usar también su opuesto != («distinto de»).

soltero == TRUE
## [1] TRUE
soltero != TRUE # igual que soltero == FALSE
## [1] FALSE
carnet_conducir == TRUE
## [1] FALSE
carnet_conducir != TRUE
## [1] TRUE

Por el mismo razonamiento podemos comparar si una variable numérica o de tipo caracter es igual o distinto a un valor dado, incluso para las numéricas podemos comparar si son menores o mayores que un número.

¿Tiene la persona menos de 32 años? ¿Tiene justo 32 años? ¿Tiene 32 años o más?

edad < 32
## [1] FALSE
edad == 32
## [1] TRUE
edad >= 32
## [1] TRUE

¿La persona se llama Carlos?

nombre == "Carlos"
## [1] FALSE

Fíjate que para comparaciones tenemos «igual a» == frente distinto !=, pero también comparaciones de orden como <=, > o >=. Las condiciones lógicas pueden ser combinadas, principalmente de dos maneras:

  • Intersección: todas las condiciones concatenadas se deben cumplir (conjunción y, operador &) para devolver un TRUE.

  • Unión: basta con que una de las condiciones concatenadas se cumpla (conjunción o, operador |) para devolver un TRUE.

Por ejemplo, podríamos preguntarnos si la persona tiene más de 32 años y está soltero (AMBAS deben cumplirse).

edad > 32 & soltero == TRUE
## [1] FALSE
# Equivalente (al ser soltero un valor ya de por si lógico)
edad > 32 & soltero
## [1] FALSE

Vemos que el resultado es FALSE ya que solo se cumple una de las condiciones: devolvería TRUE si preguntamos si tiene más de 30 años y está soltero, o si solo pedimos una de las dos condiciones (¿está soltero y/o tiene más de 32 años?).

edad > 32 | soltero # nos sirve con que alguna se cumpla
## [1] TRUE
edad > 32 & soltero # deben cumplirse ambas
## [1] FALSE
edad > 30 & soltero # deben cumplirse ambas
## [1] TRUE

5.4 Variables de tipo fecha

  • [X] Variables numéricas (individuales)
  • [X] Variables de caracteres
  • [X] Variables lógicas
  • [X] Variables de tipo fecha

Por último, vamos a ver un tipo de datos muy especial: los datos de tipo fecha. Una fecha podría ser a priori una simple cadena de texto "2021-04-21" pero podemos usar la función as.Date() para que R entienda que esa cadena de texto representa un instante temporal. Fíjate la diferencia entre una fecha en texto y una fecha con as.Date().

# Cadena de texto
fecha_char <- "2021-04-21"
fecha_char + 1
## Error in fecha_char + 1: argumento no-numérico para operador binario
# Fecha, mostrada como un texto pero guardada internamente como un número
fecha_date <- as.Date(fecha_char, format = "%Y-%m-%d")
fecha_date + 1
## [1] "2021-04-22"

En el momento en que convertimos la cadena de texto a fecha, aunque se visualice como un texto, internamente es un número, por lo que podemos restar fechas (días entre ambas), podemos sumar números a fechas (fecha días después), etc. Como ya hemos dicho, las fechas y momentos temporales no serán meras cadenas de caracter sino que tienen clases especiales asociadas. Las fechas serán guardadas internamente como el número de días transcurridos desde el 1 de enero de 1970, y las horas como número de segundos desde el 1 de enero de 1970 (para la clase POSIXct) o una lista de segundos, minutos y horas (para la clase POSIXlt).

¿Cómo obtener automáticamente la fecha de hoy? La función Sys.Date() nos devuelve directamente la fecha y hora en el momento de la ejecución de la orden.

fecha <- Sys.Date()
fecha
## [1] "2022-01-30"
fecha - 7 # una semana antes
## [1] "2022-01-23"
class(fecha) # de clase fecha
## [1] "Date"

Para convertir una cadena de texto a fecha, basta usar la función as.Date(), pasándole como argumento la fecha en formato "yyyy-mm-dd" por defecto.

as.Date("2021-03-10") # formato por defecto
## [1] "2021-03-10"

Si introducimos otro tipo de formato, debemos especificárselo en un segundo argumento, para R sepa el formato de fecha que le estamos pasando

as.Date("10-03-2020", "%d-%m-%Y") # con día-mes-año (4 cifras)
## [1] "2020-03-10"
as.Date("10-03-20", "%d-%m-%y")  # con día-mes-año (2 cifras)
## [1] "2020-03-10"
as.Date("03-10-2020", "%m-%d-%Y") # con mes-día-año (4 cifras)
## [1] "2020-03-10"
as.Date("Octubre 21, 1995 21:24", "%B %d, %Y %H:%M") # fecha escrita
## [1] NA

 

ERROR: sin pasar a fecha no se puede operar

Si tenemos una fecha como caracter, nunca podremos hacer operaciones (por ejemplo, restarle una unidad temporal, en este caso un día).

"2021-03-10" - 1 # error
## Error in "2021-03-10" - 1: argumento no-numérico para operador binario

Fíjate la diferencia cuando lo convertimos en fecha

fecha <- as.Date("2021-03-10") - 1 # día previo
fecha
## [1] "2021-03-09"

 

Dentro del entorno del paquete lubridate tenemos bastantes funciones útiles para trabajar con fechas como las siguientes

install.packages("lubridate") # solo la primera vez
library(lubridate)
ymd_hms("2017-11-28T14:02:00") # convertir a fecha una cadena año-mes-día + hora
## [1] "2017-11-28 14:02:00 UTC"
ydm_hms("2017-22-12 10:00:00") # convertir a fecha una cadena año-día-mes + hora
## [1] "2017-12-22 10:00:00 UTC"
dmy_hms("1 Jan 2017 23:59:59") # convertir a fecha una cadena textual de fecha + hora
## [1] "2017-01-01 23:59:59 UTC"
mdy("July 4th, 2000") # convertir a fecha una cadena textual de fecha
## [1] "2000-07-04"
ymd(20170131)
## [1] "2017-01-31"

Además el paquete nos proporciona herramientas para extraer la fecha y hora actuales con las funciones today() y now()

## [1] "2022-01-30"
now()
## [1] "2022-01-30 21:09:44 CET"

También tenemos disponibles en dicho paquete funciones para extraer facilmente algunas variables temporales como el día de la semana, el mes o el cuatrimestre, con las funciones year(), months(), day() o wday() (día de la semana).

fecha <- now()
year(fecha)
## [1] 2022
month(fecha)
## [1] 1
day(fecha)
## [1] 30
wday(fecha, week_start = 1) # Día de la semana (empezando por el lunes)
## [1] 7
hour(fecha)
## [1] 21
minute(fecha)
## [1] 9
second(fecha)
## [1] 44.87452
week(fecha) # Número de semana (del año)
## [1] 5

Al igual que podemos realizar operaciones aritméticas sencillas con las fechas, también podemos realizar comparaciones, por ejemplo, si el día actual es menor o mayor que otra fecha dada.

fecha_actual <- now()
fecha_actual > ymd(20170131) # Actual vs 2017-01-31
## [1] TRUE
fecha_actual > ymd(21000131) # Actual vs 2100-01-31
## [1] FALSE

 

5.5 Consejos

CONSEJOS

 

Recuperar un comando y autocompletar

Si haces click con el ratón en la consola y pulsas la flecha «arriba» del teclado, te irá apareciendo todo el historial de órdenes ejecutadas. Es una manera de ahorrar tiempo para ejecutar órdenes similares a las ya ejecutadas. Si empiezas a escribir el nombre de una variable pero no te acuerdas exactamente de su nombre, pulsando tabulador te autocompletará solo.

 

Convertir tipos de datos

A veces la lectura de variables numéricas de nuestros archivos puede hacer que un número, por ejemplo 1, sea leído como la cadena de texto "1", con la que no podemos operar como un número. Las funciones as.numeric(), as.character() y as.logical() nos permiten convertir una variable en tipo numérico, caracter o lógico, respectivamente.

"1" + 1
## Error in "1" + 1: argumento no-numérico para operador binario
as.numeric("1") + 1
## [1] 2
## [1] "1"
as.logical(c(0, 1))
## [1] FALSE  TRUE

 

5.6 📝 Ejercicios

(haz click en las flechas para ver soluciones)

📝Ejercicio 1: define una variable que guarde tu edad, otra con tu nombre, otra respondiendo a la pregunta «¿tengo hermanos?» y otra con la fecha de tu nacimiento.

  • Solución:
edad <- 32 # tipo numeric
nombre <- "Javier" # tipo caracter
hermanos <- TRUE # tipo hermanos
fecha_nacimiento <- as.Date("1989-09-10") # tipo fecha

 

📝Ejercicio 2: define otra variable con tus apellidos y junta las variables nombre y apellidos en una sola variable nombre_completo.

  • Solución:
# Apellidos
apellidos <- "Álvarez Liébana"

# Pegamos
nombre_completo <- glue("{nombre} {apellidos}")
nombre_completo
## Javier Álvarez Liébana
# Otra forma
nombre_completo <- paste(nombre, apellidos)
nombre_completo
## [1] "Javier Álvarez Liébana"

 

📝Ejercicio 3: construye una frase que diga «Hola, me llamo … y tengo … años. Nací el … de … de …» (con el nombre completo).

  • Solución:
dia_nacimiento <- day(fecha_nacimiento)
mes_nacimiento <- month(fecha_nacimiento)
a_nacimiento <- year(fecha_nacimiento)

glue("Hola, me llamo {nombre_completo} y tengo {edad} años. Nací el {dia_nacimiento} del {mes_nacimiento} de {a_nacimiento}")
## Hola, me llamo Javier Álvarez Liébana y tengo 32 años. Nací el 10 del 9 de 1989

 

📝Ejercicio 4: calcula los días que han pasado desde la fecha de tu nacimiento

  • Solución:
days(today() - fecha_nacimiento)
## [1] "11830d 0H 0M 0S"

 

📝Ejercicio 5: obtén una variable lógica que nos diga si se cumplen (todas) las condiciones i) menor de 30 años (edad < 30); ii) con hermanos (hermanos == TRUE); iii) nacido en 1990 o posterior (fecha_nacimiento >= as.Date("1990-01-01")).

  • Solución:
# Se tienen que cumplir todas
edad < 30 & fecha_nacimiento >= as.Date("1990-01-01") & hermanos
## [1] FALSE
# otra forma
edad < 30 & fecha_nacimiento >= as.Date("1990-01-01") & hermanos == TRUE
## [1] FALSE

 

📝Ejercicio 6: modifica el código del ejercicio anterior para obtener una variable lógica que nos diga si se cumplen (al menos) alguna de las condiciones i) menor de 30 años; ii) con hermanos; iii) nacido en 1990 o posterior. Al contrario que antes, no necesitamos que se cumplan todas, nos basta con que se cumple al menos una.

  • Solución:
# Se tienen que cumplir todas
edad < 30 | fecha_nacimiento >= as.Date("1990-01-01") | hermanos
## [1] TRUE

 

📝Ejercicio 7: calcula la fecha 11 días más tarde a tu fecha de nacimiento. Obtén la semana del año de dicha fecha y el día de la semana con las funciones week, wday y weekdays.

  • Solución:
# Podemos sumar porque es fecha
fecha_post <- fecha_nacimiento + 11
fecha_post
## [1] "1989-09-21"
# Semana del año
week(fecha_post)
## [1] 38
# Día de la semana (versión americana, empiezan el domingo)
wday(fecha_post)
## [1] 5
# Día de la semana (versión española)
wday(fecha_post, week_start = 1)
## [1] 4
# Día de la semana en texto
weekdays(fecha_post)
## [1] "Thursday"

 

📝Ejercicio 8: define dos números cualesquiera en variable a y b. Calcula su suma y determina cual es mayor.

  • Solución:
a <- -5
b <- 7

# Suma
c <- a + b
c
## [1] 2
# Comparaciones
a == b # ¿a = b?
## [1] FALSE
a < b # ¿a < b?
## [1] TRUE