1.4 Tipos de datos y su manejo.

Dentro de R, todas las variables están representadas como objetos y estos pueden manipularse de acuerdo las necesidades. La mayoría de los objetos, son o están compuestos por vectores (de hecho, R esta completamente vectorizados y esta es la estructura de datos más importante de la que hablaremos), y estos vectores pueden estar formados por uno de varios tipos básicos de datos: datos numéricos, caracteres, valores lógicos, o fechas. Para saber el tipo de dato que compone a un vector se puede utilizar la función typeof().

1.4.1 Datos numéricos.

Los datos numéricos pueden ser de uno de tres tipos: o double, que es el tipo de dato numérico por omisión, y se usa para representar cualquier número real en R. Otra clase de objetos numéricos corresponde a integer, que se usa para representar números enteros solamente. Para crear enteros simplemente coloca una L seguido del número:

# Crea un entero
my_int <- 4L

# Verificando
typeof(my_int)

R usa de forma intercambiable double e integer durante las operaciones matemáticas y en general no te daras cuenta cuando aparezcan enteros. Pero en ciertos casos es útil utilizar enteros ya que ocupan menos memoria (necesitas menos bytes para almacenar un integer en memoria que para almacenar un double).

Finalmente, el último tipo de dato numérico es el complex que se usa para representar números complejos: aquellos que presentan dos partes, una real y una imaginaria que viene representada por un múltiplo de \(i\) (\(i\) se define como \(i = \sqrt{-1}\)). Puede que nunca te encuentres con estos en tu vida, pero los números complejos tienen muchas aplicaciones en física, matemáticas e ingeniería, y aparecen en varios tópicos interesantes en biología. La manera de escribirlos en R es la siguiente:

# Un número complejo
my_complex <- 1 + 2i

# Verificas
typeof(my_complex)

Un punto interesante, si tomas la raíz cuadrada de un número negativo vas a obtener como resultado NaN (este es un valor especial que significa Not a Number y que surge cuando intentas realizar operaciones matemáticas que dan como resultado valores indefinidos) seguido de una advertencia. La razón es que un número negativo en R se representa como un tipo double. Si quieres tener la raíz cuadrada de un numero negativo, este debe representarse primero como un número complejo:

# -1 (la parte imaginaria esta multiplicada por cero, asi que el número es 
# real. Pero de esta forma, R entiende que se trata de un tipo de dato 
# `complex`)
my_complex <- -1 + 0i
sqrt(my_complex)

1.4.2 Caracteres.

Los caracteres o cadenas de caracteres son un tipo de dato que permite representar letras, palabras y frases en R. Para crear datos de tipo character solo tienes que escribir un cadena de caracteres entre comillas (“) o comillas sencillas (’)6. Puedes usar la función nchar para conocer la longitud de una cadena de caracteres.

hello <- "Hello World!"
hello

nchar(hello) # 12: el espacio en blanco cuenta como un caracter.

Date cuenta, que los espacios en blanco son un tipo de caracter también.

Claro, existen funciones especificas que facilitan el manejo de cadenas de caracteres en R. Por ejemplo, la función paste permite unir dos cadenas de caracteres:

paste("Hello!", "R is fun", "right?")
paste("Too", "many", "underscores!", sep="_")

La última línea de código utiliza el argumento sep de la función paste para especificar el separador que se coloca entre las cadenas de caracteres que se unirán. Puedes colocar cualquier separador que te parezca, incluso espacios vacíos (““). También puedes llevar a cabo el proceso inverso: tomar una cadena de caracteres y separarla en pedazos cada vez que se encuentre una coincidencia con respecto a un patrón7 particular. Por ejemplo:

strsplit("Break it Ralph! Break it!", " ")

rompe la cadena "Break it Ralph! Break it!" en subcadenas cada vez que encuentra una ocurrencia del patrón ” ” (un espacio en blanco), a saber, "Break", "it", "Ralph!", "Break" e "it!".

Además, puedes obtener subcadenas a partir de una cadena utilizando la función substr. Por ejemplo, substr("This is my string", 9, 17) extrae de la cadena "This is my string" una subcadena que empieza en el caracter 9 y termina en el 17 (ejecuta el comando para que observes el resultado).

