Processing math: 87%

Estructura de datos

En está parte del curso estudiaremos las estructuras de datos más importantes en R. Las colecciones o conjunto de datos en R se organizan por su dimensión (1º, 2º, o varias dimensiones) y si son homogéneas (todos los objetos deben ser del mismo tipo) o heterogéneas ( el contenido puede ser de diferentes tipos). A continuación mostramos los cinco tipos de datos más usados en el análisis de datos:

Dimensión Homogénea Heterogénea
1 Vector atómico Lista
2 Matriz Data frame
n Array

Además, analizaremos la sintaxis de R para acceder a las estructuras de datos. Como veremos podemos seleccionar un único elemento o varios elementos, mediante el uso de la notación de índices que proporciona R. Asimismo aprenderemos a elegir elementos por localización dentro de una estructura o por nombre.

La siguiente tabla resume los operadores que aporta R para el acceso a objetos en estructuras de datos.

Sintaxis Objetos Descripción
x[i] Vectores, Listas Selecciona elementos del objeto x, descritos en i. i puede ser un vector de tipo integer, chararacter (de nombres de los objetos) o lógico. Cuando es usado con listas, devuelve una lista. Cuando es usado en vectores devuelve un vector.
x[[i]] Listas Devuelve un único elemento de x que se encuentra en la posición i. i puede ser un vector de tipo integer o character de longitud 1.
x$n Listas, Dataframes Devuelve un objeto con nombre n del objeto x.
[i,j] Matrices Devuelve el objeto de la fila i y columna j. i y j pueden ser un vector de tipo integer o chararacter (de nombres de los objetos)

Vectores

El tipo más básico de estructura de dato en R es el vector. El vector es una estructura compuesta de un número de elementos finitos, homogéneos y donde dicha estructura tiene un tamaño fijo. Finito porque existe un último elemento; homogéneo porque todos los elementos son del mismo tipo y tamaño fijo porque el tamaño del vector debe ser conocido en tiempo de ejecución o compilación.

Creación de Vectores

Los vectores atómicos pueden ser creados con la función c(), que corresponde a la sigla de combinar:

vector_double <- c(1, 2.5, 4.5)
# Con el sufijo L, conseguimos un integer en lugar de un double
vector_integer <- c(1L, 6L, 10L)
# Usamos TRUE y FALSE (o T y F) para crear vectores lógicos
vector_logical <- c(TRUE, FALSE, T, F)
vector_character <- c("Hola", "Mundo!")

La función vector() crea un vector de un tipo y longitud que debemos especificar en el momento de su declaración:

vector_double <- vector(mode = "double", length = 3)
vector_integer <- vector(mode = "integer", length = 3)
vector_logical <- vector(mode = "logical", length = 4)
vector_character <- vector(mode = "character", length = 2)

Otra posibilidad es hacer uso de las funciones wrapper (del inglés, envoltorio) que existen para cada tipo de datos. Las siguientes instrucciones son equivalentes a las anteriores:

vector_double <- double(3)
vector_integer <- integer(3)
vector_logical <- logical(4)
vector_character <- character(2)

Además, mediante el operador : podemos generar sucesiones de números:

1:10
##  [1]  1  2  3  4  5  6  7  8  9 10
15:11
## [1] 15 14 13 12 11
1:10 - 1
##  [1] 0 1 2 3 4 5 6 7 8 9
1:(10 - 1)
## [1] 1 2 3 4 5 6 7 8 9

También podemos usar las funciones seq() y rep():

seq(10)  # mismo efecto que 1:10
##  [1]  1  2  3  4  5  6  7  8  9 10
seq(3, 10)  # mismo efecto que 3:10
## [1]  3  4  5  6  7  8  9 10
seq(1, 10, by = 3)  #saltando de 3 en 3
## [1]  1  4  7 10
rep(1:4, 2)  #repetimos 1:4 dos veces
## [1] 1 2 3 4 1 2 3 4
rep(1:4, each = 2)  #repetimos 1:4 dos veces, intercalando resultado
## [1] 1 1 2 2 3 3 4 4

Longitud

Todos los vectores tienen dos propiedades:

  • Un tipo, que se puede determinar con la función typeof():
typeof(letters)
## [1] "character"
typeof(1:10)
## [1] "integer"
  • Una longitud, que nos dice cuantos elementos contiene el vector. Podemos conocer este valor mediante la función length():
v <- c(1, 2, 3)
length(v)
## [1] 3
length(c(TRUE, FALSE, NA))
## [1] 3

Una posible fuente de confusión es cuando trabajamos con vectores de tipo character. Con este tipo de vector, la longitud es el número de strings, no el número de caracteres en cada string. Para esto último, utilizaremos la función nchar():

alumnos <- c("Juan", "Pepe", "Maria", "Dolores")
length(alumnos)
## [1] 4
nchar(alumnos)
## [1] 4 4 5 7

Tipos Fundamentales de Vectores Atómicos

Los cuatro tipos más importantes de vectores atómicos son:

  • Logical

  • Integer

  • Double (conocidos por numeric)

  • Character

Cabe mencionar que existen los tipos complex y raw que son raramente utilizados en el análisis de datos, es por eso que no los trataremos en este texto.

Logical

Los vectores lógicos son el tipo más simple de vector atómico puesto que sólo pueden tomar tres posibles valores TRUE, FALSE y NA. Los vectores lógicos usualmente son el resultado de expresiones con los operadores lógicos y de comparación.

1:10%%3 == 0
##  [1] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE

Para mas información sobre la sintaxis de los operadores y su precedencia consultar la documentación R:

# Sintaxis de los operadores y su precedéncia
help("Syntax", "base")
# Operadores lógicos
help("Logic", "base")

Numeric

Los vectores de tipo integer y double son en R vectores de tipo numeric. En R, los números son double por defecto. Si queremos un integer, añadiremos la letra L después del número:

typeof(1)
## [1] "double"
typeof(1L)
## [1] "integer"
1.5
## [1] 1.5

Character

Los vectores de tipo character son aquellos en los que cada elemento del vector es un string (cadena de caracteres):

titulo <- "Ciencia de datos en R"
titulo
## [1] "Ciencia de datos en R"

Manipulación de Vectores Atómicos

Ahora que ya conocemos los diferentes tipos de vectores atómicos, pasamos a ver a continuación las diferentes herramientas para trabajar con ellos. En concreto veremos:

  • Cómo conocer si un objeto es un tipo específico de vector.

  • De qué modo convertir de un tipo a otro, y cuándo sucede de forma automática.

  • De qué manera dar nombre a los elementos de un vector.

  • Conocer el significado de las operaciones vectorizadas y hacer uso de las mismas.

  • Qué sucede cuando trabajamos con vectores de diferentes longitud.

  • Cómo seleccionar partes o elementos de un vector.

Funciones para Comprobar el tipo

En ocasiones queremos realizar diferentes cosas basadas en el tipo de vector. Una opción es usar la función typeof(). Otra es usar las funciones que realizan la comprobación de tipo y devuelven TRUE o FALSE, como is.character(), is.double(), is.integer(), is.logical() o, de forma más general mediante is.atomic():

