Las estructuras de datos son objetos que contienen datos y organizados de diferentes maneras. Las estructuras tienen diferentes características, siendo las más importantes su número de dimensiones y si son homogeneas o hereterogeneas.

La siguiente tabla muestra las principales estructuras de datos que maneja R.

Dimensiones Homogeneas Heterogeneas
1 Vector Lista
2 Matriz Data frame
n Array

Sintaxis de R

1 VECTORES

Un vector es una secuencia ordenada de datos del mismo tipo (siendo éstos númericos, cadenas, fechas, etc). Es la estructura de datos más sencilla. Un simple valor (el número 3) constituye un vector númerico:

3
is.vector(3)                                         # Verificamos que el 3 es un vector con la función is.vector().
length(3)                                            # La función length() para conocer su largo.

Lo mismo ocurre con otros tipos de datos, por ejemplo, con cadenas de texto y datos lógicos.

is.vector("tres")
is.vector(TRUE)

1.1 Creación de vectores (manual)

La forma más sencilla de crear un vector es usando la función c() (combinar o contatenar) que debe incluir los datos que deseamos integrar en ese vector, separados por comas (y en caso de datos de tipo charater flanqueados por comillas).

  • Vector formado por números enteros:
x <- c(1,5,6,2,5,7,8,3,5,2,1,0)           # Creación de un vector numérico con la función c (concatenar)
x

assign("edad", c(22,28,37,34,13,24,39,5,33,32))   # Equivalente a crear un vector con el asignador <-
edad
  • Vector formado por caracteres:
nombres <- c("Jose","Catalina","Juan","Pablo")            # Idem vector alfanumérico (cada elemento siempre entre comillas)
nombres
mode(nombres)                             # Tipo de vector
print(nombres)                                                                   
print(class(nombres))                     # Obtención de la clase del vector.

Cada uno de los elementos de un vector de caracteres debe escribirse entre comillas. En caso contrario, la máquina devuelve un mensaje de error.

nombres <- c(Jose,Catalina,Juan,Pablo)    
  • Vector lógico:
c(TRUE, TRUE, FALSE, FALSE, TRUE)         

Otra posibilidad de creación de vectores es la función scan(). Esta función es muy flexible ya que permite varias opciones:

  • Opción 1: introducción manual de los datos 1 5 6 2 5 7 8 3 5 2 1 0 (atención no incluir comas separando cada número).
x_scan <- scan()     # Al terminar Intro 2 veces                                 
x_scan
  • Opción 2. Importación de fichero de texto conociendo su dirección en Internet
notas <- scan("http://aprender.uib.es/Rdir/notas.txt",    
              sep="\t",                                   # Separador
              what="character")                           # Tipo de datos                     
notas
  • Opción 3. Importación directa desde el directorio de trabajo
notas2 <- scan("notas.txt")                               
notas2

1.2 Creación de vectores (secuencias de números)

Existen otras opciones para la creación de vectores. Por ejemplo, podemos crear un vector pidiendo a R que genere secuencias de números

vector1 <- 1:15                                              # Serie del 1 hasta el 15
vector1

vector2 <- 2.3:12.5                                          # Serie del 2,3 hasta el 12,5
34:-5                                                        # Idem entre 34 y -5
-3:5                                                         # Cuidado con los paréntesis
-(3:5)

x <- 1:10                                                    # Vector x compuesto por una serie de 10 números
x

Si nuestro número de inicio contiene decimales, estas serán respetadas al hacer los incrementos o decrecimientos de uno en uno. En contraste, si es nuestro número de final el que tiene cifras decimales, este será redondeado.

  • Se conservan los decimales del inicio
-2.48:2                      
56.007:50
  • Se redondean los decimales del final
166:170.05                   
968:960.928

1.3 Creación de vectores (secuencias de números aleatorios)

Una de las capacidades más importantes de R es la posibilidad de creación de vectores a partir de secuencias de números aleatorios. Cabe señalar que, en realidad, R no genera números puramente aleatorios, sino pseudoaletarios. Esto significa que el algorítmo que genera los numeros necesita una semilla (función set.seed()) para inicializarse. Si conoces la semilla, podemos reproducir exactamente la secuencia de números en diferentes ordenadores, cosa que es imposible sin la semilla (proporcionará diferentes resultados).

set.seed(123)

Fundamentalmente, se utilizan dos funciones para la generación de estos números:

  • La función runif(): genera números racionales (con decimales).
help(runif)
runif(10,                       # Cantidad de número racionales
      3,                        # Valor mínimo
      6)                        # Valor máximo 
  • La función sample() genera números enteros (sin decimales)
help(sample)
sample(1:30,                    # Intervalo de valores (entre 1 y 30 inclusive)
       size = 10,               # Cantidad de números en el intervalo anterior
       replace = FALSE)         # TRUE permite que se repitan los números

La función sample() también se puede utilizar con cadenas de caracteres o con fechas.

sample(c("María", "Pedro","Jaime"),       # Caracteres a reproducir 
       size = 10,                         # Número de caracteres
       replace = TRUE,                    # Se pueden repetir
       prob=c(0.5,0.3,0.2))               # Probabilidades de aparición

La función rnorm() crea una población teórica con distribución normal (con una media y una desviación estándar concretas) .

rnorm(10,                                 # Número de valores
      100,                                # Media
      20)                                 # Desviación típica

También podemos crear vectores a partir de progresiones aritméticas.

help(seq)
# Secuencia ascendente empezando en 2, acabando en 50 y separados por 1,5 
seq(2,                                    
    50,
    by=1.5)
# Secuencia descendente empezando por 80, acabando por 4 y separados por 3,5
seq(80, 
    4, 
    by=-3.5)
# Error: falta el signo (negativo) de la progresión
seq(80,
    4, 
    by=3.5)                      
# Progresión aritmética de 2 a 10 con 10 miembros
seq(2, 10, 
    length.out=10)                             
# Progresión aritmética desde 2, con 10 miembros cada 0.5
seq(2, 
    by=0.5, 
    length.out=10)                             

De nuevo, es posible crear estas progresiones con cadenas de caracteres.

alfabeto <- letters[1:11]           # Progresión de las 11 primeras letras del alfabeto
alfabeto

La función rep() crea vectores a partir de repeticiones

rep(1,6)                            # El número 1 repetido 6 veces
rep("Santander",5)                  # Igual, pero los caracteres siempre entre comillas

sample(rep(c("lectura", "musica", "deportes"),25))    # Igual, pero los caracteres siempre entre comillas

rep(c(1,2,3),times=5)               # Secuencia 1 2 3 repetida 5 veces

rep(c(1,2,3),each=5)                # Secuencia 1 2 3 pero cada valor se repite 5 veces

rep(c(1,2,3,4), 
    times=c(2,3,4,5))               # Repite cada elemento un diferente número de veces

1.4 Modificación de vectores.

Una vez creados los vectores, pueden ser modificados posteriormente. Por ejemplo, si deseamos agregar un elemento a un vector ya existente, lo añadimos asignándolo a nuestro vector original.

mi_vector <- c(TRUE, FALSE, TRUE)

mi_vector <- c(mi_vector, FALSE)
mi_vector

También es posible combinar 2 vectores previos

mi_vector_1 <- c(1, 3, 5)
mi_vector_2 <- c(2, 4, 6)

mi_vector_3 <- c(mi_vector_1, mi_vector_2)
mi_vector_3

Si intentamos combinar datos de diferentes tipos en un mismo vector, R realizará una coerción automáticamente. El vector resultante será del tipo más flexible, siguiendo las reglas de coerción antes señaladas.

  • Creamos un vector numérico.
mi_vector <- c(1, 2, 3)
class(mi_vector)
  • Al agregar una cadena de texto, nuestro vector pasará a convertirse en un vector de cadena de texto.
mi_vector_nuevo <- c(mi_vector, "a")
class(mi_vector_nuevo)

Como las cadenas de texto son el tipo de dato más flexible, siempre que creamos un vector que incluye un dato de este tipo, el resultado será un vector de cadena de texto.

mi_vector_mezcla <- c(FALSE, 2, "tercero", 4.00)
class(mi_vector_mezcla)