Otras funciones útiles lo son: i) Convertir un número en una cadena de caracteres: p. ej. as.character(10). ii) Todo en minúscula (tolower()) y todo en mayúscula (toupper()). iii) Reemplazo de caracteres: puedes reemplazar todas las ocurrencias de un caracter en una cadena por otro. Por ejemplo:

r <- "La r en R es mayuscula!"
chartr(r, old = "r", new = "R")

reemplaza todas las ocurrencias de r por una R. También funciona si usas mas de un caracter para reemplazar, siempre y cuando coloques por cual lo vas a reemplazar:

change_two <- "dexduzl znzlysis"
chartr(change_two, old = "dz", new = "ta")

En este caso, todas las d son reemplazadas por una t, mientras que las z son reemplazadas por a. Esto es, el caracter original (old) en la \(i\)-esima posición es reemplazado por la coincidencia encontrada en el caracter nuevo (new) en la misma posición. Es por esto que las cadenas pasadas a los argumentos old y new deben contener el mismo número de caracteres.

1.4.3 Valores lógicos.

Estos corresponden a uno de dos posibilidades: TRUE (verdadero) o FALSE (falso), los cuales son comúnmente utilizados en la toma de decisiones dentro de estructuras de control (Woops! Adelantándome otra vez. Ya pronto sabrás de que trata), y se pueden abreviar usando T o F.

Estos valores, son el resultado de evaluar expresiones condicionales o relacionales. Estas expresiones se pueden identificar porque utilizan los operadores relacionales < (es menor que), > (es mayor que), <= (es menor o igual que), >= (es mayor o igual que), == (igualdad), y != (desigualdad). Por ejemplo, supongamos que tienes un variable v a la cual le asignas el valor de 100. Puedes utilizar las operaciones relacionales de la siguiente forma:

    v <- 100

    v > 5         # TRUE: 100 si es mayor a 5
    v < 50        # FALSE: 100 no es menor a 50
    v == 100      # TRUE: 100 es igual a 100
    v != 1000     # TRUE: 100 es diferente de (o no es igual a) 1000

Si utilizas la función typeof sobre el resultado de una operación lógica, el resultado te dirá que el resultado es un valor lógico (p. ej. typeof( v >= 65) arrojara logical como resultado. ¡Pruebalo!).

Por último, existen funciones que devuelven valores lógicos y son utiles para saber si una variable particular corresponde a un tipo de dato dado. Estas funciones son de la forma is.*, donde el asterisco corresponde a algun tipo de dato que quieras comprobar. Por ejemplo,

num <- 1457           # Un numero
int <- 65L            # Un entero
boo <- "Boo!"         # Una cadena de caracteres
logic <-  num > int   # Un valor lógico

# Uso de las funciones is.*
is.numeric(num)
is.integer(int)
is.character(boo)
is.logical(logic)

confirman que las variables declaradas corresponden a sus tipos de datos asignados. Es por ello que todas las funciones arrojan TRUE al ejecutarlas (¿Los ejecutaste para comprobarlo?). También podrías haber evaluado el comando is.numeric(logic). En este caso, el resultado es FALSE, porque logic es una variable de tipo logical, no numeric.

1.4.4 Fechas y tiempo.

Las fechas y el tiempo son unas de esas cosas que todos creemos manejar fácilmente, pero que en la practica se convierten en difíciles de tratar. La razón de esto es que las fechas y el tiempo dependen de dos factores: el movimiento de rotación y traslación terrestre, en conjunto con los problemas geopolíticos de asignar zonas horarias.

Puedes obtener la hora actual, así como fecha y la zona horaria utilizando las funciones Sys.timezone(), Sys.Date(), Sys.time(). Sin embargo, hay veces en las que hay que tratar con fechas y horas que no corresponden al actual y, por lo general, estas son importadas a R en forma de caracteres. Por lo tanto, estas se deben de convertir a objetos de clase Date o POSIXct (en el caso de horas)8 antes de poder usarlas. Puedes hacerlo de la siguiente forma:

# El formato por omisión es YYYY-MM-DD (año, mes, día)
as.Date("1994-10-29")

