5 Propiedades de los objetos

Las estructuras de datos son objetos que contienen datos. Cuando trabajamos con R, lo que estamos haciendo es manipular estas estructuras.

Las estructuras tienen diferentes características. Entre ellas, las que distinguen a una estructura de otra son su número de dimensiones y si son homogeneas o hereterogeneas.

La siguiente tabla muestra las principales estructuras de control que te encontrarás en R.

Dimensiones Homogéneas Heterogéneas
1 Vector Lista
2 Matriz Data frame
n Array

Veamos las características de cada una de ellas.

5.1 Vectores

Un vector es la estructura de datos más sencilla en R. Un vector es una colección de uno o más datos del mismo tipo.

Todos los vectores tienen tres propiedades:

  • Tipo. Un vector tiene el mismo tipo que los datos que contiene. Si tenemos un vector que contiene datos de tipo numérico, el vector será también de tipo numérico. Los vectores son atómicos, pues sólo pueden contener datos de un sólo tipo, no es posible mezclar datos de tipos diferentes dentro de ellos.

  • Largo. Es el número de elementos que contiene un vector. El largo es la única dimensión que tiene esta estructura de datos.

  • Atributos. Los vectores pueden tener metadatos de muchos tipos, los cuales describen características de los datos que contienen. Todos ellos son incluidos en esta propiedad. En este libro no se usarán vectores con metadatos, por ser una propiedad con usos van más allá del alcance de este libro.

Cuando una estructura únicamente puede contener datos de un sólo tipo, como es el caso de los vectores, decimos que es homogénea, pero no implica que necesariamente sea atómica. Regresaremos sobre esto al hablar de matrices y arrays.

Como los vectores son la estructura de datos más sencilla de R, datos simples como el número 3, son en realidad vectores. En este caso, un vector de tipo numérico y largo igual a 1.

3
## [1] 3

se verifica que el 3 es un vector con la función is.vector().

is.vector(3)
## [1] TRUE

Y se usa la función length() para conocer su largo.

length(3)
## [1] 1

Lo mismo ocurre con los demás tipos de datos, por ejemplo, con cadenas de texto y datos lógicos.

is.vector("tres")
## [1] TRUE

is.vector(TRUE)
## [1] TRUE

5.1.1 Creación de vectores

se crean vectores usando la función c() (concatenar).

Llamamos esta función y le damos como argumento los elementos que deseamos combinar en un vector, separados por comas.

# Vector numérico
c(1, 2, 3, 5, 8, 13)
## [1]  1  2  3  5  8 13

# Vector de cadena de texto
c("arbol", "casa", "persona")
## [1] "arbol"   "casa"    "persona"

# Vector lógico
c(TRUE, TRUE, FALSE, FALSE, TRUE)
## [1]  TRUE  TRUE FALSE FALSE  TRUE

Si se desea agregar un elemento a un vector ya existente, se puede hacerlo combinando nuestro vector original con los elementos nuevos y asignando el resultado a nuestro vector original.

mi_vector <- c(TRUE, FALSE, TRUE)

mi_vector <- c(mi_vector, FALSE)

mi_vector
## [1]  TRUE FALSE  TRUE FALSE

O bien si se desea cambiar un elemento dentro de un vector ya existente.

mi_vector <- c(TRUE, FALSE, TRUE)
mi_vector[2] <- "2"
mi_vector
## [1] "TRUE" "2"    "TRUE"

Naturalmente, se pueden crear vectores que son combinación de vectores.

mi_vector_1 <- c(1, 3, 5)
mi_vector_2 <- c(2, 4, 6)
mi_vector_3 <- c(mi_vector_1, mi_vector_2)
mi_vector_3
## [1] 1 3 5 2 4 6

Si se intentar combinar datos de diferentes tipos en un mismo vector, R realizará coerción automáticamente. El vector resultante será del tipo más flexible entre los datos que contenga, siguiendo las reglas de coerción.

Se crea un vector numérico.

mi_vector <- c(1, 2, 3)
class(mi_vector)
## [1] "numeric"

Pero si se intenta agregar un dato de tipo cadena de texto, el vector resultante ahora será de tipo cadena de texto.

mi_vector_nuevo <- c(mi_vector, "a")
class(mi_vector_nuevo)
## [1] "character"

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

mi_vector_mezcla <- c(FALSE, 2, "tercero", 4.00)
class(mi_vector_mezcla)
## [1] "character"

Tambien se pueden crear vectores de secuencias numéricas usando :. De un lado de los dos puntos escribimos el número de inicio de la secuencia y del otro el final.

Por ejemplo, se crea una secuencia del 1 al 10.

1:10
##  [1]  1  2  3  4  5  6  7  8  9 10

También se puede crear una secuencia del 10 al 1.

10:1
##  [1] 10  9  8  7  6  5  4  3  2  1