vector_integer <- c(1L, 2L, 3L)
typeof(vector_integer)
## [1] "integer"
is.integer(vector_integer)
## [1] TRUE
is.atomic(vector_integer)
## [1] TRUE

otro ejemplo

vector_double <- c(1, 2.5, 4.5)
typeof(vector_double)
## [1] "double"
is.double(vector_double)
## [1] TRUE
is.atomic(vector_double)
## [1] TRUE

Es importante subrayar que la función is.numeric() comprueba si un objeto es de tipo numeric y devuelve TRUE tanto para vectores de tipo integer como de tipo double.

is.numeric(vector_integer)
## [1] TRUE
is.numeric(vector_double)
## [1] TRUE

En la siguiente tabla resumimos las funciones para comprobar el tipo de nuestros vectores:

logical integer double character list
is.logical() x
is.integer() x
is.double() x
is.numeric() x x
is.character() x
is.atomic() x x x x
is.list() x
is.vector() x x x x x

Coerción

Todos los elementos de un vector deben ser del mismo tipo, así pues cuando intentemos combinar diferentes tipos estos serán convertidos al tipo más flexible. El orden es el siguiente:

logical<integer<double<character

Por ejemplo, mezclar un character y un integer producirá un character:

v <- c("a", 1)
v
## [1] "a" "1"
typeof(v)
## [1] "character"
class(v)
## [1] "character"

Cuando un vector lógico es convertido a un integer o double, TRUE es cambiado a 1 y FALSE a 0:

v <- c(FALSE, FALSE, FALSE)
as.numeric(v)
## [1] 0 0 0

La coerción sucede normalmente de forma automática. La mayoría de funciones matemáticas (+, log, abs, etc.) convierten a los tipos double o integer, y la mayoría de operaciones lógicas (&, |, any, etc.) cambian al tipo logical. Si el cambio de un tipo en otro pierde información, R nos lo advertirá mediante un mensaje.

Nombres de los Elementos

Una gran característica de los vectores en R es que podemos asignar a cada elemento un nombre. Etiquetar los elementos hace nuestro código mas legible. Podemos especificar los nombres cuando creamos un vector con la forma nombre = valor:

c(manzana = 1, platano = 2, mango = 3)
## manzana platano   mango 
##       1       2       3

Podemos añadir nombres a los elementos de un vector después de su creación con la ayuda de la función names():

frutas <- 1:4
names(frutas) <- c("manzana", "platano", "mango")
frutas
## manzana platano   mango    <NA> 
##       1       2       3       4

Gracias a la función names() podemos conocer los nombres de un vector:

names(frutas)
## [1] "manzana" "platano" "mango"   NA

Por último, si un vector no tiene nombres, la función names() devuelve NULL:

names(1:4)
## NULL

Operaciones Vectorizadas

La mayoría de las operaciones en R son vectorizadas, esto es que un operador o una función actúa en cada elemento de un vector sin la necesidad de que tengamos que escribir una construcción iterativa. Esta característica nos permite escribir un código más eficiente, conciso y mas legible que en otros lenguajes de programación.

El ejemplo mas simple es cuando sumamos dos vectores:

v1 <- 1:4
v2 <- 5:8
v3 <- v1 + v2
v3
## [1]  6  8 10 12

Cuando usamos dos o mas vectores en una operación, R alineará los vectores y llevará a cabo una secuencia de operaciones individuales. En el ejemplo anterior, cuando ejecutamos la instrucción v1 + v2, R pondrá cada vector en una fila y sumará el primer elemento de v1con el primer elemento de v2. A continuación, sumará el segundo elemento del vector 1 con el segundo elemento del vector 2, y así sucesivamente, hasta que todos los elementos se han sumado. El resultado será un vector de la misma longitud que los anteriores, como muestra la siguiente figura

Operaciones vectorizadas

Sin la vectorización tendríamos que realizar la suma mediante el uso de una estructura iterativa, como por ejemplo:

v3 <- numeric(length(v1))
for (i in seq_along(v1)) {
    v3[i] <- v1[i] + v2[i]
}
v3
## [1]  6  8 10 12

Otro tipo de operaciones que podemos realizar de forma vectorizada son las comparaciones lógicas. Supongamos que queremos saber que elementos en un vector son mas grandes que 2. Podríamos hacer lo siguiente:

v1 <- 1:4
v1 > 2
## [1] FALSE FALSE  TRUE  TRUE

A continuación, mostramos otros ejemplos de operaciones vectorizadas de tipo lógico:

v1 <- 1:4
v1 >= 2
## [1] FALSE  TRUE  TRUE  TRUE
v2 < 3
## [1] FALSE FALSE FALSE FALSE
v3 == 8
## [1] FALSE  TRUE FALSE FALSE

Desde luego, la resta, multiplicación y división son también operaciones vectorizadas:

v1 - v2
## [1] -4 -4 -4 -4
v1 * v2
## [1]  5 12 21 32
v1/v2
## [1] 0.2000000 0.3333333 0.4285714 0.5000000

Reciclado de Vectores y Repetición

En los ejemplos anteriores hemos realizado operaciones aritméticas con vectores de la misma longitud. No obstante podríamos estar preguntándonos, “¿Qué sucede si intentamos realizar operaciones en vectores de diferente longitud?”.

Si intentamos realizar la suma de un único número (escalar) a un vector, entonces el número es sumado a cada elemento en el vector, como muestra la siguiente figura:

Vectorización con un escalar

Cuando sumamos dos vectores de diferente longitud, R reciclará los elementos del vector más pequeño para que coincida con el más grande, como podemos ver en la siguiente figura:

Vectorización vectores diferente longitud

Si la longitud del vector más grande no es múltiple con la longitud del vector más pequeño, R nos lo hará saber mediante un mensaje:

1:5 + 1:7
## Warning in 1:5 + 1:7: longer object length is not a multiple of shorter object
## length
## [1]  2  4  6  8 10  7  9

Aunque R nos permita realizar operaciones con vectores de diferente longitud, esto no significa que nosotros deberíamos hacerlo. Realizar una suma de un valor escalar a un vector es coherente, pero realizar operaciones con vectores de diferente longitud puede llevarnos a errores. Es por eso, que recomendamos crear explícitamente vectores de la misma longitud antes de operar con ellos.

La función rep() es muy útil para esta tarea, permitiéndonos crear un vector con elementos repetidos:

rep(1:5, 3)
##  [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
rep(1:5, each = 3)
##  [1] 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
rep(1:5, times = 1:5)
##  [1] 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
rep(1:5, length.out = 7)
## [1] 1 2 3 4 5 1 2
rep_len(1:5, 7)
## [1] 1 2 3 4 5 1 2

Selección de Elementos

En ocasiones queremos acceder a una única parte de un vector, o quizá a un único elemento. Esto es conocido como indexing (del inglés, indexación) y se realiza mediante el uso de los corchetes []. Existen cuatro maneras diferentes de elegir una parte de un vector:

  • Mediante un vector numérico de tipo integer. Los integers deben ser todos positivos, todos negativos, o cero.

Seleccionar los elementos con integers positivos extrae los elementos de las posiciones indicadas:

v <- c("uno", "dos", "tres", "cuatro", "cinco")
v[c(3, 2, 5)]
## [1] "tres"  "dos"   "cinco"