La modificación de los elementos de un vector se puede hacer elemento a elemento.

x <- 1:10
x
x[3] <- 15                        # En la posición 3 escribimos 15
x[11] <- 25                       # Añadimos en la posición 11 un 25
x

También se puede llevar a cabo para todo un subvector de golpe.

x[c(2, 3, 4)]=x[c(2, 3, 4)]+10 # Sumamos 10 a las entradas en las posiciones 2, 3 y 4
x
rm(list=ls())

Desafío

Crea los siguientes vectores:

  • El vector ID como secuencia de números de 1:100.
ID <- 1:100
  • El vector barrio como una secuencia aleatoria de 100 elementos compuesto por cadenas de texto con los siguientes elementos (Centro, Arganzuela, Retiro, Salamanca, Chamartin, Tetuán y Chamberí) repetidos 100 veces.
barrio <- sample(c("Centro", "Arganzuela", "Retiro", "Salamanca", "Chamartin", "Tetuán", "Chamberí"),       
                size = 100,                         # Número de caracteres
                replace = TRUE)                    # Se pueden repetir
  • El vector parques como secuencia aleatoria 100 números enteros entre los valores 3 y 10 con reemplazo.
parques <- sample(3:10,                    # Intervalo de valores (entre 1 y 30 inclusive)
                  size = 100,              # Cantidad de números en el intervalo anterior
                  replace = TRUE)  
  • El vector superficie como una secuencia de 100 números racionales entre 3 y 12.
superficie <- runif(100,                      # Cantidad de número racionales
                    3,                        # Valor mínimo
                    12)
  • Un vector obras formado por 100 valores (0,1,NA).
obras <- sample(c(0:1, NA),                #Caracteres a reproducir 
       size = 100,                         # Número de caracteres
       replace = TRUE,                     # Se pueden repetir
       prob=c(0.75,0.15,0.10))              # Probabilidades de aparición
  • Un vector fecha_inauguración formado por 100 fechas entre el 1 de enero de 1930 y el 1 de enero de 2017
fecha_inauguracion <- sample(seq(as.Date('1930-01-01'), 
                                 as.Date('2017-01-01'), 
                                 by = "day"), 
                             100)

1.5 Acceso a los elementos de un vector [ ]

La notación [] hace siempre referencia a la posición de un elemento en un vector Es obligatorio su uso para acceder con algún elemento de un vector.

n <- 1:8
x <- 2*3^n-5*n^3*2^n
x

1.6 Selección de elementos de un vector por su POSICION

x[3]                                     # El tercer elemento del vector, siempre entre corchetes
x(3)                                      # ¿El tercer elemento de un vector?
x[-3]                                     # x sin el tercer elemento
x[c(1,3,5)]                               # Varios elementos al mismo tiempo
x[3:7]                                    # Los elementos tercero a séptimo de x
x[7:3]                                    # Los elementos séptimo a tercero de x

x[length(x)]                              # El último elemento del vector
x[length(x)-5]                            # El sexto elemento del vector, pero empezando empezando por el final (es decir, quita los 5 últimos)

x[seq(1, 
      length(x), 
      by=2)]                              # Secuencia empezando por uno, cada dos números hasta el final (elementos en posición impar)
x[seq(2, 
      length(x), 
      by=2)]                              # Idem par 

x[-seq(1, 
       length(x), 
       by=2)]                             # Borramos los elementos en posición impar, equivalente a la anterior orden
x[(length(x)-5):length(x)]                # Los últimos 6 elementos de x
x[length(x)-5:length(x)]                  # OJO con los paréntesis

La selección de la posición de los elementos de un vector formado por caracteres es similar

z <- c("k", "j", "h", "a", "c", "m")
z[3]
z[c(1, 3, 5)]
z[2:6]

1.7 La función which()

Con la función which()podemos conocer qué elemento de un vector satisface una determinada condición. Aplicada a un vector de valores lógicos devuelve sus posiciones.

x <-  sample(1:10,               # Intervalo de valores (entre 1 y 30 inclusive)
            size = 20,           # Cantidad de números en el intervalo anterior
            replace = TRUE)  

which(x>3)                       # Posiciones de los elementos mayores que 3
which(x>2 & x<=5)                # Posiciones de los elementos 2 y <= 5
which(x!=2 & x!=5)               # Posiciones de los elementos diferentes de 2 y 5
which(x>5 | x<=2)                # Posiciones de los elementos > 5 ó <= 2

La función which.min() proporciona la primera posición en la que el vector toma su valor mínimo; which.max() hace lo mismo, pero para el máximo.

which.min(x)
which(x==min(x))

1.8 Selección de los elementos de un vector por su VALOR.

¡ATENCIÓN! También es posible seleccionar

x <- c(1,5,6,2,5,7,8,3,5,2,1,0)
x[x>3]                                  # Elementos mayores que 3
x>3                                      

x[x>2 & x<=5]                           # Elementos mayores que 2 y menores o iguales que 5
x[x!=2 & x!=5]                          # Elementos diferentes de 2 y de 5
x[x>5 | x<=2]                           # Elementos mayores que 5 o menores o iguales que 2
x[x>=4]                                 # Elementos mayores o iguales que 4
x[!x<4]                                 # Condición equivalente a la anterior
x[x%%4==0]                              # Múltiplos de 4 