Las secuencias creadas con : son consecutivas con incrementos o decrementos de 1. Estas secuencias pueden empezar con cualquier número, incluso si este es negativo o tiene cifras decimales.

# Número negativo
-43:-30
##  [1] -43 -42 -41 -40 -39 -38 -37 -36 -35 -34 -33 -32 -31 -30

# Número con cifras decimales
67.23:75
## [1] 67.23 68.23 69.23 70.23 71.23 72.23 73.23 74.23

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

# Se conservan los decimales del inicio
-2.48:2
## [1] -2.48 -1.48 -0.48  0.52  1.52

56.007:50
## [1] 56.007 55.007 54.007 53.007 52.007 51.007 50.007

# Se redondean los decimales del final
166:170.05
## [1] 166 167 168 169 170

968:960.928
## [1] 968 967 966 965 964 963 962 961

5.1.2 Vectorización de operaciones

Existen algunas operaciones al aplicarlas a un vector, se aplican a cada uno de sus elementos. A este proceso le llamamos vectorización.

Las operaciones aritméticas y relacionales pueden vectorizarse. Si las aplicamos a un vector, la operación se realizará para cada uno de los elementos que contiene.

Por ejemplo, se crea un vector numérico.

mi_vector <- c(2, 3, 6, 7, 8, 10, 11)

Si se aplican operaciones aritméticas, se obtendrá un vector con un resultado por cada elemento.

# Operaciones aritméticas
mi_vector + 2
## [1]  4  5  8  9 10 12 13

mi_vector * 2
## [1]  4  6 12 14 16 20 22

mi_vector %% 2
## [1] 0 1 0 1 0 0 1

Al aplicar operaciones relacionales, se obtendrás un vector de TRUEy FALSE, uno para cada elemento comparado.

mi_vector > 7
## [1] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE

mi_vector < 7
## [1]  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE

mi_vector == 7
## [1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE

De esta manera aplicar una operación es muy eficiente. Comparada con otros procedimientos, requiere de menos tiempo de cómputo, lo cual a veces es considerable, en particular cuando se trabaja con un número grande de datos.

Aunque el nombre de este proceso es vectorización, también funciona, en ciertas circunstancias, para otras estructuras de datos, principalmente para calculos matriciales.

5.2 Matrices y arrays

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

En un sentido estricto, las matrices son una caso especial de un array, que se distingue por tener específicamente dos dimensiones, un “largo”” y un “alto”. Las matrices son, por lo tanto, una estructura con forma rectangular, con renglones y columnas.

Como las matrices son usadas de manera regular en matemáticas y estadística, es una estructura de datos de uso común en R.

Los arrays, por su parte, pueden tener un número arbitrario de dimensiones. Pueden ser cubos, hipercubos y otras formas. Su uso no es muy común en R, aunque a veces es deseable contar con objetos n-dimensionales para manipular datos. Como los arrays tienen la restricción de que todos sus datos deben ser del mismo tipo, no importando en cuántas dimensiones se encuentren, esto limita sus usos prácticos.

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

5.2.1 Creación de matrices

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

# Un vector numérico del uno al doce
1:12
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12

# matrix() sin especificar renglones ni columnas
matrix(1:12)
##       [,1]
##  [1,]    1
##  [2,]    2
##  [3,]    3
##  [4,]    4
##  [5,]    5
##  [6,]    6
##  [7,]    7
##  [8,]    8
##  [9,]    9
## [10,]   10
## [11,]   11
## [12,]   12

# Tres renglones y cuatro columnas
matrix(1:12, nrow = 3, ncol = 4)
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12

# Cuatro columnas y tres columnas
matrix(1:12, nrow = 4, ncol = 3)
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

# Dos renglones y seis columnas
matrix(1:12, nrow = 4, ncol = 3)
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

Los datos que se intentan agrupar en una matriz serán acomodados en orden, de arriba a abajo, y de izquierda a derecha, hasta formar un rectángulo.

Si se multiplica el número de renglones por el número de columnas, se obtandrá el número de celdas de la matriz. Donde el número de celdas es igual al número de elementos que se desea acomodar, así que la operación ocurre sin problemas.

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

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

matrix(1:12, nrow = 3, ncol = 3)
## Warning in matrix(1:12, nrow = 3, ncol = 3): data length differs from size of matrix: [12 != 3 x 3]
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9

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

matrix(1:12, nrow = 5, ncol = 4)
## Warning in matrix(1:12, nrow = 5, ncol = 4): data length [12] is not a sub-multiple or multiple of the number of rows
## [5]
##      [,1] [,2] [,3] [,4]
## [1,]    1    6   11    4
## [2,]    2    7   12    5
## [3,]    3    8    1    6
## [4,]    4    9    2    7
## [5,]    5   10    3    8

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

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