Repitiendo una posición, podemos obtener un vector de una longitud más grande que el vector original:

v[c(1, 1, 5, 5, 5, 2)]
## [1] "uno"   "uno"   "cinco" "cinco" "cinco" "dos"

Los valores negativos eliminan los elementos en las posiciones especificadas:

v[c(-1, -3, -5)]
## [1] "dos"    "cuatro"

No podemos mezclar valores positivos y negativos:

v[c(1, -1)]
  • Por medio de un vector lógico obtenemos todos los valores correspondientes al valor TRUE. Este tipo es útil en conjunción con la funciones de comparación:
v <- c(10, 3, NA, 5, 8, 1, NA)
# Devuelve todos los valores que no son NA en x
v[!is.na(v)]
## [1] 10  3  5  8  1
# Todos los valores pares (o desconocidos) en x
v[v%%2 == 0]
## [1] 10 NA  8 NA
  • Si hemos dado nombres a los elementos de nuestro vector, podemos seleccionar sus elementos con un vector de tipo character:
frutas <- c(manzana = 1, platano = 2, kiwi = 3, pera = 4, naranja = 5)
frutas[c("platano", "naranja")]
## platano naranja 
##       2       5
  • Mediante v[], obtendremos el vector completo:
v <- c(10, 3, NA, 5, 8, 1, NA)
v[]
## [1] 10  3 NA  5  8  1 NA

Esta notación no es muy útil para acceder a vectores, sin embargo nos será de gran ayuda en el acceso a matrices (y cualquier tipo de estructura multidimensional) puesto que nos permite seleccionar todas las filas o columnas. Por ejemplo, si x es 2D, v[1,] selecciona la primera fila y todas las columnas, y v[,1] recupera todas las filas y columnas excepto la primera.

Resumen

  • Los vectores tienen una longitud que podemos conocer o definir mediante la función length().

  • La función seq() y sus variantes nos permite crear sucesiones de números.

  • Podemos dar nombre a los elementos de un vector en el momento de su creación o una vez creado mediante la función names().

  • Podemos acceder a los elementos de un vector mediante los [] y un índice. La función rep() nos permite crear vectores con elementos repetidos.

Matrices

Una matriz es una extensión de un vector a dos dimensiones, lo que implica que dispone del atributo dimension. El atributo dimensión es en si mismo un vector de longitud 2 (numero de filas, numero de columnas). Una matriz se utiliza para representar datos de un único tipo en dos dimensiones.

Creación de Matrices

Para crear matrices utilizaremos la función matrix(), la sintaxis es la siguiente

 str(matrix)
## function (data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)

A continuación mostramos la descripción de los argumentos:

  • data es el vector que contiene los elementos que formaran parte de la matriz.

  • nrow es el número de filas.

  • ncol es el número de columnas.

  • byrow es un valor lógico. Si es TRUE el vector que pasamos será ordenado por filas.

  • dimnames nombres asignado a filas y columnas.

Seguidamente se muestra un ejemplo de creación de una matriz:

matriz <- matrix(1:12, nrow = 4)
matriz
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

A partir de un vector, si le añadimos el atributo dimensión podemos obtener una matriz:

m <- 1:12
 m
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12
 dim(m) <- c(4, 3)
m
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

Cuando creamos una matriz, los valores que pasamos son ordenados por columnas. Pero también es posible llenar la matriz por filas especificando el argumento byrow = TRUE:

matriz <- matrix(1:12, nrow = 4, byrow = TRUE)
matriz
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
## [3,]    7    8    9
## [4,]   10   11   12

En el siguiente ejemplo hacemos uso del argumento dimnames para dar nombre a las filas y columnas:

automoviles <- matrix(
                 1:12,
                 nrow = 4,
                 byrow = TRUE,
                 dimnames = list(
                   c("Blanco", "Rojo", "Negro", "Gris"),
                   c("Toyota", "Audi", "Nissan")) )
automoviles
##        Toyota Audi Nissan
## Blanco      1    2      3
## Rojo        4    5      6
## Negro       7    8      9
## Gris       10   11     12

Mediante las funciones cbind() y rbind() es posible crear matrices por columnas o por filas a partir de dos vectores de la misma longitud:

v1 <- c(1, 2, 3)
v2 <- c(4, 5, 6)
m1 <- cbind(v1, v2)
m1
##      v1 v2
## [1,]  1  4
## [2,]  2  5
## [3,]  3  6
v1 <- c(1, 2, 3)
v2 <- c(4, 5, 6)
m1 <- rbind(v1, v2)
m1
##    [,1] [,2] [,3]
## v1    1    2    3
## v2    4    5    6

Filas, Columnas y Dimensión

La función dim() devuelve un vector de integers con la dimensión del objeto:

dim(automoviles)
## [1] 4 3

Además con las funciones nrow() y ncol() podemos conocer el número de filas y columnas, respectivamente:

nrow(automoviles)
## [1] 4
ncol(automoviles)
## [1] 3

La función length() que hemos visto con anterioridad en los vectores, también funciona en matrices. Cuando trabajamos con matrices; no obstante, devuelve el producto de cada una de las dimensiones:

length(automoviles)
## [1] 12

Nombres de las Filas, Columnas y Dimensiones

Del mismo modo que los vectores poseen el atributo names para sus elementos, las matrices disponen de rownames y colnames para las filas y columnas.

colores <- rownames(automoviles)
colores
## [1] "Blanco" "Rojo"   "Negro"  "Gris"
marcas <- colnames(automoviles)
marcas
## [1] "Toyota" "Audi"   "Nissan"

Por medio de la función dimnames() obtendremos una lista que contiene dos vectores con los atributos rownames y colnames:

dimnames(automoviles)
## [[1]]
## [1] "Blanco" "Rojo"   "Negro"  "Gris"  
## 
## [[2]]
## [1] "Toyota" "Audi"   "Nissan"

Operaciones con Matrices

La función diag() extrae la diagonal principal de una matriz:

A <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, ncol = 3, byrow = TRUE)
 A
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
## [3,]    7    8    9
diag(A)
## [1] 1 5 9

Además, diag() nos permite crear matrices diagonales:

diag(c(1, 2, 3, 4))
##      [,1] [,2] [,3] [,4]
## [1,]    1    0    0    0
## [2,]    0    2    0    0
## [3,]    0    0    3    0
## [4,]    0    0    0    4

La matriz identidad es muy fácil de crear en R. Por ejemplo, la matriz identidad de dimensión 4 es:

Id4 = diag(1, nrow = 4)
Id4
##      [,1] [,2] [,3] [,4]
## [1,]    1    0    0    0
## [2,]    0    1    0    0
## [3,]    0    0    1    0
## [4,]    0    0    0    1

Hay que tener cierto cuidado con los operadores aritméticos básicos (+,,). Si se suman una matriz y una constante, el efecto es que dicha constante se suma a todos los elementos de la matriz. Lo mismo ocurre con la diferencia, la multiplicación y la división:

M = matrix(nrow=2,c(1,2,3, 4),byrow = FALSE)
M
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
M + 2
##      [,1] [,2]
## [1,]    3    5
## [2,]    4    6