Si un vector no contiene ningún término que satisfaga la condición que imponemos, obtenemos un vector vacío. R lo indica con numeric(0) si es numérico, character(0) si es alfanumérico, o integer(0

x <- 2^(0:10)
x
x[20<x & x<30]                         # Elementos de x estrictamente entre 20 y 30
length(x[20<x & x<30])                 # ¿Cuántas entradas hay entre 20 y 30?
which(x>1500)                          # ¿Qué índices de elementos mayores que 1500

x <- c()                               # Vector vacío
x                                      # R no sabe de qué tipo son los datos que faltan en un vector vacío
z <- NULL                              # Idem
z                                      # Idem
y <- c(x, 2, z)                        # Sólo muestra el único elemento del vector con valor real
y

Los operadores lógicos también se pueden usar para pedir si una condición sobre unos números concretos se satisface o no

exp(pi)>pi^(exp(1))           # ¿Es mayor e^pi que pi^e?
1234567%%9==0                 # ¿Es 1234567 múltiplo de 9?

1.9 Selección de los elementos de un vector utilizando los elementos de otro vector

x <- c(1,5,6,2,5,7,8,3,5,2,1,0)
y <- c(2,-3,0,1,2,-1,4,-1,-2,3,5,1)
x[y>0]                                     # Entradas de x correspondientes a entradas positivas de y

1.10 Vectorización de operaciones

Existen algunas operaciones, por ejemplo las operaciones aritméticas y relacionales, que se aplican a cada uno de los elementos de un vector. Por ejemplo, creamos un vector numérico.

mi_vector <- c(2, 3, 6, 7, 8, 10, 11)
  • Si aplicamos operaciones aritméticas, obtenemos un vector con un resultado por cada elemento.
mi_vector + 2                      # Operaciones aritméticas
mi_vector * 2
mi_vector %% 2
  • Al aplicar operaciones relacionales, obtenemos un vector de TRUE o FALSE, uno para cada elemento comparado.
mi_vector > 7
mi_vector < 7
mi_vector == 7

Esta manera de aplicar una operación es muy eficiente. Comparada con otros procedimientos, requiere de menos tiempo de cómputo, lo cual a veces es considerable, en particular cuando trabajamos con un número grande de datos. Aunque el nombre de este proceso es vectorización, también funciona, en ciertas circunstancias, para otras estructuras de datos.

1.11 Operaciones con vectores

x <-c(1,2,3,4,5)
y<-c(2,4,6,8,10)
x+y                                     # Suma de vectores
x*y                                     # Producto de vectores término a término
x^2                                     # Cuadrado de todos los términos de un vector
sum(x)                                  # Suma de todos los valores de un vector
mean(x)                                 # Media de todos los valores de un vector
sum(x*y)                                # Producto escalar de dos vectores
plot(x,y)                               # Gráfico de nube de puntos


1:5+1:5                                 # Suma entrada a entrada
(1:5)*(1:5)                             # Producto entrada a entrada
(1:5)^(1:5)                             # Potencia entrada a entrada

n <- 1:20                               # Secuencia 1,...,20
x <- 3*2^n-20                           # Aplicamos la fórmula a la secuencia desde n=1,...,20
x
n <- 0:20
y <- n/(n^2+1)
y
(0:20)/((0:20)^2+1)

x <- c(1,5,6,2,5,7,8,3,5,2,1,0)
length(x)
max(x)
min(x)
sum(x)
prod(x)
mean(x)
cumsum(x)
diff(x)
diff(cumsum(x))

sort(x)
sort(x, 
     dec=TRUE)
rev(x)

n <- 0:200
sum(1/(n^2+1))

i <- 0:20
x <- 2^(-i)
y <- cumsum(x)
y

1.12 Datos ausentes en un vector.

Los datos ausentes aparecen codificados en R como NA. Hay que tener en cuenta que, en cualquier cálculo, la presencia de NA da como resultado NA. Por ello, es conveniente decidir si se eliman del análisis o se sustituyen por otros datos.

x <- c(1,4,3,NA,7,6,9,4,0,8)
x
sum(x)                                              # Al incluir NA, no realiza ningún cálculo
is.na(x)                                            # Preguntamos si hay algún caso perdido
which(is.na(x))                                     # Los casos perdidos a los elementos situados en las posiciones 12,13,15 y 16

Cómo trabajar los datos ausentes en R. Varias posibilidades:

  • Primera posibilidad: eliminar los datos ausentes
x[!is.na(x)]
  • Segunda posibilidad: avisar al programa que hay datos ausentes, pero que no los tome en consideración para cualquier cálculo
sum(x, na.rm=TRUE)                                  # argumento na.rm = TRUE; la función sólo tiene en cuenta los valores no ausentes
  • Tercera posibilidad: sustituimos los datos ausentes por otros, por ejemplo la media de todos los elementos válidos (operación conocida como IMPUTACIÓN).
y <- x                                              # Creamos una copia de x y la llamamos y
y[is.na(y)] <- mean(y, na.rm=TRUE)                  # Reemplazamos los valores NA del vector y por la media del resto de elementos válidos del vector
y

Se eliminan todos los objetos del área de trabajo y se limpia la consola.

rm(i, mi_vector, n, x, y, z)                                           
cat("\014")                                               

Desafío

  • ¿Cuáles son los elementos ubicados en las posiciones 25, 45 y 60 de la variable ID?.
ID[c(25,45,60)]   
  • ¿Cuáles son los elementos ubicados en las posiciones pares empezando por 60 de la variable barrio?
barrio[seq(60, 
      length(barrio), 
      by=2)]  
  • ¿Cuál es la posición de los elementos de la variable parques mayores que 5 y menores o iguales que 8 (utiliza la función which)?.
which(parques > 9 & parques <= 10)    
  • ¿Qué valores de la variable superficie superan el valor de 5?.
superficie[superficie >5]                           
  • ¿Cuáles son los últimos 6 elementos de la variable fecha_inauguración?.
x[(length(x)-5):length(x)]  
  • Calcula la superficie verde media en metros cuadrados de cada elemento (originalmente en hectáreas?.
superficie / parques * 10000
  • Pregunta al programa si el vector obras contiene algún caso perdido (NA)
is.na(obras)                          # Preguntamos si hay algún caso perdido
  • ¿Dónde estás situados los casos ausentes (NA)?
which(is.na(obras))                   

2 DATA FRAMES

Los data frames son estructuras de datos de dos dimensiones que pueden contener datos de diferentes tipos. Es la estructura de datos de mayor uso en Ciencias Sociales. Mientras que en una matriz todas las celdas deben contener datos del mismo tipo, cada columna de un dataframe pueden contener un tipo diferente de datos. En un dataframe:

En términos generales, las filas de un data frame representan casos, individuos u observaciones, mientras que las columnas representan atributos o variables. Por ejemplo, el famoso conjunto de datos Iris:

Dataset Iris

2.1 Creación de un dataframe

A la hora de crear un dataframe hay que tener en cuenta que, en realidad, está compuesto por vectores (columnas) con la misma longitud.

La creación de un dataframe puede realizarse siguiendo varios procedimientos:

  • OPCIÓN 1: mediante la función data.frame(), y utilizando como argumentos cada uno de los vectores que conformarán el dataframe. A cada vector se le asignará un nombre, que se convertirá en el nombre de la variable (columna). Como todos los nombres, es recomendable que sea claro.
df1 <- data.frame("entero" = 1:4, 
                    "factor" = c("a", "b", "c", "d"), 
                    "numero" = c(1.2, 3.4, 4.5, 5.6), 
                    "cadena" = as.character(c("a", "b", "c", "d")))
df1

Si los vectores no son de la misma longitud, R nos devuelve un error. El ejemplo que viene a continuación lo muestra

data.frame("entero" = 1:3, 
           "factor" = c("a", "b", "c", "d"), 
           "numero" = c(1.2, 3.4, 4.5, 5.6), 
           "cadena" = as.character(c("a", "b", "c", "d")))
  • OPCIÓN 2: agrupando varios vectores creados previamente.
ID <- c(1, 2, 3, 4, 5, 6, 7)                              
sexo <- c(0,1,1,0,1,0,0)   
edad <- c(25, 34, 28, 43, 70, 65, 52)
peso <- c(83.5,63.8,102.6,88.5,80.1,55.8,64.7)
ingreso <- c("10/24/08","10/28/08","10/1/08","10/12/08","5/1/09","01/16/09","01/28/09")
diabetes <- c("Tipo1", "Tipo2", "Tipo2", "Tipo1", "Tipo1", "Tipo1", "Tipo2")    
estatus <- c("Pobre", "Excelente", "Mejorando", "Mejorando", "Mejorando", "Excelente","Pobre")  
estudios <- c(1,2,0,1,3,2,3)

df2 <- data.frame(ID, sexo, edad, peso, ingreso, diabetes, estatus, estudios)
df2

Para no llenar el Global Environment de objetos que no se utilizarán más, se eliminan todos los vectores creados previamente (¡pero no el dataframe!).

rm(ID, sexo, edad, peso, ingreso, diabetes, estatus, estudios)    # Eliminamos los vectores originales

También podemos convertir una matriz en data frame (coercionar) y viceversa.

matriz <- matrix(1:12, ncol = 4)               # Creamos una matriz.
matriz

df3 <- as.data.frame(matriz)                    # Usamos as.data.frame() para coercionar .
class(df3)                                      # Verificamos el resultado
df3                                             # Resultado
rm(matriz)

Desafío

Crea un dataframe denominado zonas_verdes que contenga los vectores ID, barrio, parques, superficie, obrasy fecha_inauguracion en este mismo orden.

zonas_verdes <- data.frame(ID, barrio, parques, superficie, obras, fecha_inauguracion)
zonas_verdes

Elimina posteriormente los vectores originales.

rm(ID, barrio, parques, superficie, obras, fecha_inauguracion)

2.2 Estructura de un dataframe

Podemos solicitar información muy diversa acerca de cualquier dataframe.

df2                      # Vista general del dataframe

dim(df2)                 # Dimensiones (filas y columnas)
length(df2)              # La longitud de un data frame es igual a su número de columnas

class(df2)               # Tipo de estructura del objeto
str(df2)                 # Información sobre la estructura del dataframe

head(df2, 3)              # Primeras 3 líneas
tail(df2, 5)              # Últimas 5 líneas

names(df2)               # Etiquetas de las variables
rownames(df2)            # Identificadores de los casos (filas).
dimnames(df2)            # Lista los identificadores de las filas y los nombres de las columnas.

Una utilidad de R bastante interesante es una ventana de datos cuya estructura recuerda a Excel.

View(df2)                

2.3 Acceso a los elementos (filas=casos; columnas=variables) de un dataframe

  • Sólo a una variable (columna). Hay dos opciones

  • OPCIÓN 1: utilizando la sintaxis nombre del dataframe$variable. Es la más habitual.

df2$edad               
  • Opción 2: mediante la función attach() se cargan en memoria las variables del dataframe, de tal manera que no es necesario indicarle a R que esa variable pertenece a un dataframe concreto. Esta circunstancia se desactiva con la función detach().

¡ATENCIÓN! cualquier modificación del dataframe original (pe. el añadido de una nueva variable o la recodificación de otra) desactiva la función attach().

attach(df2)            
edad                   

detach(df2)            
  • A uno o varios elementos concretos. Si para acceder a cualquier elemento de un vector utilizamos los corchetes [], para acceder a los elementos de un dataframe, que tiene una estructura formada por filas y columnas se utilizan los corchetes, pero incluyendo una , que separa las filas de las columnas [fila, columna]
df2[1:2, ]                                                 # Muestra los dos primeros CASOS (FILAS)

df2[, 1:2]                                                 # Muestra las 2 primeras VARIABLES (COLUMNAS)
df2[1:2]                                                   # Por defecto muestra las dos primeras VARIABLES
df2[c("diabetes","estatus")]                               # Especifica las VARIABLES según su nombre

df2$edad[2:5]                                              # CASOS 2 a 5 de la VARIABLE edad 
df2[2:5]                                                   # Idem
df2[1:5,1:3]                                               # 5 primeras filas y las 3 primeras columnas  

df2[df2$sexo=="Hombre" & df2$edad > 20, ]                  # Selección de casos basada en criterios
df2[sexo=="Hombre" & edad > 20, ]                          # Mensaje de error: ¿por qué?
df2[df2$sexo=="Hombre" & df2$edad > 20, ][1:3, ]           # Encadenamiento de varias opciones: las 3 primeras filas de la anterior

Desafío

  • ¿Cuál es la longitud del dataframe zonas verdes?
length(df2)              # La longitud de un data frame es igual a su número de columnas
  • Muestra en pantalla los 10 primeros casos del dataframe zonas verdes.
head(zonas_verdes, 10)             
  • Muestra la estructura general del dataframe zonas_verdes
str(zonas_verdes)                 # Información sobre la estructura del dataframe
  • Muestra los casos del 17 a 45 de la variable fecha_inauguración.
zonas_verdes$fecha_inauguracion[17:45] 
  • Muestra los casos en los que la fecha de inauguración sea posterior
zonas_verdes[zonas_verdes$fecha_inauguracion >= "2012-07-01", ]
  • Utiliza los operadores relacionales anteriores para mostrar los casos que corresponden a parques en obras (código 1) y cuya superficie supera las 10 hectáreas
zonas_verdes[zonas_verdes$obras == 1 & zonas_verdes$superficie > 10, ]

2.4 Transformar una variable original en un factor

Es muy habitual disponer tanto de variables en forma de cadenas de texto como de variables cuantitativas discretas que, en realidad, representan variables cualitativas, como el sexo (habitualmente codficada como 0 = hombre y 1 = mujer). Para trabajar con ellas se pueden transformar en factores (variable cualitativa nominal), lo que permitirá operar con ella de una manera más eficiente

class(df2$sexo)                                            # Verifica si la variable estatus es un factor.
is.factor(df2$sexo)                                        # Conversión de una variable numérica en factor.            
df2$sexo <- factor(df2$sexo,                               # Convierte la variables categórica sexo en factor.
               levels=c(0,1),                              # Categorías (o niveles)
               labels=c("Hombre","Mujer"))                 # Etiquetas
df2$sexo
is.factor(df2$sexo)                                                                 

En el caso de variables cualitativas ordinales también es posible transformar una variable original en un factor ordenado. Existen dos posibilidades.

  • OPCIÓN 1: cuando la variable original está compuesta de valores numéricos.
df2$estudios <- ordered(df2$estudios,                    # Convierte la variables categórica estudios en factor ordenado
                    levels=c(0,1,2,3),                   # Categorías (o niveles)
                    labels=c("Sin estudios","Estudios Primarios","Estudios Secundarios","Estudios Superiores"))
df2$estudios
  • OPCIÓN 2: cuando la variable original es una variable alfanumérica (formada por caracteres). Este es una caso más simple, ya que R reconoce que cada una de las etiquetas diferentes es un factor.
df2$diabetes <- as.factor(df2$diabetes)
df2$diabetes
df2$estatus <- as.factor(df2$estatus)
df2$estatus

Desafío

Discute qué variables del dataframe zonas_verdes pueden ser transformadas en factores. Transfórmalas en factores.

zonas_verdes$barrio <- as.factor(zonas_verdes$barrio)

zonas_verdes$barrio <- factor(zonas_verdes$barrio,
                              levels=c(0,1),
                              labels=c("Sin obras","Con obras"))

¡ATENCIÓN! A veces es conveniente duplicar la variable original creando una nueva variable como factor.

2.5 Selección de casos y variables.

La creación de otros dataframes a partir de la selección de casos y/o variables es una prolongación del apartado anterior, dado que simplemente creamos un nuevo objeto (como dataframe) accediendo a los elementos del dataframe original.

  • Uno o varios casos (horizontal).
nuevasfilas1 <- df2[c(5:7), ]                                           # Opción 1
nuevasfilas1

nuevasfilas2 <- df2[1:3,    ]                                           # Opción 2
nuevasfilas2

rm(nuevasfilas1, nuevasfilas2)
  • Una o varias variables (columnas).

  • OPCIÓN 1. Identificando las nuevas variables como un vector y utilizando este vector para extraer las columnas a continuación.

variables.seleccionadas <- c("sexo", "estudios")                                
nuevascolumnas <- df2[   , variables.seleccionadas]
nuevascolumnas

rm(variables.seleccionadas, nuevascolumnas)
  • OPCIÓN 2. La función subset() extrae un subconjunto que cumple con una o varias condiciones.
hombres <- subset(df2,                             # Dataframe
                  sexo == "Hombre")                # Condición: selección de los "Hombres" en la variable "sexo".                    
hombres
mayores <- subset(df2,                             # Dataframe
                  sexo == "Mujer" & edad>30)       # Condicion(es)
mayores
grupo_riesgo <- subset(df2,                                                  # Dataframe
                          sexo == "Hombre" & edad > 50 & estatus == "pobre")    # Condicion(es)

nuevosdatos <- subset(df2,                            # Dataframe
                      edad >= 45 | edad < 24,         # Condiciones
                      select=c("estatus","diabetes")) # Variables seleccionadas (estatus y grupo)

nuevosdatos <- subset(df2,                            # Dataframe
                      sexo=="Hombre" & edad > 30,     # Condiciones
                      select = ingreso:estatus)       # Variables seleccionadas: desde estudios a grupo
nuevosdatos

rm(hombres, mayores, grupo_riesgo, nuevosdatos)

2.6 Eliminar variables

Para mostrar cómo eliminar variables de un dataframe, respetando el dataframe original, es pertinente crear un nuevo dataframe, denominado borrame (que luego será borrado), similar al original, sin alguna de las variables.

Una alternativa puede ser la función subset() que requiere como argumentos:

  • El dataframe con el que se está trabajando.

  • Las variables a eliminar. Son posibles diferentes alternativas:

borrame <- subset(df2, 
                  select =-edad)                     # Elimina del dataframe la variable "edad". 
borrame <- subset(df2, 
                  select =-c(edad,sexo))             # Elimina del dataframe las variables "edad" y "sexo". 
borrame <- subset(df2, 
                  select =-c(ingreso:estatus))       # Elimina todas las variables situadas entre "ingreso" y "estatus".
rm(borrame)

Desafío

  • Selecciona los siguientes casos como un nuevo dataframe que llamarás borrame1

  • El caso 37.

  • Los casos 57 a 83

  • Los casos 33, 44 y 55

  • Elimina borrame1.

  • Crea un nuevo dataframe denominado barrio_salamanca formado por los casos correspondientes al barrio de Salamanca.

  • Crea un nuevo dataframe denominado subconjunto formado por las variables ID, barrio y superficie.

  • Crea un nuevo dataframe denominado subconjunto_2 que comprenda aquellos casos correspondientes a las variables barrio y fecha_inauguración en los que el número de parques sea igual o mayor que 4 y estén actualmente en obras.

  • Elimina del dataframe barrio_salamanca las variables parques y `fecha_inauguración”.

2.7 Dividir un dataframe

Es posible dividir un dataframe con la función split(). Esta función divide los datos originales en diferentes grupos, basándose en los valores de una variable.El siguiente bloque resume los argumentos de la función y su descripción.

° split(x, # Vector o data frame ° f, # Grupos de clase factor, vector o lista ° drop = FALSE, # Si eliminar los grupos no usados (TRUE) o no (FALSE) ° sep = “.”, # Cadena de caracteres para separar los grupos cuando f es una lista ° lex.order = FALSE, # Si la concatenación de factores debe ser ordenada léxicamente (TRUE) o no (FALSE) ° …) # Argumentos adicionales :::

Es posible juntarlos con la función unsplit().

fichero.dividido.sexo <- split(df2, df2$sexo)
fichero.dividido.sexo
fichero.dividido.sexo.diabetes <- split(df2, list(df2$sexo, df2$diabetes))

unsplit(fichero.dividido.sexo, df2$sexo)

rm(fichero.dividido.sexo, fichero.dividido.sexo.diabetes)

2.8 Recodificación de variables

Consiste en la creación de una variable nueva a partir de una preexistente. Ejemplo: vamos a recodificar la variable edad en 3 categorías (jóvenes, adultos, ancianos) creando una nueva variable denominada grupo en el dataframe df2.

df2$grupo[df2$edad > 64] <- "ancianos"
df2$grupo[df2$edad >= 30 & df2$edad <= 64] <- "adultos"
df2$grupo[df2$edad < 30] <- "jovenes"
df2

Como alternativa, las funciones with() y within() ejecuta una o varias instrucciones sobre las variables de un dataframe accediendo a ellas solamente por su nombre, sin necesidad de utilizar attach().

df2 <- within(df2,{
  grupo <- NA                                              # Crea la variable grupo, pero vacía  
  grupo[edad > 64] <- "ancianos"                           # Los restantes comandos rellenan esa variable de manera ordenada
  grupo[edad >= 30 & edad <= 64] <- "adultos"
  grupo[edad < 30] <- "jovenes" })

2.9 Cambio del nombre de una variable

  • OPCIÓN 1: a través del editor de texto
fix(df2)                    
  • OPCIÓN 2: directamente desde teclado
names(df2)                                                        # Lista los nombres de las variables
names(df2)[4] <- "hospitalizacion"                                # Cambia el nombre de la 4ª variable
df
names(df2)[5:8] <- c("item1", "item2", "item3", "item4")          # Renombra las columnas 5 a 8 con los nombres del vector
  • OPCIÓN 3: mediante la función rename.vars() del paquete gdata.
if(!require("gdata")) install.packages("gdata")                                 

library(gdata) 
df2 <- rename.vars(df2,
                   c("hospitalizacion","item1", "item2","item3","item4"),
                   c("edad","diabetes","estatus","estudios","grupo"))
df2

2.10 Reordenar un dataframe según los valores de una o varias variables

df2[order(df2$edad), ]
df2[order(df2$sexo,df2$edad), ]                            # Ordeno el nuevo dataframe según género y edad
df2[order(df2$sexo, -df2$edad), ]                          # Según género (ascendente) y edad (descendente)

Desafío

  • Divide el dataframe en varios grupos utilizando como factor la variable obras.

  • Crea una nueva variable denominada tamaño_parque recodificando la variable superficie en tres categorías:

  • Microparques: hasta 1 ha

  • Parques medianos: entre 1 y 5 ha

  • Grandes parques: > 5 ha.

  • Cambia el nombre de esta última variable y reemplazalo por tamaño.

  • Ordena el dataframe zonas_verdes utilizando como criterio el número de parques en orden descendente. Extrae a continuación los 6 primeros casos.

2.11 Añadir filas (casos) a continuación de los datos originales

Para ello, en primer lugar vamos a crear un dataframe con las variables e df2, pero vacío (reflejando por inclusión, tras el tipo de variable, del número 0 entre paréntesis).

nuevos.df <- data.frame(ID=numeric(0),               # c(8, 9, 10, 11, 12, 13, 14)
                         sexo=numeric(0),            # c(0,1,0,0,0,1,1)
                         edad =numeric(0),           # c(35, 44, 38, 53, 80, 75, 62) 
                         ingreso=character(0),       # c("11/24/08","11/28/08","11/1/08","11/12/08","2/1/09","03/16/09","03/28/09")
                         diabetes=character(0),      # c("Tipo2", "Tipo1", "Tipo1", "Tipo2", "Tipo2","Tipo2", "Tipo1")
                         estatus=character(0),       # c("Mejorando", "Mejorando","Excelente","Pobre","Pobre", "Excelente", "Mejorando")
                         estudios=numeric(0))        # c(1,3,2,3,1,2,0) 

A continuación se abrirá el editor de texto (función edit()) y se rellenará manualmente los datos.

nuevos.df <- edit(nuevos.df)

#El dataframe se crea uniendo los uniendo los vectores

#nuevos.df <- data.frame(ID, sexo, edad, ingreso, diabetes, estatus, estudios) 

Con la función rbind() (r de row) los nuevos casos se añaden a continuación.

df.actual <- rbind(df2,nuevos.df)        # Añadimos nuevas.filas debajo
df.actual 
str(df.actual)

Crea un nuevo dataframe, compuesto por 25 casos, con las mismas variables que el dataframe zonas_verdes, salvo que ahora añadiremos los parques de nuevos barrios.

  • Se creará una variable identificativa ID compuesta de 25 números consecutivos entre 101 y 125.

  • El vector barrio debe estar formado por 25 elementos con los siguientes elementos (Fuencarral, Latina, Usera) repetidos.

  • El vector parques estará formado por secuencia aleatoria de 25 números enteros entre los valores 3 y 10 con reemplazo.

  • El vector superficie debe ser una secuencia de 25 números racionales entre 5 y 10.

  • El vector obras estará formado por 25 valores, pero sin valores NA.

  • El vector fecha_inauguración contendrá 25 fechas entre el 13 de junio de 1978 y el 31 de agosto de 2022

  • Estos vectores se unen para formar un dataframe denominado zonas_verdes_añadido, que una vez añadido al dataframe zonas_verdes, eliminándose posteriormente.

  • A continuación, se eliminan tanto los vectores como el dataframe zonas_verdes_añadido.

Atención: en caso de existir casos repetidos en los dos dataframes se utiliza la función merge():

  • con el argumento all=FALSE (valor por defecto) solo aquellos que son comunes a ambos.

  • con el argumento all=TRUE se consigue que se muestren todos los datos de ambos dataframes

2.12 Añadir columnas (variables) a los datos originales

Se pueden añadir diferentes vectores para crear un único dataframe con la función cbind(c de column). ¡ATENCIÓN! todos ellos deben tener la misma longitud.

length(df.actual) 
hijos <- c(1,0,0,1,2,3,2,1,3,2,1,0,3,0)

df.actual <- cbind(df.actual, hijos)

En caso de existir alguna variable repetida, puede utilizarse como guía; en este caso, la variable repetida será ID. Tenemos que crear el dataframe pulsacionescon el ID de cada fila y el número de pulsaciones, para luego mezclarlos.

nuevos.datos <- data.frame(ID=numeric(0),
                           pulsaciones=numeric(0))
nuevos.datos <- edit(nuevos.datos)

total <- merge(df.actual, nuevos.datos, by="ID")

A continuación, se añadirá una nueva variable, denominada canchas, que informa sobre el número de canchas deportivas en cada parque.

  • Primero, se creará una variable identificativa ID compuesta por 125 números consecutivos entre 1 y 125.

  • En segundo lugar, la variable canchas estará formado por una secuencia aleatoria de 125 números enteros entre los valores 0 y 3, con reemplazo.

  • Posteriormente, se creará un nuevo dataframe denominado canchas, que se unirá al dataframe zonas_verdes. Para ello utiliza la función merge()y el argumento by.

2.13 El problema de los datos ausentes (lagunas) en un dataframe

# Creamos un dataframe provisional
x1 <- c("a", "b", NA, NA,"a","b","b","b","c","d")
y1 <- c(2,8,7,5,1,9,4,3,7,NA)
borrame <- data.frame(x1,y1)
head(borrame, n=10)

rm(x1, y1)

Existen multitud de procedimientos para aplicar cuando tenemos valores perdidos. Básicamente existen dos aproximaciones posibles:

  • Eliminar los casos y las variables que tienen datos faltantes.

  • Imputar los valores perdidos, es decir, sustituirlos por estimaciones.

¿Cómo verificamos si existen datos ausentes?

sapply(borrame, 
       function(x) sum(is.na(x)))                                      # Cuenta el número de nulos por variable
  • Primera alternativa: eliminar los casos (filas) con lagunas
borrame2 <- borrame             # Creamos un nuevo objeto copiado del original
borrame2 <- borrame2[!is.na(borrame2$x1), ]                                        # Cada variable por separado; habría que repetir para y1
# ó
borrame3 <- na.omit(borrame)                                                     # Elimina todas las filas que contengan algun valor nulo
borrame3
borrame4 <- borrame[complete.cases(borrame), ]                                   # Similar al anterior
borrame4     
  • Segunda alternativa: imputar (reemplazar) los valores ausentes por otro valor (pe. la media; sólo variables cuantitativas).
borrame[is.na(borrame$y1), "y1"] <- mean(borrame$y1, na.rm=T)
borrame
rm(x1, y1,borrame, borrame2, borrame3, borrame4)                       

2.14 Cambio en la disposición de las variables: de filas a columnas y viceversa

El formato largo es el más conveniente para filtrar y realizar algunos tipos de agregaciones, mientras que el formato ancho es típico de datos realizados en el tiempo.

  • Formato ancho: una columna para cada variable.

  • Formato largo: cada fila es una combinación única de variable de identificación.

install.package("reshape2")
library(reshape2)

# La función de fusión ("melt»): permite pasar de un formato ancho a uno largo.
# Necesitamos indicar qué variables se miden y cuáles son de identificación.
# Argumentos:
#    data: Conjunto de datos para fundir
#    id.vars: Variables de identificación (Id). Si está en blanco, usará todas las variables que no sean measure.vars. Puede ser un valor entero (posición variable) o cadena (nombre de la variable)
#    measure.vars: Variables medidas Si está en blanco, usará todas las variables que no sean id.vars. Puede ser un valor entero (posición variable) o cadena (nombre de la variable)
#    variable_name: Nombre de la variable que almacenará los nombres de las variables originales
#    na.rm: ¿Deben eliminarse los valores de NA del conjunto de datos?

# ¡ATENCIÓN! la función asume que cualquier variable de tipo factor o carácter es una variable "id" si no se especifica explícitamente.

sujeto <- c("Ana", "Pepe")
tiempo <- c(1,1)
edad <- c(33, NA)
peso <- c(NA, 90)
altura <- c(1.87,1.73)
pareja <- as.data.frame(sujeto, tiempo, edad, peso, altura)

largo<- melt(pareja)
largo
largo<- melt(pareja, 
             id=c("sujeto","tiempo"))        # Si queremos que la variable «tiempo» se incluya como variable de identificación, junto al sujeto
largo
melt(pareja, 
     id=c("sujeto","tiempo"), na.rm=T)       # No incluye los valores ausentes (NA).

La función de moldear (“cast») nos permite pasar de un formato largo a uno ancho. Los argumentos son:

  • data: el conjunto de datos para utilizar.

  • formula: si fuera necesario, con la fórmula de conversión describimos la forma del formato de salida (si omite este argumento, devolverá el marco de datos en la forma clásica con las variables medidas en las columnas y todas las demás variables de identificación en las filas).

  • fun.aggregate (opcional): función de agregación para usar, si es necesario

  • margins (opcional): ¿qué valores marginales se deben calcular?. Vector de nombres de variables (puede incluir «grand _col» y «grand _row») a calcular los márgenes, o TRUE para calcular todos los márgenes.

cast(largo)

library(MASS)
print(ships)
molten.ships <- melt(ships, 
                     id = c("type","year"))
print(molten.ships)

recasted.ship <- cast(molten.ships, 
                      type+year~variable,
                      sum)
print(recasted.ship)

2.15 Seleccionar fechas

fechainicio <- as.Date("2009-01-01")
fechafin <- as.Date("2009-10-31")
nuevosdatos <- df[which(df$fecha >= fechainicio &
                          df$fecha <= fechafin), ]
df[edad == -9999, "edad"] <- NA             # Recode 99 to missing for the variable age
df

2.16 Exportación e importación de dataframes

2.16.1 Exportación

  • En el caso de querer exportar objetos en el formato de R, podemos optar por la función save().
save(df2, file="fichero_exportado_R.rdata")                # Exportamos un único objeto, el dataframe df2

Pero también podríamos salvar varios objetos simultáneamente, por ejemplo

save(df1, df2, file = "primera_sesion.RData")    

Como se ha señalado en líneas precedentes, en caso de disponer de muchos objetos, es más conveniente grabar todo el espacio de trabajo con la función ’save.image()`; con esto, además, se salva el script de R con las órdenes.

save.image(file = "mi_espacio_trabajo.RData")

Podemos exportar un data frame a un fichero de texto usando la función write.table(). Como en el caso de su importación, el separador entre las columnas puede también modificarse.

write.table(NH3,                             # Dataframe a exportar
            "borrame.txt",                   # Nombre del fichero
             sep=";")                        # Separador entre columanas, en este caso punto y coma.

En el caso de que queramos exportar el fichero como *.csv podemos recurrir a dos opciones, que dependen del formato:

write.csv(df, file="fichero_exportado_csv.csv")                                 # Formato anglosajón: campos separados por comas, símbolo decimal es un punto
write.csv2(df,file="fichero_exportado_csv2.csv")                                # Formato español: campos separados punto y coma, símbolo decimal es la coma

Por último, para ficheros excel puede recurrirse a varios paquetes. Uno de ellos es el paquete xlsx.

if(!require("xlsx")) install.packages("xlsx") 
library(xlsx)
write.xlsx(df,  # Dataframe 
           "fichero_exportado_excel.xlsx",  # Nombre del fichero de salida en formato excel 
           sheetName = "Hoja1",             # Nombre del libro (hoja)
           append = TRUE)                   # Si TRUE este fichero se añade como otro libro al fichero excel -preexistente-

2.16.2 Importación

Cuando se trabaja con grandes ficheros, es habitual importarlos de una fuente externa. A través de diferentes paquetes, R es capaz de leer la mayoría de los formatos de datos existentes en la actualidad, tanto desde nuestro disco duro como desde fuentes externas. Tenemos diferentes opciones

RStudio ofrece una serie de herramientas para importar ficheros. A través de algunas ventanas, por ejemplo File, podemos importar diferentes tipos de ficheros.

Importar datos desde Environment: elección del formato

También es posible importar ficheros acudiendo a la ventana Environment.

Importar datos desde Environment: elección del formato Tanto en un caso como en otro, una vez elegido el formato de entrada, aparece una nueva ventana en la que podemos elegir una serie de opciones.

Importar datos desde Environment: elección del fichero y sus características

También es posible importar ficheros a través de códigos. La sintaxis depende del tipo de fichero:

Ficheros .rdata.

fichero_importado_rdata <- load("mi_espacio_trabajo.rdata", 
                                verbose=TRUE)    # Opción verbose=TRUE muestra el nombre de los objetos del archivo

Ficheros de texto. Para importar este tipo de ficheros, es necesario instalar el paquete readr.

if(!require("readr")) install.packages("readr") 
library(readr)

La función read.tableimporta los ficheros de texto, pero deben tomarse algunas precauciones previas. Este es el caso de los separadores entre columnas. Para informar a R de qué separador es el correcto, se debe utilizar el argumento sep con las siguientes posibilidades: - sep="," las separaciones entre columnas son comas - sep=";" idem signos de punto y coma - sep="\t" idem son tabuladores. - dec= separador decimal (ATENCIÓN, en España el decimal es una coma, en US/UK un punto)

Fichero de texto con columnas está separadas por espacios en blanco.

NH1 <- read.table("http://aprender.uib.es/Rdir/NotaHermanos.txt", 
                  header=TRUE)                        # Importante si el fichero contiene los nombres de las variables
head(NH1)
str(NH1)

Fichero de texto con Formato csv: las columnas están separadas por comas.

NH3 <- read.table("http://aprender.uib.es/Rdir/NotaHermanosc.txt", 
               header=TRUE, 
               sep=",",                     # Separador de columas
               stringsAsFactors=TRUE)       # Impide que transforme (por defecto) en factores todas la columnas con caracteres.
str(NH3)

Podemos descargar datos procedentes de páginas web con las mismas funciones. La única diferencia consiste en que, en lugar de proporcionar la ruta al fichero, tendremos que proporcionar la ruta de internet. Por ejemplo:

datos <- read_csv("https://raw.githubusercontent.com/datasciencelabs/data/master/bio260-heights.csv")
str(datos)

rm(datos)

Si los datos originales carecen de encabezado R las nombra con V1, V2, …, V6 etc. Sin embargo, podemos asignar nombre a cada variable con la función colnames() o se pueden asignar directamente cuando se importan usando el argumento col.names de la función read.table().

nombres <- c("ID", "edad", "hijos", "coches", "yates", "motos") 
web <- "http://people.cst.cmich.edu/lee1c/spss/V16_materials/DataSets_v16/Diseaseoutbreak.txt"
misdatos_web <- read.table(web,
                           col.names = nombres) 
head(misdatos_web)                                                                            # un vistazo para ver qué resulta

Ficheros excel.

library(readxl)
fichero_importado_excel <- read_excel("fichero_exportado_R.xlsx",                   # Libro que queremos abrir
                                      sheet = "Hoja1",                     # Hoja que queremos importar, bien especificando su nombre o su posición
                                      skip = 1)                            # Empezamos a importar a partir de la cuarta fila. 

str(fichero_importado_excel)  # Verificar la estructura de los datos que hemos leído mediante
head(fichero_importado_excel) # Comprobar las primeras lineas de datos

Existe la posibilidad de importar otros tipos de ficheros (SPSS, Stata), pero no son de utilidad en este curso.

3 MATRICES Y ARRAYS

Las matrices y arrays pueden ser descritas como vectores multidimensionales. Al igual que un vector, únicamente pueden contener datos de un sólo tipo, pero además tienen más dimensiones.

En general, es preferible usar listas en lugar de arrays, una estructura de datos que además tienen ciertas ventajas que veremos más adelante.

3.1 Creación de matrices

Creamos matrices en R con la función matrix(). La función matrix() acepta dos argumentos, nrow y ncol. Con ellos especificamos el número de renglones y columnas que tendrá nuestra matriz.

1:12                 # Un vector numérico del uno al doce

matrix(1:12)         # matrix() sin especificar renglones ni columnas

matrix(1:12, nrow = 3, ncol = 4)          # Tres renglones y cuatro columnas

matrix(1:12, nrow = 4, ncol = 3)          # Cuatro columnas y tres columnas

matrix(1:12, nrow = 4, ncol = 3)          # Dos renglones y seis columnas

Los datos que intentemos agrupar en una matriz serán acomodados en orden, de arriba a abajo, y de izquierda a derecha, hasta formar un rectángulo. Si multiplicamos el número de renglones por el número de columnas, obtendremos el número de celdas de la matriz. En los ejemplo anteriores, el número de celdas es igual al número de elementos que queremos acomodar, así que la operación ocurre sin problemas.

Cuando intentamos acomodar un número diferente de elementos y celdas, ocurren dos cosas diferentes.

Si el número de elementos es mayor al número de celdas, se acomodarán todos los datos que sean posibles y los demás se omitirán.

matrix(1:12, nrow = 3, ncol = 3)

Si, por el contrario, el número de celdas es mayor que el número de elementos, estos se reciclaran. En cuanto los elementos sean insuficientes para acomodarse en las celdas, R nos devolverá una advertencia y se empezaran a usar los elementos a partir del primero de ellos.

matrix(1:12, nrow = 5, ncol = 4)

Otro procedimiento para crear matrices es la unión vectores con las siguientes funciones:

  • cbind() para unir vectores, usando cada uno como una columna.
  • rbind() para unir vectores, usando cada uno como un renglón.

De este modo podemos crear cuatro vectores y unirlos para formar una matriz. Cada vector será un renglón en esta matriz. Creamos cuatro vectores, cada uno de largo igual a cuatro.

vector_1 <- 1:4
vector_2 <- 5:8
vector_3 <- 9:12
vector_4 <- 13:16

Usamos rbind() para crear un matriz, en la que cada vector será un renglón.

matriz <- rbind(vector_1, vector_2, vector_3, vector_4)

matriz                       # Resultado

Si utilizamos cbind(), entonces cada vector será una columna.

matriz <- cbind(vector_1, vector_2, vector_3, vector_4)

matriz                       # Resultado

Al igual que con matrix(), los elementos de los vectores son reciclados para formar una estructura rectangular y se nos muestra un mensaje de advertencia.

# Elementos de largo diferente
vector_1 <- 1:2
vector_2 <- 1:3
vector_3 <- 1:5

matriz <- cbind(vector_1, vector_2, vector_3)

matriz                     # Resultado

Finalmente, las matrices pueden contener NAs. Creamos dos vectores con un NA en ellos.

vector_1 <- c(NA, 1, 2)
vector_2 <- c(3,  4, NA)

Creamos una matriz con rbind().

matriz <- rbind(vector_1, vector_2)

matriz                    # Resultados

Como NA representa datos perdidos, puede estar presente en compañía de todo tipo de de datos.

3.2 Propiedades de las matrices

No obstante que las matrices y arrays son estructuras que sólo pueden contener un tipo de datos, no son atómicas. Su clase es igual a matriz (matrix) o array segun corresponda. Verificamos esto usando la función class().

mi_matriz <- matrix(1:10)

class(mi_matriz)

Las matrices y arrays pueden tener más de una dimensión. Obtenemos el número de dimensiones de una matriz o array con la función dim(). Esta función nos devolverá varios números, cada uno de ellos indica la cantidad de elementos que tiene una dimensión.

mi_matriz <- matrix(1:12, 
                    nrow = 4, 
                    ncol = 3)
dim(mi_matriz)

Cabe señalar que si usamos dim() con un vector, obtenemos NULL. Esto ocurre con todos los objetos unidimensionales

mi_vector <- 1:12

dim(mi_vector)

Finalmente, las operaciones aritméticas también son vectorizadas al aplicarlas a una matriz. La operación es aplicada a cada uno de los elementos de la matriz. Creamos una matriz.

mi_matriz <- matrix(1:9, nrow = 3, ncol = 3)

mi_matriz                  # Resultado

Intentemos sumar, multiplicar y elevar a la tercera potencia.

mi_matriz + 1                       # Suma

mi_matriz * 2                       # Multiplicación

mi_matriz ^ 3                       # Potenciación

Si intentamos vectorizar una operación utilizando una matriz con NAs, esta se aplicará para los elementos válidos, devolviendo NA cuando corresponda. Creamos una matriz con NAs.

vector_1 <- c(NA, 2, 3)
vector_2 <- c(4, 5, NA)

matriz <- rbind(vector_1, vector_2)

matriz                             # Resultado

Intentamos dividir sus elementos entre dos.

matriz / 2

Finalmente, podemos usar la función t() para transponer una matriz, es decir, rotarla 90°. Creamos una matriz con tres renglones y dos columnas.

matriz <- matrix(1:6, nrow = 3)

matriz                 # Resultado

Usamos t() para transponer y obtenemos una matriz con dos renglones y dos columnas.

matriz_t <- t(matriz)

matriz_t               # Resultado

Una matriz de orden n x m es una tabla rectangular de números (o, en algunas situaciones específicas, algún otro tipo de datos: valores lógicos, etiquetas…) formada por n filas y m columnas. Una matriz es cuadrada cuando tiene el mismo número de filas que de columnas, es decir, cuando n = m.

Desde el punto de vista de R, una matriz es una colección de valores con dos dimensiones (como un dataframe) pero todos del mismo tipo (a diferencia del dataframe). Las matrices son una estructura de datos con gran importancia en Geografía, ya que es una estructura de datos usada frecuentemente para almacenar información (formato raster).

Una matriz se crea con la función matrix, que acepta los siguientes argumentos:

- `data`  Un vector con los valores que componen la matriz
- `nrow`  Número de filas
- `ncol`  Número de columnas
- `byrow` Indica si la matriz será rellena columna a columna (FALSE, por defecto) o fila a fila (TRUE)

Ejemplos:

matrix(1:6, nrow = 2)          # Matriz del 1 al 6 organizada en dos filas
matrix(1:6, ncol = 2)          # Matriz del 1 al 6 organizada en dos columnas

matrix(1:12, nrow = 3)         # Matriz del 1 al 12 organizada en 3 filas                                 
matrix(1:12, nrow = 3, byrow = TRUE)  # Matriz el 1 al 12

m <- matrix(1:20, 
            nrow=5, 
            ncol=4)
m
class(m)

altitud = c(
  NA, NA, NA, NA, NA, NA, NA, 3, 5, 7, 
  NA, NA, NA, 61, 106, 47, 31, 39, 32, 49, 
  NA, NA, NA, 9, 132, 254, 233, 253, 199, 179, 
  NA, NA, NA, 4, 11, 146, 340, 383, 357, 307, 
  NA, 4, 6, 9, 6, 6, 163, 448, 414, 403, 
  3, 6, 9, 10, 6, 6, 13, 152, 360, 370, 
  3, 4, 7, 16, 27, 12, 64, 39, 48, 55)

dem <- matrix(altitud, 
             nrow = 10, 
             ncol = 7)
dem

En una matriz como la llamada dem

  - las filas se identifican como [i, ], donde i es el índice de la fila: dem[5, ] significa la 5ª fila.
  - Las columnas con [,j], donde j es el índice de la columna.

Las dimensiones de una matriz, es decir, sus números de filas y de columnas, se obtienen con las funciones nrow y ncol. Si queremos un vector formado por las dos dimensiones, podemos emplear la función dim.

nrow(dem)
ncol(dem)
dim(dem)

También podemos realizar numerosos cálculos con estas matrices.

sum(dem, na.rm = T)
mean(dem, na.rm = T)

3.3 Arrays

A diferencia de las matrices, confinadas a dos dimensiones, los arrays pueden tener cualquier número de dimensiones. La función array() tiene un atributo dim que crea el número requerido de dimensiones.

a <- array(c('green','yellow'),
           dim = c(3,3,2))
print(a)

dim1 <- c("A1", "A2")                   
dim2 <- c("B1", "B2", "B3")
dim3 <- c("C1", "C2", "C3", "C4")
z <- array(1:24, 
           c(2,3,4), 
           dimnames=list(dim1, dim2, dim3))

4 LISTAS

Las listas, al igual que los vectores, son estructuras de datos unidimensionales, sólo tienen largo, pero a diferencia de los vectores cada uno de sus elementos puede ser de diferente tipo o incluso de diferente clase, por lo que son estructuras heterogéneas.

Podemos tener listas que contengan datos atómicos, vectores, matrices, arrays, data frames u otras listas. Esta última característica es la razón por la que una lista puede ser considerada un vector recursivo, pues es un objeto que puede contener objetos de su misma clase.

Para crear una lista usamos la función list(), que nos pedirá los elementos que deseamos incluir en nuestra lista. Para esta estructura, no importan las dimensiones o largo de los elementos que queramos incluir en ella.

Al igual que con un data frame, tenemos la opción de poner nombre a cada elemento de una lista.

Por último, no es posible vectorizar operaciones aritméticas usando una lista, se nos devuelve un error como resultado.

mi_vector <- 1:10
mi_matriz <- matrix(1:4, nrow = 2)
mi_df     <- data.frame("num" = 1:3, "let" = c("a", "b", "c"))

mi_lista <- list("un_vector" = mi_vector, "una_matriz" = mi_matriz, "un_df" = mi_df)

mi_lista

Creamos una lista que contiene otras listas.

lista_recursiva <- list("lista1" = mi_lista, "lista2" = mi_lista)

# Resultado
lista_recursiva

4.1 Propiedades de una lista

Una lista es unidimensional, sólo tiene largo. El largo de una lista es igual al número de elementos que contiene, sin importar de qué tipo o clase sean. Usamos la lista recursiva que creamos en la sección anterior para ilustrar esto.

length(lista_recursiva)

Dado que una lista siempre tiene una sola dimensión, la función dim() nos devuelve NULL.

dim(lista_recursiva)

Las listas tienen clase list, sin importar qué elementos contienen.

class(lista_recursiva)

Finalmente, no es posible vectorizar operaciones aritméticas usando listas. Al intentarlo nos es devuelto un error.

mi_lista / 2

Si deseamos aplicar una función a cada elemento de una lista, usamos lapply()

5 COERCIÓN ENTRE ESTRUCTURAS DE DATOS

Al igual que con los datos, cuando intentamos hacer operaciones con una estructura de datos, R intenta coercionarla al tipo apropiado para poder llevarlas a cabo con éxito.

También podemos usar alguna de las funciones de la familia as() coercionar de un tipo de estructura de datos. A continuación se presentan las más comunes.

Función Coerciona a Coerciona a
as.vector() Vector Matrices
as.matrix() Matrices Vectores, Data frames
as.data.frame() Data frame Vectores, Matrices
as.list() Lista Vectores, Matrices, Data frames

Las estructuras de datos más sencillas, (unidimensionales, homogeneas) pueden ser coercionadas a otras más complejas (multidimensionales, heterogeneas), pero la operación inversa casi nunca es posible.

Veamos algunos ejemplos. Creamos un vector, una matriz, un data frame y una lista.

mi_vector <- c("a", "b", "c")
mi_matriz <- matrix(1:4, nrow = 2)
mi_df <- data.frame("a" = 1:2, "b" = c("a", "b"))
mi_lista <- list("a" = mi_vector, "b" = mi_matriz, "c" = mi_df)

Intentemos coercionar a vector con as.vector().

as.vector(mi_matriz)

as.vector(mi_df)

as.vector(mi_lista)

La coerción que intentamos sólo tuvo éxito para una matriz. Para data frame y lista, nos devolvió los mismos objetos. Nota que as.vector() no devolvió un error o una advertencia a pesar de que no tuvo éxito al coercionar, en este caso un data frame o una lista. Esto es importante, pues no puedes confiar que as.vector() tuvo éxito porque corrió sin errores, es necesaria una verificación adicional. Como R intenta coercionar automáticamente, esto puede producir resultados inesperados si no tenemos cuidado. Intentemos coercionar a matriz con as.matrix().

as.matrix(mi_vector)

as.matrix(mi_df)

as.matrix(mi_lista)

El vector fue coercionado a una matriz con una sola columna.

Por su parte, al correr la función con un data frame, coercionamos también todos los datos que contiene, siguiendo las reglas de coerción de tipos de dato que vimos en el capítulo 4.

Al coercionar una lista a una matriz, efectivamente obtenemos un objeto de este tipo, sin embargo perdemos toda la información que contiene, por lo tanto, no podemos considerar que esta es una coerción exitosa. Del mismo modo que con as.vector(), no nos es mostrado ningún error ni advertencia.

Intentemos coercionar a matriz con as.data.frame().

as.data.frame(mi_vector)

as.data.frame(mi_matriz)

as.data.frame(mi_lista)

El vector, al igual que cuando fue coercionado a matriz, devolvió como resultado un objeto con una sola columna, mientras que la matriz conservó sus renglones y columnas.

En este caso, al intentar la coerción de lista a data frame, obtenemos un error. Esta es la única situación en la que esto ocurre utilizando las funciones revisadas en esta sección.

Por último, intentemos coercionar a matriz con as.list().

as.list(mi_vector)
as.list(mi_matriz)
as.list(mi_df)

ACTIVIDAD DE EVALUACIÓN CONTINUA

Una vez concluida la explicación teórica, el alumnado deberá realizar las siguientes actividades de evaluación continua, correspondientes a este apartado

Actividad evaluación continua 1

Actividad evaluación continua 2

Actividad evaluación continua 3