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) |
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.
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
Todos los vectores tienen dos propiedades:
typeof(letters)
## [1] "character"
typeof(1:10)
## [1] "integer"
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
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.
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")
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
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"
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.
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 |
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.
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
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
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
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:
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:
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
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:
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)]
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
frutas <- c(manzana = 1, platano = 2, kiwi = 3, pera = 4, naranja = 5)
frutas[c("platano", "naranja")]
## platano naranja
## 2 5
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.
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.
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.
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
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
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"
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=5x−y=0
que en forma matricial puede expresarse como
(321−1)(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
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:
automoviles["Blanco", "Audi"]
## [1] 2
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
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
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
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].
Para eliminar filas utilizaremos la notación [−i,].
De forma similar para eliminar columnas utilizaremos la notación [,−j].
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.
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
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.
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
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
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.
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
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:
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.
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)
breaks <dbl> | wool <fctr> | tension <fctr> | ||
---|---|---|---|---|
1 | 26 | A | L | |
2 | 30 | A | L | |
3 | 54 | A | L | |
4 | 25 | A | L | |
5 | 70 | A | L | |
6 | 52 | A | L |
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.
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
nombre <chr> | apellido <chr> | fecha_nacimiento <chr> | sexo <chr> | nro_hijos <dbl> |
---|---|---|---|---|
Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
Margarita | Garcia | 1974-05-07 | MUJER | 2 |
Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
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)
nombre <chr> | apellido <chr> | fecha_nacimiento <chr> | sexo <chr> | nro_hijos <dbl> | |
---|---|---|---|---|---|
1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
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
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().
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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
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().
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, ]
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
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"
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().
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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
# Asignamos el valor `NULL` al valor en [1, 3]
copia_censo[1, 3] <- NULL
copia_censo
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
# Asignamos `NULL`a la variable `nro_hijos`
copia_censo$nro_hijos <- NULL
copia_censo
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
# 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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 |
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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | |
---|---|---|---|---|---|
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 |
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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> |
---|
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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <dbl> | nacionalidad <chr> | |
---|---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 | ES |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 | FR |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 | RU |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 | IT |
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
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <chr> | nacionalidad <chr> | |
---|---|---|---|---|---|---|
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 | ES |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 | FR |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 | RU |
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 | IT |
5 | Oscar | Gonzalez | 1989-07-15 | HOMBRE | 0 | ES |
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), ]
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <chr> | nacionalidad <chr> | |
---|---|---|---|---|---|---|
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 | IT |
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 | ES |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 | FR |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 | RU |
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), ]
Nombre <S3: AsIs> | Apellido <S3: AsIs> | Fecha_Nacimiento <date> | Sexo <chr> | Numero_Hijos <chr> | nacionalidad <chr> | |
---|---|---|---|---|---|---|
ID4 | Daniel | Alfara | 1983-09-19 | HOMBRE | 4 | IT |
ID3 | Ruben | Sancho | 1958-12-25 | HOMBRE | 3 | RU |
ID2 | Margarita | Garcia | 1974-05-07 | MUJER | 2 | FR |
ID1 | Juan | Sanchez | 1976-06-14 | HOMBRE | 1 | ES |
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
alumno <dbl> | sexo <chr> | asignatura <chr> | nota <dbl> | |
---|---|---|---|---|
1 | M | Matematicas | 10 | |
2 | F | Ciencias | 4 | |
1 | M | Ciencias | 8 | |
2 | F | Literatura | 6 | |
2 | F | Matematicas | 7 | |
1 | M | Literatura | 7 |
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
alumno <dbl> | sexo <chr> | matematicas <dbl> | ciencias <dbl> | literatura <dbl> |
---|---|---|---|---|
1 | M | 10 | 8 | 7 |
2 | F | 7 | 4 | 6 |
Como podemos observar cada columna (variable) representa un valor para cada instancia.
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
values <dbl> | ind <fctr> | |||
---|---|---|---|---|
10 | matematicas | |||
7 | matematicas | |||
8 | ciencias | |||
4 | ciencias | |||
7 | literatura | |||
6 | literatura |
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
Ciencias <dbl> | Literatura <dbl> | Matematicas <dbl> | ||
---|---|---|---|---|
4 | 6 | 10 | ||
8 | 7 | 7 |
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.
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.
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 |
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.
Desde R leer el fichero que se acaba de crear y almacenar su contenido en una variable de nombre datos.
Obtener los valores para las 4 variables para el tercer y el séptimo individuo
Determinar la altura del noveno individuo
Comprobar si la edad del primer individuo es mayor que la edad del último individuo
Comprobar si el peso conjunto de los dos primeros individuos es menor que el peso conjunto de los dos últimos
Calcular la raíz cuadrada de la altura del quinto individuo.
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:
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
Crear un data frame de nombre Estudiantes que contenga la información de las tres variables creadas
La puntuación media de los hombres y de las mujeres
Los datos de las tres variables para el quinto individuo
Comprobar si la puntuación del segundo individuo o del cuarto individuo es superior a 3.5.
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:
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.
Crear 4 vectores numéricos, de manera que cada uno almacene la altura de los pinos de una parcela
Agrupar los 4 vectores creados en el apartado anterior en una estructura de datos adecuada, teniendo en cuenta sus características
Comprobar, para cada parcela, si la altura del primer pino supera a la del último pino
Calcular el logaritmo decimal de la altura del tercer pino de la parcela 3
Calcular la suma de las alturas de los 3 primeros pinos de la parcela 4
Obtener la altura media de los pinos de la parcela 1.