Asimismo, si a una matriz se le suma un vector cuya longitud sea igual al número de filas de la matriz, se obtiene como resultado una nueva matriz cuyas columnas son la suma de las columnas de la matriz original más dicho vector.

v = c(3,4)
M + v
##      [,1] [,2]
## [1,]    4    6
## [2,]    6    8

La suma o resta de matrices de la misma dimensión se realiza con los operadores + y -; el producto de matrices (siempre que sean compatibles) se realiza con el operador %*%:

M + M
##      [,1] [,2]
## [1,]    2    6
## [2,]    4    8
M - M
##      [,1] [,2]
## [1,]    0    0
## [2,]    0    0
M%*%M
##      [,1] [,2]
## [1,]    7   15
## [2,]   10   22

Una fuente de posibles errores en el cálculo matricial, cuando se utilizan matrices de la misma dimensión, es utilizar los operadores * y / ya que multiplican (o dividen) las matrices término a término:

M * M
##      [,1] [,2]
## [1,]    1    9
## [2,]    4   16
M / M
##      [,1] [,2]
## [1,]    1    1
## [2,]    1    1

La traspuesta de una matriz se calcula simplemente con la función t():

M
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
t(M)
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4

El determinante de una matriz cuadrada se calcula mediante la función det():

det(M)
## [1] -2

La función solve() permite obtener la inversa de una matriz cuando sólo se le pasa un argumento:

solve(M)
##      [,1] [,2]
## [1,]   -2  1.5
## [2,]    1 -0.5

Además, con la función solve()podemos resolver sistemas de ecuaciones lineales. Por ejemplo, si disponemos del siguiente sistema de ecuaciones:

Ax=b

3x+2y=5xy=0

que en forma matricial puede expresarse como

(3211)(xy)=(50)

Podemos resolver el sistema de ecuaciones en R, del siguiente modo:

A <- matrix(c(3, 2, 1, -1), ncol = 2, byrow = TRUE)
b <- c(5, 0)
solve(A, b)
## [1] 1 1

Selección de Elementos

Los elementos de una matriz están indexados con dos índices lo cual hace que resulte fácil acceder a los elementos y trabajar con ellos si lo que nos interesa es sólo una parte de la información contenida en una matriz y no la matriz entera, esto se logra con el operador de indexación [i,j] donde i es el elemento fila y j es el elemento columna.

Siguiendo con el ejemplo anterior, si quisiéramos seleccionar el número de automóviles blancos correspondiente a la marca Audi podríamos hacerlo de dos maneras:

  • Escribiendo el nombre de la matriz y entre corchetes los nombres de la fila y columnas entre comillas:
automoviles["Blanco", "Audi"]
## [1] 2
  • Alternativamente, podemos utilizar la notación de índices:
automoviles[1, 2]
## [1] 2

También podemos seleccionar columnas y filas enteras, de manera que si queremos seleccionar todos los automóviles blancos lo haríamos del siguiente modo:

automoviles[1,]
## Toyota   Audi Nissan 
##      1      2      3
# otra forma de hacerlo es
automoviles["Blanco",]
## Toyota   Audi Nissan 
##      1      2      3

Agregar Filas y Columnas

Podemos emplear las funciones cbind() y rbind() para agregar filas y columnas a una matriz que hemos creado con anterioridad:

# Añadimos una nueva fila a la matriz
verde <- c(8, 5, 7)
automoviles <- rbind(automoviles, verde)
automoviles
##        Toyota Audi Nissan
## Blanco      1    2      3
## Rojo        4    5      6
## Negro       7    8      9
## Gris       10   11     12
## verde       8    5      7
# Añadimos una nueva columna
ford <- c(2, 7, 3, 5, 9)
automoviles <- cbind(automoviles, ford)
automoviles
##        Toyota Audi Nissan ford
## Blanco      1    2      3    2
## Rojo        4    5      6    7
## Negro       7    8      9    3
## Gris       10   11     12    5
## verde       8    5      7    9

Eliminar Filas y Columnas

Para eliminar filas utilizaremos la notación [i,], de forma similar para eliminar columnas utilizaremos la notación [,j]. A modo de ejemplo, vamos a eliminar la fila y columna que hemos añadido en el apartado anterior:

 #Eliminando la fila verde
automoviles[-5, ]
##        Toyota Audi Nissan ford
## Blanco      1    2      3    2
## Rojo        4    5      6    7
## Negro       7    8      9    3
## Gris       10   11     12    5
# Eliminando columna ford
automoviles[, -4]
##        Toyota Audi Nissan
## Blanco      1    2      3
## Rojo        4    5      6
## Negro       7    8      9
## Gris       10   11     12
## verde       8    5      7

Resumen

  • La función dim() devuelve un vector de integers con la dimensión del objeto.

  • Además con las funciones nrow() y ncol() podemos conocer el número de filas y columnas.

  • Por medio de la función dimnames() obtendremos una lista que contiene dos vectores con los atributos rownames y colnames.

  • Podemos seleccionar elementos de un vector usando la notación [i,j].

  • Con la ayuda de las funciones cbind() y rbind() podemos agregar filas y columnas.
  • Para eliminar filas utilizaremos la notación [i,].

  • De forma similar para eliminar columnas utilizaremos la notación [,j].

Arrays

Un array es una extensión de un vector a más de dos dimensiones. Los arrays se emplean para representar datos multidimensinales de un único tipo. Los arrays son raramente utilizados en el análisis de datos, por este motivo no profundizaremos en su estudio en este texto.

Creación de Arrays

Para crear un array utilizaremos la función array(), a la que pasaremos un vector atómico con los valores y un vector de dimensiones. Opcionalmente, podemos proporcionar nombres para cada dimensión:

array_3_D <- array(1:24, dim = c(4, 3, 2), dimnames = list(c("uno", "dos", "tres", 
    "cuatro"), c("five", "six", "seven"), c("un", "deux")))
array_3_D
## , , un
## 
##        five six seven
## uno       1   5     9
## dos       2   6    10
## tres      3   7    11
## cuatro    4   8    12
## 
## , , deux
## 
##        five six seven
## uno      13  17    21
## dos      14  18    22
## tres     15  19    23
## cuatro   16  20    24

Podemos comprobar si un objeto es un array mediante la función is.array):

is.array(array_3_D)
## [1] TRUE

Finalmente, podemos conocer su dimensión con la ayuda de la función dim():

dim(array_3_D)
## [1] 4 3 2

Listas

Podemos entender una lista como un contenedor de objetos que pueden ser de cualquier clase: números, vectores, matrices, funciones, data.frames, incluso otras listas. Una lista puede contener a la vez varios de estos objetos, que pueden ser además de distintas dimensiones.

Creación de listas

Podemos crear listas con la función list(), que acepta un número arbitrario de argumentos. Los elementos de la lista pueden ser cualquier tipo de objeto:

lista <- list(1:3, "Ruben", pi, list(c(-1, -2), -5))
lista
## [[1]]
## [1] 1 2 3
## 
## [[2]]
## [1] "Ruben"
## 
## [[3]]
## [1] 3.141593
## 
## [[4]]
## [[4]][[1]]
## [1] -1 -2
## 
## [[4]][[2]]
## [1] -5