De este modo se pueden crear cuatro vectores y unirlos para formar una matriz. Cada vector será un renglón en esta matriz.

se crean cuatro vectores, cada uno de largo igual a cuatro.

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

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

matriz <- rbind(vector_1, vector_2, vector_3, vector_4)
# Resultado
matriz
##          [,1] [,2] [,3] [,4]
## vector_1    1    2    3    4
## vector_2    5    6    7    8
## vector_3    9   10   11   12
## vector_4   13   14   15   16

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

matriz <- cbind(vector_1, vector_2, vector_3, vector_4)
# Resultado
matriz
##      vector_1 vector_2 vector_3 vector_4
## [1,]        1        5        9       13
## [2,]        2        6       10       14
## [3,]        3        7       11       15
## [4,]        4        8       12       16

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

# Elementos de largo diferente
vector_1 <- 1:2
vector_2 <- 1:3
vector_3 <- 1:5
matriz <- cbind(vector_1, vector_2, vector_3)
## Warning in cbind(vector_1, vector_2, vector_3): number of rows of result is not a multiple of vector length (arg 1)
# Resultado
matriz
##      vector_1 vector_2 vector_3
## [1,]        1        1        1
## [2,]        2        2        2
## [3,]        1        3        3
## [4,]        2        1        4
## [5,]        1        2        5

Finalmente, las matrices pueden contener NAs.

Se crean dos vectores con un NA en ellos.

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

Se crea una matriz con rbind().

matriz <- rbind(vector_1, vector_2)
# Resultados
matriz
##          [,1] [,2] [,3]
## vector_1   NA    1    2
## vector_2    3    4   NA

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

5.2.2 Propiedades de las matrices

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

se verifica esto usando la función class().

mi_matriz <- matrix(1:10)
class(mi_matriz)
## [1] "matrix" "array"

Las matrices y arrays pueden tener más de una dimensión.

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

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

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

mi_vector <- 1:12
dim(mi_vector)
## NULL

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

Se crea una matriz de demensión \(3\times3\).

mi_matriz <- matrix(1:9, nrow = 3, ncol = 3)
# Resultado
mi_matriz
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9

Se intenta sumar, multiplicar y elevar a la tercera potencia.

# Suma
mi_matriz + 1
##      [,1] [,2] [,3]
## [1,]    2    5    8
## [2,]    3    6    9
## [3,]    4    7   10

# Multiplicación
mi_matriz * 2
##      [,1] [,2] [,3]
## [1,]    2    8   14
## [2,]    4   10   16
## [3,]    6   12   18

# Potenciación
mi_matriz ^ 3
##      [,1] [,2] [,3]
## [1,]    1   64  343
## [2,]    8  125  512
## [3,]   27  216  729

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

Se crean una matriz con NAs.

vector_1 <- c(NA, 2, 3)
vector_2 <- c(4, 5, NA)
matriz <- rbind(vector_1, vector_2)
# Resultado
matriz
##          [,1] [,2] [,3]
## vector_1   NA    2    3
## vector_2    4    5   NA

Se intenta dividir sus elementos entre dos.

matriz / 2
##          [,1] [,2] [,3]
## vector_1   NA  1.0  1.5
## vector_2    2  2.5   NA

Finalmente, se puede usar la función t() para transponer una matriz, es decir, rotarla.

Se crea una matriz con tres renglones y dos columnas.

matriz <- matrix(1:6, nrow = 3)
# Resultado
matriz
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    5
## [3,]    3    6

Se usa t() para transponer.

matriz_t <- t(matriz)
# Resultado
matriz_t
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6

Obtenemos una matriz con dos renglones y dos columnas.

5.3 Data frames

Los data frames son estructuras de datos de dos dimensiones (rectangulares) que pueden contener datos de diferentes tipos, por lo tanto, son heterogéneas. Esta estructura de datos es la más usada para realizar análisis de datos y el muy común trabajarlos con otros paquetes estadísticos.

Se puede entender a los data frames como una versión más flexible de una matriz. Mientras que en una matriz todas las celdas deben contener datos del mismo tipo, los renglones de un data frame admiten datos de distintos tipos, pero sus columnas conservan la restricción de contener datos de un sólo tipo.

En términos generales, los renglones en un data frame representan casos, individuos u observaciones, mientras que las columnas representan atributos, rasgos o variables.

Por ejemplo, así lucen los primeros cinco renglones del objeto iris, el famoso conjunto de datos Iris de Ronald Fisher, que está incluido en todas las instalaciones de R.

head(iris, 5)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
tail(iris, 5)
##     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
## 146          6.7         3.0          5.2         2.3 virginica
## 147          6.3         2.5          5.0         1.9 virginica
## 148          6.5         3.0          5.2         2.0 virginica
## 149          6.2         3.4          5.4         2.3 virginica
## 150          5.9         3.0          5.1         1.8 virginica