# A veces es mejor especificar el formato
as.Date("29/10/1994", format = "%d/%m/%y")

Date cuenta que el separador de los componentes de la fecha puede ser distinto: en el ejemplo, una fecha usa como separador “-”, mientras que la otra usa “/”. Otros separadores comunes son el punto (“.”) o sin separador (““). Si utilizas la función typeof sobre un objeto de la clase Date o POSIXct el resultado será double: esto es así porque internamente R representa las fechas y horas como números de punto flotante.

También puedes crear un objeto de clase POSIXct usando la función ISOdate, la cual recibe literales de cadena especificando el año, mes, día, hora, minuto, segundos, e incluso zona horaria (p. ej. ISOdate(year = "2020", month = "03", day = "10")). Luego, puedes convertir este a una fecha (objeto de clase Date) usando as.Date al igual que antes.

Una funcionalidad mas acerca de las fechas y horas, es que puedes realizar cálculos con estas:

# Cálculos con fechas
x <- Sys.Date() # Fecha Actual
y <- as.Date("2020-03-01") # Por ahí inicio la pandemia por Sars-CoV-2

x > y # Operaciones lógicas con fechas
x - y # Te dice cuantos días de diferencia hay entre ambas fechas

# Cálculos con zonas horarias
x <- as.POSIXct("2015-09-22 01:00:00", tz = "Europe/Madrid")
y <- as.POSIXct("2015-09-22 01:00:00", tz = "America/Caracas")

y == x # Operaciones lógicas con zonas horarias
y - x # Te dice cuantos horas de diferencia hay entre ambas zonas

Si no conoces cuales son las zonas horarias, no hay problema. R provee una función que te muestra todas las zonas horarias: OlsonNames().

Pon en práctica lo que aprendiste.

  1. Crea un objeto de clase Date que contenga tu fecha de nacimiento.
  2. Calcula cuantos días y tiempo falta para tu próximo cumpleaños.

1.4.5 Otros valores especiales.

R tiene algunos valores especiales que deben tratarse con cuidado. Algunos de ellos ya han aparecido:

  • Valores faltantes (NA o Not Available): aparecen a menudo cuando se importan datos en los cuales no se han registrado algunas observaciones. Puedes verificar que el valor almacenado en una variable es un NA usando la función is.na().
  • Valores no numéricos (NaN o Not a Number): aparecen cuando se tratan de realizar operaciones que arrojan resultados indefinidos. Ya vimos un ejemplo con la raíz cuadrada de un número negativo. Otro ejemplo ocurre cuando tratas de realizar la operación 0/0.
  • Infinitos (Inf o Infinite): aparece cuando se tratan de realizar operaciones cuyo resultado es un valor muy grande. Por ejemplo, aparece cuando tratas de hacer la operación 1/0. También se pueden obtener infinitos negativos cuando hay negativos involucrados en la operación (p. ej. -1*Inf).

  1. En R es indiferente si usas comillas dobles o sencillas para definir caracteres, el resultado final será el mismo. La razón de la existencia de ambos es que para escribir en una computadora unas comillas simple solo tienes que presionar una tecla, mientras que para la comillas dobles tienes que presionar dos teclas (por lo general, shift + 2, aunque depende de como este configurado tu teclado). Los programadores son flojos, y siempre que puedan ahorrarse el tiempo de presionar aunque sea una tecla, lo harán. Aunque en R ya esta tan extendido el uso de las comillas dobles, que será raro que encuentres código con comillas simples.↩︎

  2. Un patrón hace referencia a una expresión regular o regex (por regular expression) el cual simplemente es una expresión (cadena o secuencia de caracteres) que define un patrón de búsqueda. Este es un tema muy extenso y puede que no se cubra en este texto.↩︎

  3. Esto de las clases y los objetos se volverá más importante y lo aclararé mas adelante. Por ahora solo es necesario que entiendas que los objetos representados en R pertenecen a clases particulares (p. ej. las fechas corresponden a objetos de la clase Date) los cuales pueden tener atributos particulares que definen algunas características del objeto (p. ej. un atributo de un vector es la longitud, esto es, cuantos elementos contiene el vector).↩︎