Como con los vectores, podemos dar nombre a los elementos en su construcción, o posteriormente con la ayuda de la función names():

names(lista) <- c("a", "b", "c", "d")
lista
## $a
## [1] 1 2 3
## 
## $b
## [1] "Ruben"
## 
## $c
## [1] 3.141593
## 
## $d
## $d[[1]]
## [1] -1 -2
## 
## $d[[2]]
## [1] -5
la_misma_lista <- list(a = 1:3, b = "Ruben", c = pi, d = list(c(-1, -2), -5))
la_misma_lista
## $a
## [1] 1 2 3
## 
## $b
## [1] "Ruben"
## 
## $c
## [1] 3.141593
## 
## $d
## $d[[1]]
## [1] -1 -2
## 
## $d[[2]]
## [1] -5

Un herramienta muy útil para el trabajo con listas es la función str() que nos muestra su estructura:

str(lista)
## List of 4
##  $ a: int [1:3] 1 2 3
##  $ b: chr "Ruben"
##  $ c: num 3.14
##  $ d:List of 2
##   ..$ : num [1:2] -1 -2
##   ..$ : num -5

Selección de Elementos

Disponemos de tres métodos para seleccionar elementos de una lista, que examinaremos a partir de lista:

lista <- list(a = 1:3, b = "Ruben", c = pi, d = list(c(-1, -2), -5))
lista
## $a
## [1] 1 2 3
## 
## $b
## [1] "Ruben"
## 
## $c
## [1] 3.141593
## 
## $d
## $d[[1]]
## [1] -1 -2
## 
## $d[[2]]
## [1] -5
  • La notación [] extrae una sublista. El resultado será siempre una lista:
str(lista[1:2])
## List of 2
##  $ a: int [1:3] 1 2 3
##  $ b: chr "Ruben"
str(lista[4])
## List of 1
##  $ d:List of 2
##   ..$ : num [1:2] -1 -2
##   ..$ : num -5

Como con los vectores, podemos seleccionar elementos con un vector de tipo logical, integer o character.

  • La notación [[]] extrae un único componente de la lista. Esto es, elimina un nivel en la jerarquía de la lista:
str(lista[[1]])
##  int [1:3] 1 2 3
str(lista[[4]])
## List of 2
##  $ : num [1:2] -1 -2
##  $ : num -5
str(lista[[4]][1])
## List of 1
##  $ : num [1:2] -1 -2
str(lista[[4]][[1]])
##  num [1:2] -1 -2
  • El operador $ extrae elementos de una lista por medio de su nombre. El funcionamiento es el mismo que con el operador [[]] excepto que no tenemos que utilizar comillas (""):
str(lista$a)
##  int [1:3] 1 2 3
str(lista[["a"]])
##  int [1:3] 1 2 3

La distinción entre [] y [[]] es importante en las listas, puesto que [[]] navega jerárquicamente por la lista, mientras que [] devuelve una sublista. A continuación mostramos una representación visual de las operaciones realizadas en el código anterior:

Selección elementos de una lista, visualmente

Resumen

  • Creamos listas con la función list().

  • Con la ayuda de la función names() podemos consultar o cambiar los nombres de los elementos.

  • Una función útil para el trabajo con listas es str() que nos muestra su estructura.

  • Hemos analizado los diferentes operadores para seleccionar elementos de una lista.

Data frames

Los data frames se utilizan en R para almacenar datos en forma de hoja de datos. Cada fila de la hoja de datos corresponde a una observación o valor de una instancia, mientras que cada columna corresponde a un vector que contiene los datos de una variable.

Como podemos observar en el ejemplo, cada observación, indicado en la primera columna con un número, tiene ciertas características que son representadas en las tres columnas restantes. Cada columna consiste de valores del mismo tipo, puesto que se corresponden a vectores: es por eso, que la columna breaks contiene valores de tipo double, mientras que las columnas wooly tension contienen caracteres que son almacenados como factor.

head(warpbreaks)
ABCDEFGHIJ0123456789
 
 
breaks
<dbl>
wool
<fctr>
tension
<fctr>
126AL
230AL
354AL
425AL
570AL
652AL

La estructura de un data.frame es muy similar a la de una matriz. La diferencia es que las filas de un data.frame pueden contener valores de diferentes tipos de datos.

Los data.frame también tienen similitud con las listas, puesto que son básicamente colecciones de elementos. Sin embargo, el data.frame es una lista que únicamente contiene vectores de la misma longitud. Por lo tanto, podemos considerarlo un tipo especial de lista y en el podemos acceder a sus elementos del mismo modo que lo hacemos en las matrices o las listas.

Creación de un Data Frame

Podemos crear data frames con la función data.frame():

# Creamos vectores con los valores
nombre <- c("Juan", "Margarita", "Ruben", "Daniel")
apellido <- c("Sanchez", "Garcia", "Sancho", "Alfara")
fecha_nacimiento <- c("1976-06-14", "1974-05-07", "1958-12-25", "1983-09-19")
sexo <- c("HOMBRE", "MUJER", "HOMBRE", "HOMBRE")
nro_hijos <- c(1, 2, 3, 4)
# Creamos un dataframe con la ayuda de data.frame()
censo <- data.frame(nombre, apellido, fecha_nacimiento, sexo, nro_hijos)
censo
ABCDEFGHIJ0123456789
nombre
<chr>
apellido
<chr>
fecha_nacimiento
<chr>
sexo
<chr>
nro_hijos
<dbl>
JuanSanchez1976-06-14HOMBRE1
MargaritaGarcia1974-05-07MUJER2
RubenSancho1958-12-25HOMBRE3
DanielAlfara1983-09-19HOMBRE4

Recordemos que los data frames requieren que las variables sean de la misma longitud. Por este motivo, tenemos que asegurarnos que el número de argumentos pasados a la función c() sea el mismo. Además, debemos asegurarnos que las cadenas de caracteres están entre "".

Además, observemos que cuando hacemos uso de la función data.frame(), las variables de tipo character son importadas como variables categóricas, que en R son representadas como factor.

Si estamos interesados en inspeccionar las primeras o últimas líneas, podemos hacer uso de las funciones head() y tail(), respectivamente:

head(censo)
ABCDEFGHIJ0123456789
 
 
nombre
<chr>
apellido
<chr>
fecha_nacimiento
<chr>
sexo
<chr>
nro_hijos
<dbl>
1JuanSanchez1976-06-14HOMBRE1
2MargaritaGarcia1974-05-07MUJER2
3RubenSancho1958-12-25HOMBRE3
4DanielAlfara1983-09-19HOMBRE4

Por otro lado, podemos usar la función str() para conocer la estructura del data frame:

str(censo)
## 'data.frame':    4 obs. of  5 variables:
##  $ nombre          : chr  "Juan" "Margarita" "Ruben" "Daniel"
##  $ apellido        : chr  "Sanchez" "Garcia" "Sancho" "Alfara"
##  $ fecha_nacimiento: chr  "1976-06-14" "1974-05-07" "1958-12-25" "1983-09-19"
##  $ sexo            : chr  "HOMBRE" "MUJER" "HOMBRE" "HOMBRE"
##  $ nro_hijos       : num  1 2 3 4