Los primeros cinco renglones corresponden a cinco casos, en este caso flores. Las columnas son variables con los rasgos de cada flor: largo y ancho de sépalo, largo y ancho de pétalo, y especie.

Para crear un data frame se usa la función data.frame(). Esta función nos pedirá un número de vectores igual al número de columnas que deseemos. Todos los vectores que proporcionemos deben tener el mismo largo.

Esto es muy importante: Un data frame está compuesto por vectores.

Más adelante se hará evidente porque esta característica de un data frame es sumamente importante y también, cómo se puede sacarle provecho.

Además, se puede asignar un nombre a cada vector, que se convertirá en el nombre de la columna. Como todos los nombres, es recomendable que este sea claro, no ambiguo y descriptivo.

mi_df <- data.frame(
                    "entero" = 1:4, 
                    "factor" = c("a", "b", "c", "d"), 
                    "numero" = c(1.2, 3.4, 4.5, 5.6),
                    "cadena" = as.character(c("a", "b", "c", "d"))
                    )
mi_df
##   entero factor numero cadena
## 1      1      a    1.2      a
## 2      2      b    3.4      b
## 3      3      c    4.5      c
## 4      4      d    5.6      d

# se puede usar dim() en un data frame
dim(mi_df)
## [1] 4 4

# El largo de un data frame es igual a su número de columnas
length(mi_df)
## [1] 4

# names() nos permite ver los nombres de las columnas
names(mi_df)
## [1] "entero" "factor" "numero" "cadena"

# La clase de un data frame es data.frame
class(mi_df) 
## [1] "data.frame"

Si los vectores que se usa para construir el data frame no son del mismo largo, los datos no se reciclaran. Se nos devolverá un error.

data.frame(
  "entero" = 1:3, 
  "factor" = c("a", "b", "c", "d"), 
  "numero" = c(1.2, 3.4, 4.5, 5.6),
  "cadena" = as.character(c("a", "b", "c", "d"))
)

También se puede coercionar esta matriz a un data frame.

Se crea una matriz.

matriz <- matrix(1:12, ncol = 4)

Se usa as.data.frame() para coercionar una matriz a un data frame.

df <- as.data.frame(matriz)

Se verifica la clase de objeto es df.

class(df)
## [1] "data.frame"

# Resultado
df
##   V1 V2 V3 V4
## 1  1  4  7 10
## 2  2  5  8 11
## 3  3  6  9 12

5.3.1 Propiedades de un data frame

Al igual que con una matriz, si aplicamos una operación aritmética a un data frame, esta se vectorizará.

Los resultados que obtendremos dependerán del tipo de datos de cada columna. R nos devolverá todas las advertencias que ocurran como resultado de las operaciones realizadas, por ejemplo, aquellas que hayan requerido una coerción.

mi_df <- data.frame(
  "entero" = 1:4, 
  "factor" = c("a", "b", "c", "d"), 
  "numero" = c(1.2, 3.4, 4.5, 5.6),
  "cadena" = as.character(c("a", "b", "c", "d"))
)
mi_df * 2
mi_df[,1] * 2
df$V1 *2

5.4 Listas

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

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

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

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

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

mi_vector <- 1:10
mi_matriz <- matrix(1:4, nrow = 2)
mi_df     <- data.frame("num" = 1:3, "let" = c("a", "b", "c"))
mi_lista <- list("un_vector" = mi_vector, "una_matriz" = mi_matriz, "un_df" = mi_df)
mi_lista
## $un_vector
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## $una_matriz
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
## 
## $un_df
##   num let
## 1   1   a
## 2   2   b
## 3   3   c

Se crean una lista que contiene otras listas.

lista_recursiva <- list("lista1" = mi_lista, "lista2" = mi_lista)
# Resultado
lista_recursiva
## $lista1
## $lista1$un_vector
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## $lista1$una_matriz
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
## 
## $lista1$un_df
##   num let
## 1   1   a
## 2   2   b
## 3   3   c
## 
## 
## $lista2
## $lista2$un_vector
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## $lista2$una_matriz
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
## 
## $lista2$un_df
##   num let
## 1   1   a
## 2   2   b
## 3   3   c

5.4.1 Propiedades de una lista

Una lista es unidimensional, sólo tiene largo.

El largo de una lista es igual al número de elementos que contiene, sin importar de qué tipo o clase sean. se usa la lista recursiva que se crean en la sección anterior para ilustrar esto.

length(lista_recursiva)
## [1] 2

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

dim(lista_recursiva)
## NULL

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

class(lista_recursiva)
## [1] "list"

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

mi_lista / 2
## Error in mi_lista/2: non-numeric argument to binary operator

Si se aplicar una función a cada elemento de una lista, se usa lapply(). Para volver a generar un lista como resultado.