Como podemos observar, las variables nombre, apellido, fecha_nacimiento y sexo de censo son tratadas como factors. Para evitar este comportamiento podemos hacer lo siguiente:

  • Para las variables nombre y apellido podemos hacer uso de la función I() en la definición de los vectores para que las variables no sean tratadas como factor. Esta función inhibe la interpretación de sus argumentos.

  • La variable sexo podemos dejarla como factor, puesto que se trata de una cantidad limitada de posibles valores.

  • Para que la variable fecha_nacimiento sea registrada como fecha, podemos hacer uso de la función as.Date():

# Creamos vectores con los valores y el tipo de dato deseado
nombre <- I(c("Juan", "Margarita", "Ruben", "Daniel"))
apellido <- I(c("Sanchez", "Garcia", "Sancho", "Alfara"))
fecha_nacimiento <- as.Date(c("1976-06-14", "1974-05-07", "1958-12-25", "1983-09-19"))
sexo <- c("HOMBRE", "MUJER", "HOMBRE", "HOMBRE")
nro_hijos <- c(1, 2, 3, 4)
# Creamos un dataframe con la ayuda de data.frame()
censo <- data.frame(nombre, apellido, fecha_nacimiento, sexo, nro_hijos)
str(censo)
## 'data.frame':    4 obs. of  5 variables:
##  $ nombre          : 'AsIs' chr  "Juan" "Margarita" "Ruben" "Daniel"
##  $ apellido        : 'AsIs' chr  "Sanchez" "Garcia" "Sancho" "Alfara"
##  $ fecha_nacimiento: Date, format: "1976-06-14" "1974-05-07" ...
##  $ sexo            : chr  "HOMBRE" "MUJER" "HOMBRE" "HOMBRE"
##  $ nro_hijos       : num  1 2 3 4

Filas, Columnas y Dimensión

Como las matrices, el data.frame dispone del atributo dimensión determinado por el número de filas y columnas. Para comprobar el número de filas y columnas de censo, podemos hacer uso de la función dim():

# Devuelve el número de filas y columnas
dim(censo)
## [1] 4 5
dim(censo)[1]
## [1] 4
dim(censo)[2]
## [1] 5

También podemos recuperar el número de filas y columnas en censo con la ayuda de las funciones nrow() y ncol():

# Usamos `nrow()`para recuperar el número de filas
nrow(censo)
## [1] 4
ncol(censo)
## [1] 5
## [1] 5
# Usamos `length()`para recuperar el número de columnas
length(censo)
## [1] 5

Observemos que, puesto que la estructura de un data.frame es similar a una lista, podemos hacer uso de la función length() para recuperar el número de columnas. No obstante, para que nuestro código sea comprensible, recomendamos evitar el uso de esta función y en su lugar utilizar la función ncol().

Nombre de las Filas y Columnas

Los data frames también cuentan con el atributo names, que nos indica los nombres de las variables que hemos definido. En otras palabras, podemos ver y establecer una cabecera.

# Listamos los nombres de las variables (cabecera)
names(censo)
## [1] "nombre"           "apellido"         "fecha_nacimiento" "sexo"            
## [5] "nro_hijos"

Para cambiar los nombres, podemos hacer uso de la función names():

# Asignamos diferentes nombres a las columas de `censo`
names(censo) <- c("Nombre", "Apellido", "Fecha_Nacimiento", "Sexo", "Numero_Hijos")
names(censo)
## [1] "Nombre"           "Apellido"         "Fecha_Nacimiento" "Sexo"            
## [5] "Numero_Hijos"

También podemos cambiar los nombres de las filas y columnas con la ayuda de las funciones rownames() y colnames(), respectivamente:

# Asignamos diferentes nombres a las columnas de `censo`
colnames(censo) <- c("Nombre", "Apellido", "Fecha_Nacimiento", "Sexo", "Numero_Hijos")
rownames(censo) <- c("ID1", "ID2", "ID3", "ID4")
censo
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID1JuanSanchez1976-06-14HOMBRE1
ID2MargaritaGarcia1974-05-07MUJER2
ID3RubenSancho1958-12-25HOMBRE3
ID4DanielAlfara1983-09-19HOMBRE4

Observemos que, la función names() devuelve el mismo valor que colnames(). Para que nuestro código sea inteligible, recomendamos el uso de la función colnames().

Selección de Elementos

El acceso a los elementos que se encuentran en un data.frame es muy similar al acceso a los datos de una matriz que ya vimos en la sección anterior. Así por ejemplo, si queremos ver sólo los datos de los sujetos (filas) 2 a 4, escribiríamos:

censo[2:4, ]
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID2MargaritaGarcia1974-05-07MUJER2
ID3RubenSancho1958-12-25HOMBRE3
ID4DanielAlfara1983-09-19HOMBRE4

Si queremos acceder a la variable nombre(primera columna), podemos tratar a censo igual que si fuese una matriz:

censo[, 1]
## [1] "Juan"      "Margarita" "Ruben"     "Daniel"

Aunque también podemos referirnos a la columna por su nombre:

censo$Nombre
## [1] "Juan"      "Margarita" "Ruben"     "Daniel"

Nótese que en este caso hemos de utilizar el nombre del data.frame censo seguido del operador $ y del nombre de la variable que nos interesa (Nombre). De manera equivalente, la selección de esa variable puede realizarse mediante:

censo[, "Nombre"]
## [1] "Juan"      "Margarita" "Ruben"     "Daniel"

o poniendo el nombre de la variable entre dobles corchetes y entre comillas:

censo[["Nombre"]]
## [1] "Juan"      "Margarita" "Ruben"     "Daniel"

Attach y Detach

La notación $ para el acceso a elementos de un data.frame puede hacerse engorroso cuando hemos de escribir constantemente el nombre del data.frame(en particular si éste en muy largo). Imaginemos, por ejemplo, que para el conjunto de datos censo deseamos construir tablas de frecuencias de la variable nro_hijos, y que además queremos calcular la media del número de hijos. La sintaxis a utilizar sería la siguiente:

# Calculamos la tabla de frecuencias
table(censo$Sexo)
## 
## HOMBRE  MUJER 
##      3      1
# Diagrama de barras variable `sexo`
barplot(table(censo$Sexo))

# Calculamos la media de `nro_hijos`
mean(censo$Numero_Hijos)
## [1] 2.5
# Calculamos la mediana de `nro_hijos`
median(censo$Numero_Hijos)
## [1] 2.5
# Calculamos la varianza de `nro_hijos`
var(censo$Numero_Hijos)
## [1] 1.666667

Obviamente, escribir tantas veces censo resulta tedioso, al margen de que se multiplica el riesgo de cometer errores en la redacción de los comandos. Para evitar este problema podemos hacer uso de la función attach(), cuyo objetivo consiste básicamente en “enganchar” el contenido del dataframe al entorno donde R busca los nombres de variable; de esta forma se puede acceder directamente a las variables del dataframe por su nombre:

# Attach el dataframe `censo
attach(censo)
# Calculamos distribución frecuencias absolutas
cbind(table(sexo))
##        [,1]
## HOMBRE    3
## MUJER     1
# Diagrama de barras de `nro_hijos`
barplot(table(sexo))

# Calculamos la media de `nro_hijos`
mean(nro_hijos)
## [1] 2.5
# Calculamos la mediana de `nro_hijos`
median(nro_hijos)
## [1] 2.5
# Calculamos la varianza de `nro_hijos`
var(nro_hijos)
## [1] 1.666667
# Detach el dataframe `censo`
detach(censo)

Una vez que hayamos acabado nuestra tarea “desenganchamos” el dataframe con la función detach().

Eliminar Columnas y Filas

Si deseamos eliminar valores o columnas enteras asignaremos el valor NULL a la unidad deseada:

# Creamos una copia de `censo`
copia_censo <- censo
copia_censo 
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID1JuanSanchez1976-06-14HOMBRE1
ID2MargaritaGarcia1974-05-07MUJER2
ID3RubenSancho1958-12-25HOMBRE3
ID4DanielAlfara1983-09-19HOMBRE4
# Asignamos el valor `NULL` al valor en [1, 3]
copia_censo[1, 3] <- NULL
copia_censo 
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID1JuanSanchez1976-06-14HOMBRE1
ID2MargaritaGarcia1974-05-07MUJER2
ID3RubenSancho1958-12-25HOMBRE3
ID4DanielAlfara1983-09-19HOMBRE4
# Asignamos `NULL`a la variable `nro_hijos`
copia_censo$nro_hijos <- NULL
copia_censo 
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID1JuanSanchez1976-06-14HOMBRE1
ID2MargaritaGarcia1974-05-07MUJER2
ID3RubenSancho1958-12-25HOMBRE3
ID4DanielAlfara1983-09-19HOMBRE4
# Mostramos por pantalla valor en [1, 3]

valor_eliminado <- copia_censo$Numero_Hijos
valor_eliminado
## [1] 1 2 3 4
# Mostramos por pantalla variables `copia_censo`
names(copia_censo)
## [1] "Nombre"           "Apellido"         "Fecha_Nacimiento" "Sexo"            
## [5] "Numero_Hijos"

Para eliminar filas definiremos un vector de tipo lógico en el que indicaremos para cada fila si será eliminada o no:

# Definimos las filas a conservar
filas_a_conservar <- c(TRUE, FALSE, TRUE, FALSE)
# Obtenenos un subconjunto de `censo` con las filas a conservar
subconjunto_censo <- censo[filas_a_conservar, ]
# Mostramos por pantalla `subconjunto_censo`
subconjunto_censo
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID1JuanSanchez1976-06-14HOMBRE1
ID3RubenSancho1958-12-25HOMBRE3

Otro modo de eliminar las filas es haciendo lo contrario añadiendo el operador lógico de negación (!), a modo de ejemplo utilizaremos el código anterior:

# Definimos las filas a conservar
filas_a_conservar <- c(TRUE, FALSE, TRUE, FALSE)

# Obtenenos un subconjunto de `censo` con las filas eliminadas
subconjunto_censo <- censo[!filas_a_conservar, ]

# Mostramos por pantalla `subconjunto_censo`
subconjunto_censo
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
ID2MargaritaGarcia1974-05-07MUJER2
ID4DanielAlfara1983-09-19HOMBRE4

Por último, destacar que podemos definir condiciones lógicas para filtrar nuestros resultados. Por ejemplo, si deseamos mostrar los sujetos de censo que tienen mas de dos hijos lo haríamos del siguiente modo:

subconjunto_censo <- censo[censo$nro_hijos > 2, ]
subconjunto_censo
ABCDEFGHIJ0123456789
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>

Añadir Filas y Columnas

Del mismo modo que utilizamos los operadores [,] y $ para acceder y cambiar un valor, podemos añadir columnas en censo del siguiente modo:

# Añadimos la columna `nacionalidad` en `censo`
censo$nacionalidad <- c("ES", "FR", "RU", "IT")
# Mostramos `censo` `por pantalla
censo
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<dbl>
nacionalidad
<chr>
ID1JuanSanchez1976-06-14HOMBRE1ES
ID2MargaritaGarcia1974-05-07MUJER2FR
ID3RubenSancho1958-12-25HOMBRE3RU
ID4DanielAlfara1983-09-19HOMBRE4IT

Para añadir filas a un dataframe existente, definiremos un nuevo vector respetando las variables de las columnas que han sido definidas con anterioridad y pegando esta fila al dataframe original mediante la función rbind() (acrónimo de rowbind, pegar por filas):

# Definimos una nueva fila
fila_nueva <- c("Oscar", "Gonzalez", "1989-07-15", "HOMBRE", 0, "ES")

# Añadimos la nueva fila a `censo` con `rbind()`
censo <- rbind(censo, fila_nueva)

# Mostramos por pantalla `censo`
censo
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<chr>
nacionalidad
<chr>
ID1JuanSanchez1976-06-14HOMBRE1ES
ID2MargaritaGarcia1974-05-07MUJER2FR
ID3RubenSancho1958-12-25HOMBRE3RU
ID4DanielAlfara1983-09-19HOMBRE4IT
5OscarGonzalez1989-07-15HOMBRE0ES

Ordenación de DataFrames

Para ordenar un data.frame hemos de aplicar la función order() a la variable por la que queramos ordenar. Por ejemplo, si queremos ordenar el dataframe censo por orden alfabético de nombre, haríamos:

# Usamos `order()` para ordenar
censo[order(nombre), ]
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<chr>
nacionalidad
<chr>
ID4DanielAlfara1983-09-19HOMBRE4IT
ID1JuanSanchez1976-06-14HOMBRE1ES
ID2MargaritaGarcia1974-05-07MUJER2FR
ID3RubenSancho1958-12-25HOMBRE3RU

También podemos controlar la forma de ordenación mediante el argumento decreasing, el cual acepta los valores lógicos TRUE y FALSE. Por ejemplo, si deseamos ordenar los sujetos por el mayor número de hijos, lo haríamos:

censo[order(nro_hijos, decreasing = TRUE), ]
ABCDEFGHIJ0123456789
 
 
Nombre
<S3: AsIs>
Apellido
<S3: AsIs>
Fecha_Nacimiento
<date>
Sexo
<chr>
Numero_Hijos
<chr>
nacionalidad
<chr>
ID4DanielAlfara1983-09-19HOMBRE4IT
ID3RubenSancho1958-12-25HOMBRE3RU
ID2MargaritaGarcia1974-05-07MUJER2FR
ID1JuanSanchez1976-06-14HOMBRE1ES

Cambiar la Forma de un data.frame: formato widey long

Cuando nos encontramos con múltiples valores, dispersos en varias columnas, para la misma instancia, nuestros datos estan en forma wide (ancho).

Por otro lado, nuestros datos están en forma long (largo) si existe una observación (fila) por variable. Es decir, disponemos de múltiples filas por instancia.

Permitidme ilustrar estos conceptos mediante un ejemplo. Nuestros datos están en formato long cuando los colacamos del siguiente modo:

# Definimos las filas
alumno <- c(1, 2, 1, 2, 2, 1)
sexo <- c("M", "F", "M", "F", "F", "M")
asignatura <- c("Matematicas", "Ciencias", "Ciencias", "Literatura", "Matematicas", 
    "Literatura")
nota <- c(10, 4, 8, 6, 7, 7)

# Creamos el `data.frame`
observaciones_formato_long <- data.frame(alumno, sexo, asignatura, nota)

# Mostramos por pantalla `formato_long
observaciones_formato_long
ABCDEFGHIJ0123456789
alumno
<dbl>
sexo
<chr>
asignatura
<chr>
nota
<dbl>
1MMatematicas10
2FCiencias4
1MCiencias8
2FLiteratura6
2FMatematicas7
1MLiteratura7

Como podemos observar, existe una fila para cada variable que hemos definido. Este formato es muy útil en la gran mayoría de funciones estadísticas.

Por otro lado, nuestros datos se encuentra en formato wide cuando los organizamos del siguiente modo:

# Definimos las columnas
alumno <- c(1, 2)
sexo <- c("M", "F")
matematicas <- c(10, 7)
ciencias <- c(8, 4)
literatura <- c(7, 6)

# Creamos el `data.frame`
observaciones_formato_wide <- data.frame(alumno, sexo, matematicas, ciencias, 
    literatura)

# Mostramos por pantalla `formato_wide`
observaciones_formato_wide
ABCDEFGHIJ0123456789
alumno
<dbl>
sexo
<chr>
matematicas
<dbl>
ciencias
<dbl>
literatura
<dbl>
1M1087
2F746

Como podemos observar cada columna (variable) representa un valor para cada instancia.

Uso de stack() Para Data Frames con Estructuras Simples

La función stack() básicamente combina varios vectores en un único vector. Siguiendo con el ejemplo, si queremos nuestro data.frame en formato long combinaremos las columnas matematicas, ciencias y literatura en una sola:

# Pasamos de `wide` a `long`
formato_long <- stack(observaciones_formato_wide, select = c(matematicas, ciencias, 
    literatura))

# Mostramos `formato_long`
formato_long
ABCDEFGHIJ0123456789
values
<dbl>
ind
<fctr>
10matematicas
7matematicas
8ciencias
4ciencias
7literatura
6literatura

Para pasar de formato long a wide, necesitamos desagrupar nuestros datos con el objetivo de obtener una fila por instancia, en la que cada valor se corresponde a una variable diferente. Para ello haremos uso de la función unstack() como mostramos en el siguiente ejemplo:

# Construimos a formato `wide`
formato_wide <- unstack(observaciones_formato_long, nota ~ asignatura)

# Devuelve `formato_wide`
formato_wide
ABCDEFGHIJ0123456789
Ciencias
<dbl>
Literatura
<dbl>
Matematicas
<dbl>
4610
877

Transformar Data Frames con Estructuras Complejas

Existen otras funciones que nos permiten transformar data frames con estructuras más complejas:

  • La función reshape() que forma parte del paquete stats.

  • Las funciones spread() y gatherdel paquete tidyr.

  • La función melt() del paquete reshape2.

En este curso en un módulo posterior profundizaremos en el uso del paquete tidyr.

Resumen

  • Creamos data frames con data.frame().

  • Mediante la función str() lograremos conocer la estrucutra del data frame.

  • Para inspeccionar las primeras o últimas líneas, podemos hacer uso de las funciones head() y tail().

  • Por medio de nrow()y ncol() recuperamos el número de filas y columnas.

  • Accederemos a los elementos de una data frame del mismo modo que lo hacemos en las matrices o listas.

  • Gracias a las funciones attach y detach() podemos accecer a las variables de un data frame de forma directa.

  • Hemos aprendido a elimiar, añadir y ordenar filas y columnas. Por último, hemos discutido cuando nos encontramos en formato wide y long.

Actividad práctica 1

Ejercicio 1

En la siguiente tabla se recogen datos acerca del sexo, la edad, el peso y la altura de 10 personas:

Sexo Edad Peso Altura
Hombre 33 77 166
Mujer 31 72 153
Mujer 22 76 155
Hombre 32 67 177
Mujer 27 68 163
Mujer 29 74 172
Mujer 21 77 175
Mujer 25 65 179
Hombre 28 75 171
Mujer 37 67 165
  1. Elaborar un fichero de texto (con extensión .txt) que contenga la información de la tabla anterior. Utilizar la coma (,) para separar la información referente a cada variable. Guárdalo en la carpeta que cree el script y llámarlo mibasededatos.txt.

  2. Desde R leer el fichero que se acaba de crear y almacenar su contenido en una variable de nombre datos.

  3. Obtener los valores para las 4 variables para el tercer y el séptimo individuo

  4. Determinar la altura del noveno individuo

  5. Comprobar si la edad del primer individuo es mayor que la edad del último individuo

  6. Comprobar si el peso conjunto de los dos primeros individuos es menor que el peso conjunto de los dos últimos

  7. Calcular la raíz cuadrada de la altura del quinto individuo.

Ejercicio 2

La siguiente tabla incluye información acerca del sexo de seis estudiantes, sus puntuaciones en la asignatura de Estadística y la opinión que tienen acerca de la asignatura (Buena, Regular o Mala)

Sexo Puntuación Opinión
Hombre 3.6 Malo
Hombre 3.8 Bueno
Mujer 0.0 Malo
Hombre 3.7 Bueno
Hombre 1.0 Malo
Hombre 2.3 Bueno
Mujer 1.6 Regular
Mujer 0.5 Regular
Mujer 1.4 Bueno
Mujer 4.1 Malo

Se pide:

  1. Almacenar el contenido de la tabla en tres variables distintas (Sexo, Puntuación y Opinión), seleccionando el tipo de variable adecuado en cada caso

  2. Crear un data frame de nombre Estudiantes que contenga la información de las tres variables creadas

  3. La puntuación media de los hombres y de las mujeres

  4. Los datos de las tres variables para el quinto individuo

  5. Comprobar si la puntuación del segundo individuo o del cuarto individuo es superior a 3.5.

Ejercicio 3

En la siguiente tabla se muestra la altura, en metros, de los pinos que componen 4 parcelas de terreno.

Parcela 1 Parcela 2 Parcela 3 Parcela 4
6.9 6.6 7.4 11.5
10.4 9.6 11.3 8.1
9.3 10.9 6.3 10.4
9.8 13.9 9.0 11.5
8.7 8.0 8.3 8.7
7.3 14.3 8.7 12.0
14.1 8.3 10.3 6.7
8.0 13.5 8.3 9.7
14.7 11.5 13.3 6.3
7.3 9.9 10.2 8.5

Se pide:

  1. Elaborar un archivo de excel (con extensión .xlsx) que contenga la información de la tabla anterior. Guárdalo en la carpeta que cree el script y llámarlo mibasededatos.xlsx.

  2. Crear 4 vectores numéricos, de manera que cada uno almacene la altura de los pinos de una parcela

  3. Agrupar los 4 vectores creados en el apartado anterior en una estructura de datos adecuada, teniendo en cuenta sus características

  4. Comprobar, para cada parcela, si la altura del primer pino supera a la del último pino

  5. Calcular el logaritmo decimal de la altura del tercer pino de la parcela 3

  6. Calcular la suma de las alturas de los 3 primeros pinos de la parcela 4

  7. Obtener la altura media de los pinos de la parcela 1.